Files
python-bpf/docs/getting-started/quickstart.md
2026-01-25 13:31:21 +05:30

6.2 KiB

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 compilation
  • section - Decorator to specify which kernel event to attach to
  • bpfglobal - Decorator for BPF global variables
  • BPF - Class to compile, load, and attach BPF programs
  • trace_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 for bpf_printk helper function
  • return 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 file
  • b.load() - Loads the compiled BPF program into the kernel
  • b.attach_all() - Attaches all BPF programs to their specified hooks
  • trace_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

  1. Always include a LICENSE - Required by the kernel
  2. Use type hints - Required by PythonBPF to generate correct code
  3. Return the correct type - Match the expected return type for your program type
  4. Test incrementally - Start simple and add complexity gradually
  5. Check kernel logs - Use dmesg to see BPF verifier messages if loading fails

Common Issues

Program Won't Load

If your BPF program fails to load:

  • Check dmesg for verifier error messages
  • Ensure 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_pipe is accessible

Compilation Errors

If compilation fails:

  • Check that llc is installed and in your PATH
  • Verify your Python syntax is correct
  • Ensure all imported types are from ctypes
  • In the worst case, compile object files manually using compile_to_ir() and llc to get detailed errors

Verification Failure

If verification fails:

  • Compile the object files using compile() function instead of loading directly
  • Run sudo check.sh check <bpf>.o to get detailed verification output

Next Steps

Now that you understand the basics, explore:

  • {doc}../user-guide/decorators - Learn about all available decorators
  • {doc}../user-guide/maps - Use BPF maps for data storage and communication
  • {doc}../user-guide/structs - Define custom data structures
  • {doc}../user-guide/helpers - Discover all available BPF helper functions
  • Examples directory - See more complex examples