Quick Start
This guide will walk you through creating your first BPF program with PythonBPF.
Your First BPF Program
Let’s create a simple “Hello World” program that prints a message every time a process is executed on your system.
Step 1: Create the Program
Create a new file called hello_world.py:
from pythonbpf import bpf, section, bpfglobal, BPF, trace_pipe
from ctypes import c_void_p, c_int64
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def hello_world(ctx: c_void_p) -> c_int64:
print("Hello, World!")
return 0
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
b = BPF()
b.load()
b.attach_all()
trace_pipe()
Step 2: Run the Program
Run the program with sudo (required for BPF operations):
sudo python3 hello_world.py
Step 3: See it in Action
Open another terminal and run any command:
ls
echo "test"
date
You should see “Hello, World!” printed in the first terminal for each command executed!
Press Ctrl+C to stop the program.
Understanding the Code
Let’s break down what each part does:
Imports
from pythonbpf import bpf, section, bpfglobal, BPF, trace_pipe
from ctypes import c_void_p, c_int64
bpf- Decorator to mark functions for BPF compilationsection- Decorator to specify which kernel event to attach tobpfglobal- Decorator for BPF global variablesBPF- Class to compile, load, and attach BPF programstrace_pipe- Utility to read kernel trace output (similar to BCC)c_void_p,c_int64- C types for function signatures
The BPF Function
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def hello_world(ctx: c_void_p) -> c_int64:
print("Hello, World!")
return 0
@bpf- Marks this function to be compiled to BPF bytecode@section("tracepoint/syscalls/sys_enter_execve")- Attaches to the execve syscall tracepoint (called when processes start)ctx: c_void_p- Context parameter (required for all BPF functions)print()- the PythonBPF API forbpf_printkhelper functionreturn 0- BPF functions must return an integer
License Declaration
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
The Linux kernel requires BPF programs to declare a license
Most kernel features require GPL-compatible licenses
This is defined as a BPF global variable
Compilation and Execution
b = BPF()
b.load()
b.attach_all()
trace_pipe()
BPF()- Creates a BPF object and compiles the current fileb.load()- Loads the compiled BPF program into the kernelb.attach_all()- Attaches all BPF programs to their specified hookstrace_pipe()- Reads and displays output from the kernel trace buffer
Alternatively, you can also use the compile() function to compile the BPF code to an object file:
from pythonbpf import compile
This object file can then be loaded using any other userspace library in any language.
Next Example: Tracking Process IDs
Let’s make a more interesting program that tracks which processes are being created:
from pythonbpf import bpf, section, bpfglobal, BPF, trace_pipe
from pythonbpf.helper import pid
from ctypes import c_void_p, c_int64
@bpf
@section("tracepoint/syscalls/sys_enter_execve")
def track_exec(ctx: c_void_p) -> c_int64:
process_id = pid()
print(f"Process with PID: {process_id} is starting")
return 0
@bpf
@bpfglobal
def LICENSE() -> str:
return "GPL"
b = BPF()
b.load()
b.attach_all()
trace_pipe()
This program uses BPF helper functions:
pid()- Gets the current process ID
Run it with sudo python3 track_exec.py and watch processes being created!
Common Patterns
Tracepoints
Tracepoints are predefined hooks in the kernel. Common ones include:
# System calls
@section("tracepoint/syscalls/sys_enter_execve")
@section("tracepoint/syscalls/sys_enter_clone")
@section("tracepoint/syscalls/sys_enter_open")
# Scheduler events
@section("tracepoint/sched/sched_process_fork")
@section("tracepoint/sched/sched_switch")
Kprobes
Kprobes allow you to attach to any kernel function:
@section("kprobe/do_sys_open")
def trace_open(ctx: c_void_p) -> c_int64:
print("File is being opened")
return 0
XDP (eXpress Data Path)
For network packet processing:
from pythonbpf.helper import XDP_PASS
@section("xdp")
def xdp_pass(ctx: c_void_p) -> c_int64:
return XDP_PASS
Best Practices
Always include a LICENSE - Required by the kernel
Use type hints - Required by PythonBPF to generate correct code
Return the correct type - Match the expected return type for your program type
Test incrementally - Start simple and add complexity gradually
Check kernel logs - Use
dmesgto see BPF verifier messages if loading fails
Common Issues
Program Won’t Load
If your BPF program fails to load:
Check
dmesgfor verifier error messagesEnsure your LICENSE is GPL-compatible
Verify you’re using supported BPF features
Make sure return types match function signatures
No Output
If you don’t see output:
Verify the tracepoint/kprobe is being triggered
Check that you’re running with sudo
Ensure
/sys/kernel/tracing/trace_pipeis accessible
Compilation Errors
If compilation fails:
Check that
llcis installed and in your PATHVerify your Python syntax is correct
Ensure all imported types are from
ctypesIn the worst case, compile object files manually using
compile_to_ir()andllcto get detailed errors
Verification Failure
If verification fails:
Compile the object files using
compile()function instead of loading directlyRun
sudo check.sh check <bpf>.oto get detailed verification output
Next Steps
Now that you understand the basics, explore:
Decorators - Learn about all available decorators
BPF Maps - Use BPF maps for data storage and communication
BPF Structs - Define custom data structures
Helper Functions and Utilities - Discover all available BPF helper functions
Examples directory - See more complex examples