Contributing¶
hello-ebpf has a split test environment: the compiler plugin and annotation processor are tested with pure-Java tools (no Linux kernel required); BPF integration tests require a real kernel and run in vng (virtme-ng) on a Linux machine (the project uses a dedicated thinkstation). CI runs the full suite on ubuntu-26.04 hosted runners that provide a 6.x kernel with BTF enabled.
Development environment¶
Mac (plugin and annotation processor work)¶
Standard Maven test on the pure-Java modules:
No kernel, no clang, and no BPF headers are needed for these tests. CompilerPluginTest compiles @BPF classes in-process via InMemoryJavaCompiler and asserts on the generated C string.
Linux / thinkstation (BPF integration tests)¶
Prerequisites (from .github/workflows/ci.yml):
- clang 19+ (
apt install clang-19) libbpf-dev,linux-tools-$(uname -r),bpftool- Linux kernel 6.17 or later with BTF (
/sys/kernel/btf/vmlinuxpresent) CONFIG_SCHED_CLASS_EXT=yandCONFIG_BPF_LSM=yfor scheduler and LSM tests
Run BPF integration tests as root (sudo strips PATH; supply HOME and JAVA_HOME explicitly):
sudo -E env HOME=$HOME JAVA_HOME=$JAVA_HOME PATH=$JAVA_HOME/bin:$PATH \
./mvnw -ntp -B test -pl bpf,bpf-samples \
-Dmaven.test.skip=false -DskipTests=false
For tests that need vng, write logs to /tmp rather than the repo directory: virtme-ng's copy-on-write overlay silently discards writes to the host filesystem on exit.
Module structure¶
| Module | What it contains |
|---|---|
annotations |
All @BPF*, @Type, @Size, etc. annotation definitions; no runtime code |
bpf-processor |
Annotation processor (Processor, TypeProcessor); generates $Impl class skeletons |
bpf-compiler-plugin |
javac compiler plugin; translates @BPFFunction bodies to C and embeds the .o |
bpf-compiler-plugin-test |
Pure-Java unit tests for the compiler plugin using InMemoryJavaCompiler |
bpf |
Runtime: BPFProgram, map types, BPFJ helpers, BPF integration tests |
bpf-runtime |
Auto-generated Panama bindings for the kernel BPF uAPI (~1000 files) |
bpf-samples |
End-to-end sample programs; doubles as integration-test source |
bpf-archetype |
Maven archetype for bootstrapping new hello-ebpf projects |
Adding a new hook type end-to-end¶
- Add a
@MyHookannotation inannotations/src/main/java/me/bechberger/ebpf/annotations/. Follow the pattern of existing hook annotations in that package. - Register the SEC pattern. If the hook maps to a known
SEC("mytype/...")string, add a case toProgramType(inbpf/) soautoAttachProgramscan discover it. If the SEC string requires codegen (e.g. a per-field suffix), register the pattern in the compiler plugin'sprocessBPFProgramImpldispatch. - Add a Java interface (e.g.
MyHookInterface) with a default-body method for the callback. The Java body must throwMethodIsBPFRelatedFunction()so the compiler plugin recognises it as a BPF-only method. - Add attach logic in
BPFProgram(or as a new@StructOps-style handler) using the existingautoAttachProgramsmechanism or a dedicatedattach*method. - Write a compiler plugin test in
bpf-compiler-plugin-test/that compiles a minimal@BPFclass implementing the interface and asserts the generated C contains the expectedSEC("mytype/...")declaration. - Write a BPF integration test in
bpf/src/test/java/that loads the program on a real kernel, attaches it, and verifies it fires.
Coding conventions¶
- Annotations belong in
annotations/, not inbpf/orbpf-processor/. - Methods that exist only on the BPF side must have
throw new MethodIsBPFRelatedFunction()as their Java body. This is the signal the compiler plugin uses to recognise them as translation targets. - Do not write inline C strings inside
@BPFclasses. Use@BuiltinBPFFunctiontemplates with$arg1,$typeof,$lambda, etc. placeholders instead. The template language is documented inmemory/reference_method_template_language.md. - BPF integration tests live in
bpf/src/test/java/and are tagged so they are skipped when running without root or a kernel. Check existing tests (e.g.XDPTest.java) for the@Tagconvention in use. - Do not name
@StructOpsinterface method argumentsctx— theBPF_PROGmacro reserves that identifier and clang will reject the generated C (feedback_bpf_prog_macro_ctx_collision.md).
Next: Changelog