Complete Guide to eBPF Tracepoint Programming
📚 Table of Contents
- Introduction to Tracepoints
- Finding Tracepoint Events
- Determining Function Parameter Types
- Writing eBPF Programs
- Complete Example
Introduction to Tracepoints
Tracepoints are static tracing points provided by the Linux kernel that allow us to insert probes at specific locations in the kernel to monitor system events.
Tracepoint vs Kprobe
| Feature | Tracepoint | Kprobe |
|---|---|---|
| Stability | ✅ Stable kernel API | ⚠️ Depends on kernel implementation details |
| Performance | ✅ Lower overhead | ⚠️ Higher overhead |
| Portability | ✅ Stable across kernel versions | ⚠️ May change with kernel versions |
| Flexibility | ⚠️ Only at predefined locations | ✅ Can attach to any kernel function |
Finding Tracepoint Events
Method 1: Check System Files
View the /sys/kernel/debug/tracing/available_events file to list all available tracepoint events.
Format:
<category>:<name>Example: Finding unlinkat-related events
cat /sys/kernel/debug/tracing/available_events | grep unlinkatOutput:
syscalls:sys_exit_unlinkat
syscalls:sys_enter_unlinkatMethod 2: Using bpftrace Tool
bpftrace -l 'tracepoint:*' | grep unlinkatOutput:
tracepoint:syscalls:sys_enter_unlinkat
tracepoint:syscalls:sys_exit_unlinkatSEC Macro Format Definition
Full Format
SEC("tracepoint/<category>/<name>")Example:
SEC("tracepoint/syscalls/sys_enter_openat")Abbreviated Format
SEC("tp/<category>/<name>")Example:
SEC("tp/syscalls/sys_enter_openat")Note:
SEC("tp/xx/yy")andSEC("tracepoint/xx/yy")are equivalent; choose based on personal preference.
Determining Function Prototype - Parameter Types and Return Values
Step 1: View Event Format Information
Check the /sys/kernel/debug/tracing/events/<category>/<name>/format file to understand the fields contained in the event.
Example: sys_enter_unlinkat event
cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_unlinkat/formatOutput:
name: sys_enter_unlinkat
ID: 784
format:
field:unsigned short common_type; offset:0; size:2; signed:0;
field:unsigned char common_flags; offset:2; size:1; signed:0;
field:unsigned char common_preempt_count; offset:3; size:1; signed:0;
field:int common_pid; offset:4; size:4; signed:1;
field:int __syscall_nr; offset:8; size:4; signed:1;
field:int dfd; offset:16; size:8; signed:0;
field:const char * pathname; offset:24; size:8; signed:0;
field:int flag; offset:32; size:8; signed:0;Important: The first 8 bytes of fields (common_type, common_flags, etc.) cannot be directly accessed by regular eBPF programs; they can only be accessed through specific BPF helper functions.
Step 2: Quick View Using bpftrace
bpftrace -l tracepoint:syscalls:sys_enter_unlinkat -vOutput:
tracepoint:syscalls:sys_enter_unlinkat
int __syscall_nr
int dfd
const char * pathname
int flagFrom the output above, we can obtain the following parameters from the sys_enter_unlinkat event:
dfd- Directory file descriptorpathname- File pathflag- Flags
Step 3: Find Corresponding Kernel Structure
Look up the corresponding structure definition in the vmlinux.h file.
Naming Convention
| Event Type | Corresponding Structure |
|---|---|
sys_enter_* | struct trace_event_raw_sys_enter |
sys_exit_* | struct trace_event_raw_sys_exit |
sys_enter Event Structure Definition
struct trace_event_raw_sys_enter {
struct trace_entry ent;
long int id;
long unsigned int args[6]; // System call parameter array
char __data[0];
};Parameter Access Method:
args[0]- First parameter (dfd)args[1]- Second parameter (pathname)args[2]- Third parameter (flag)- And so on...
Writing eBPF Programs
Complete Example: Monitoring unlinkat System Call
#include "vmlinux.h"
#include <bpf/bpf_helpers.h>
#include <bpf/bpf_tracing.h>
// Define tracepoint, triggered when a process executes the unlinkat system call (deletes a file)
SEC("tracepoint/syscalls/sys_enter_unlinkat")
int trace_enter_unlinkat(struct trace_event_raw_sys_enter *ctx)
{
// 1. Get process PID
__u32 pid = bpf_get_current_pid_tgid() >> 32;
// 2. Get process name
char comm[16];
bpf_get_current_comm(comm, sizeof(comm));
// 3. Get system call parameters
// unlinkat(int dfd, const char *pathname, int flag)
int dfd = (int)ctx->args[0];
const char *pathname = (const char *)ctx->args[1];
int flag = (int)ctx->args[2];
// 4. Print information
bpf_printk("pid: %d, comm: %s, pathname: %s\n", pid, comm, pathname);
return 0;
}
char LICENSE[] SEC("license") = "GPL";Complete Example
Example Scenario: Monitoring File Deletion Operations
Step 1: Find Tracepoint Events
# Find unlinkat-related events
bpftrace -l 'tracepoint:*' | grep unlinkat
# Output:
# tracepoint:syscalls:sys_enter_unlinkat
# tracepoint:syscalls:sys_exit_unlinkatStep 2: View Event Parameters
# View parameters of sys_enter_unlinkat
bpftrace -l tracepoint:syscalls:sys_enter_unlinkat -v
# Output:
# int __syscall_nr
# int dfd
# const char * pathname
# int flagStep 3: Write eBPF Program
Refer to the complete example code above.
Step 4: Compile and Run
# Compile eBPF program
make
# Run program (requires root privileges)
sudo ./tracepoint
# Test in another terminal
touch /tmp/test.txt
rm /tmp/test.txtCommon Questions
Q1: How to choose sys_enter or sys_exit?
- sys_enter: Triggered when entering a system call, can obtain input parameters
- sys_exit: Triggered when exiting a system call, can obtain return values and results
Q2: How to determine the args array index?
Follow the parameter order in the system call function prototype:
int unlinkat(int dfd, const char *pathname, int flag);
// args[0] args[1] args[2]Q3: Why can't some fields be accessed?
The first 8 bytes of common fields are used internally by the kernel and cannot be directly accessed by eBPF programs. Only the system call parameters in the args array can be accessed.
Reference Resources
Summary
Steps for writing Tracepoint eBPF programs:
- ✅ Use
bpftrace -lor checkavailable_eventsto find the target event - ✅ Use
bpftrace -l -vor check theformatfile to understand event parameters - ✅ Find the corresponding structure definition in
vmlinux.h - ✅ Write the eBPF program, accessing parameters through the
argsarray - ✅ Compile, run, and test
Tracepoints provide stable and efficient kernel tracing capabilities, making them an essential tool for eBPF programming.