mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-02-03 10:14:44 +08:00
docs: enhance README files with detailed explanations and improved clarity across multiple tutorials
This commit is contained in:
@@ -62,15 +62,15 @@ This tutorial provides practical eBPF development practices, covering topics fro
|
||||
|
||||
### Who Is This Tutorial For?
|
||||
|
||||
- **Developers** looking to implement custom kernel solutions.
|
||||
- **System Administrators** aiming to enhance performance and security.
|
||||
- **Tech Enthusiasts** exploring cutting-edge kernel technologies.
|
||||
This tutorial is designed for developers who want to build custom kernel solutions, system administrators looking to enhance performance and security, and tech enthusiasts exploring cutting-edge kernel technologies. Whether you're debugging production issues or building the next generation of observability tools, this guide will help you get started with eBPF.
|
||||
|
||||
### What Will You Learn?
|
||||
|
||||
- **Core Concepts:** eBPF fundamentals and integration with the Linux kernel.
|
||||
- **Practical Skills:** Writing and deploying eBPF programs.
|
||||
- **Advanced Topics:** Exploring security, tracing, and future innovations in eBPF.
|
||||
You'll learn eBPF fundamentals and how they integrate with the Linux kernel. We'll cover practical skills for writing and deploying eBPF programs, from simple "Hello World" examples to advanced topics like security monitoring, tracing, and performance profiling. By the end, you'll be able to write your own eBPF tools and understand how to apply them to real-world problems.
|
||||
|
||||
### Prerequisites
|
||||
|
||||
Before starting, make sure you have a Linux system with kernel version 4.8 or higher (we recommend 5.15+ or 6.2+ for full feature support). You'll need basic C programming knowledge and familiarity with system concepts like processes and system calls. Most examples require root or sudo privileges to run. You should also install development tools like clang, llvm, and libelf-dev on your system.
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ Once you have chosen a suitable development framework, such as BCC (BPF Compiler
|
||||
|
||||
Through the above process, you can develop, compile, run, and debug eBPF programs using the BCC tool. Note that the development process of other frameworks, such as libbpf, cilium/ebpf, and eunomia-bpf, is similar but slightly different. Therefore, when choosing a framework, please refer to the respective official documentation and examples.
|
||||
|
||||
By following this process, you can develop an eBPF program that runs in the kernel. eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain. It aims to simplify the development, building, distribution, and running of eBPF programs. It is based on the libbpf CO-RE lightweight development framework, supports loading and executing eBPF programs through a user space WebAssembly (WASM) virtual machine, and packages precompiled eBPF programs into universal JSON or WASM modules for distribution. We will use eunomia-bpf for demonstration purposes.
|
||||
We use eunomia-bpf to compile and run this example. You can install it from <https://github.com/eunomia-bpf/eunomia-bpf>.
|
||||
|
||||
## Download and Install eunomia-bpf Development Tools
|
||||
|
||||
@@ -98,11 +98,15 @@ int handle_tp(void *ctx)
|
||||
}
|
||||
```
|
||||
|
||||
This program defines a handle_tp function and attaches it to the sys_enter_write tracepoint using the SEC macro (i.e., it is executed when the write system call is entered). The function retrieves the process ID of the write system call invocation using the bpf_get_current_pid_tgid and bpf_printk functions, and prints it in the kernel log.
|
||||
This program defines a handle_tp function and attaches it to the sys_enter_write tracepoint using the SEC macro. This means it gets executed every time the write system call is entered. The function retrieves the process ID of the current process and prints it to the kernel log using `bpf_printk`.
|
||||
|
||||
- `bpf_printk()`: A simple mechanism to output information to the trace_pipe (/sys/kernel/debug/tracing/trace_pipe). This is fine for simple use cases, but it has limitations: a maximum of 3 parameters; the first parameter must be %s (i.e., a string); and the trace_pipe is globally shared in the kernel, so other programs using the trace_pipe concurrently might disrupt its output. A better approach is to use BPF_PERF_OUTPUT(), which will be discussed later.
|
||||
- `void *ctx`: ctx is originally a parameter of a specific type, but since it is not used here, it is written as void *.
|
||||
- `return 0;`: This is necessary, returning 0 (to know why, refer to #139 <https://github.com/iovisor/bcc/issues/139>).
|
||||
The `SEC("tp/syscalls/sys_enter_write")` macro tells the eBPF loader where to attach this function. The format is `tp` for tracepoint, followed by the subsystem (`syscalls`), and the specific event name (`sys_enter_write`). You can find available tracepoints by running `sudo ls /sys/kernel/debug/tracing/events/syscalls/` on your system.
|
||||
|
||||
The `BPF_NO_GLOBAL_DATA` macro at the top is for compatibility with older kernels (before 5.2) that don't support global variables in eBPF. If you're running a modern kernel (5.15+), this isn't strictly necessary but doesn't hurt to include for portability.
|
||||
|
||||
The `bpf_printk()` function is your friend for debugging. It outputs to `/sys/kernel/debug/tracing/trace_pipe`, which is a simple way to see what your eBPF program is doing. However, it has some limitations you should know about. It only accepts up to 3 parameters, the trace_pipe is shared globally across all eBPF programs, and it can impact performance on high-frequency events. For production use, you'll want to use ring buffers or perf event arrays, which we'll cover in later tutorials.
|
||||
|
||||
The `ctx` parameter contains tracepoint-specific data, but since we don't need it for this simple example, we just declare it as `void *`. In more advanced programs, you would cast this to access the tracepoint's arguments. Finally, eBPF programs must return an integer - returning 0 is standard practice for tracepoints.
|
||||
|
||||
To compile and run this program, you can use the ecc tool and ecli command. First, on Ubuntu/Debian, execute the following command:
|
||||
|
||||
@@ -141,13 +145,14 @@ $ sudo cat /sys/kernel/debug/tracing/trace_pipe | grep "BPF triggered sys_enter_
|
||||
|
||||
Once you stop the ecli process by pressing Ctrl+C, the corresponding output will also stop.
|
||||
|
||||
Note: If your Linux distribution (e.g. Ubuntu) does not have the tracing subsystem enabled by default, you may not see any output. Use the following command to enable this feature:
|
||||
If you don't see any output, the tracing subsystem might not be enabled on your system. This is common on some Linux distributions. You can enable it by running:
|
||||
|
||||
```console
|
||||
$ sudo su
|
||||
# echo 1 > /sys/kernel/debug/tracing/tracing_on
|
||||
$ sudo sh -c 'echo 1 > /sys/kernel/debug/tracing/tracing_on'
|
||||
```
|
||||
|
||||
If you're still not seeing output, make sure the program is actually loaded and running (you should see "Running eBPF program..." in the ecli terminal). Try triggering some write syscalls manually by running `echo "test" > /tmp/test.txt` in another terminal. You can also check if your eBPF program is loaded by running `sudo bpftool prog list`.
|
||||
|
||||
## Basic Framework of eBPF Program
|
||||
|
||||
As mentioned above, the basic framework of an eBPF program includes:
|
||||
@@ -190,6 +195,6 @@ The development and usage process of eBPF programs can be summarized in the foll
|
||||
- Use the eBPF program: This includes monitoring the execution of the eBPF program and exchanging and sharing data using eBPF kernel maps and shared memory.
|
||||
- In practical development, there may be additional steps such as configuring compilation and loading parameters, managing eBPF kernel modules and kernel maps, and using other advanced features.
|
||||
|
||||
It should be noted that the execution of BPF programs occurs in the kernel space, so special tools and techniques are needed to write, compile, and debug BPF programs. eunomia-bpf is an open-source BPF compiler and toolkit that can help developers write and run BPF programs quickly and easily.
|
||||
The execution of BPF programs occurs in the kernel space, so special tools and techniques are needed to write, compile, and debug them.
|
||||
|
||||
You can also visit our tutorial code repository <https://github.com/eunomia-bpf/bpf-developer-tutorial> or website <https://eunomia.dev/tutorials/> for more examples and complete tutorials, all of which are open-source. We will continue to share more about eBPF development practices to help you better understand and master eBPF technology.
|
||||
|
||||
@@ -235,7 +235,7 @@ The code for Softirq is similar, and I won't elaborate on it here.
|
||||
|
||||
## Run code.Translated content
|
||||
|
||||
eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain that combines Wasm. Its purpose is to simplify the development, building, distribution, and execution of eBPF programs. You can refer to <https://github.com/eunomia-bpf/eunomia-bpf> to download and install the ecc compilation toolchain and ecli runtime. We use eunomia-bpf to compile and run this example.
|
||||
We use eunomia-bpf to compile and run this example. You can install it from <https://github.com/eunomia-bpf/eunomia-bpf>.
|
||||
|
||||
To compile this program, use the ecc tool:
|
||||
|
||||
|
||||
@@ -76,7 +76,7 @@ First, we import necessary header files such as vmlinux.h, bpf_helpers.h, bpf_tr
|
||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
||||
```
|
||||
|
||||
Next, we define a kprobe named `BPF_KPROBE(do_unlinkat)` which gets triggered when the `do_unlinkat` function is entered. It takes two parameters: `dfd` (file descriptor) and `name` (filename structure pointer). In this kprobe, we retrieve the PID (process identifier) of the current process and then read the filename. Finally, we use the `bpf_printk` function to print the PID and filename in the kernel log.
|
||||
The first kprobe hooks into the entry point of `do_unlinkat`. The `BPF_KPROBE` macro makes it easy to access the function's parameters - in this case `dfd` (file descriptor) and `name` (a pointer to the filename structure). We grab the current process ID and then use `BPF_CORE_READ` to safely read the filename from kernel memory.
|
||||
|
||||
```c
|
||||
SEC("kprobe/do_unlinkat")
|
||||
@@ -92,7 +92,9 @@ int BPF_KPROBE(do_unlinkat, int dfd, struct filename *name)
|
||||
}
|
||||
```
|
||||
|
||||
Next, we define a kretprobe named `BPF_KRETPROBE(do_unlinkat_exit)` that will be triggered when exiting the `do_unlinkat` function. The purpose of this kretprobe is to capture the return value (`ret`) of the function. We again obtain the PID of the current process and use the `bpf_printk` function to print the PID and return value in the kernel log.
|
||||
You might wonder why we can't just access `name->name` directly. The answer is that eBPF programs run in a restricted environment and need special helpers to safely read kernel memory. `BPF_CORE_READ` handles this safely and also provides CO-RE (Compile Once - Run Everywhere) support, meaning your program will work across different kernel versions even if the structure layout changes.
|
||||
|
||||
The kretprobe is the counterpart that triggers when the function returns. Here we can capture the return value to see if the unlink operation succeeded or failed. A return value of 0 means success, while negative values indicate errors.
|
||||
|
||||
```c
|
||||
SEC("kretprobe/do_unlinkat")
|
||||
@@ -106,7 +108,9 @@ int BPF_KRETPROBE(do_unlinkat_exit, long ret)
|
||||
}
|
||||
```
|
||||
|
||||
eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain that combines with Wasm. Its goal is to simplify the development, build, distribution, and execution of eBPF programs. You can refer to <https://github.com/eunomia-bpf/eunomia-bpf> to download and install the ecc compiler toolchain and ecli runtime.
|
||||
By combining kprobe and kretprobe, you get the complete picture - you can see what file is being deleted and whether the operation succeeded. This pattern is useful for debugging, security monitoring, or building observability tools.
|
||||
|
||||
We use eunomia-bpf to compile and run this example. You can install it from <https://github.com/eunomia-bpf/eunomia-bpf>.
|
||||
|
||||
To compile this program, use the ecc tool:
|
||||
|
||||
|
||||
@@ -4,23 +4,15 @@ eBPF (Extended Berkeley Packet Filter) is a powerful network and performance ana
|
||||
|
||||
This article is the third part of the eBPF Tutorial by Example, focusing on capturing unlink system calls using fentry in eBPF.
|
||||
|
||||
## Fentry
|
||||
## What is fentry and Why Use It?
|
||||
|
||||
fentry (function entry) and fexit (function exit) are two types of probes in eBPF (Extended Berkeley Packet Filter) used for tracing at the entry and exit points of Linux kernel functions. They allow developers to collect information, modify parameters, or observe return values at specific stages of kernel function execution. This tracing and monitoring functionality is very useful in performance analysis, troubleshooting, and security analysis scenarios.
|
||||
fentry (function entry) and fexit (function exit) are the modern way to trace kernel functions in eBPF. They were introduced in kernel 5.5 for x86 processors and 6.0 for ARM processors. Think of them as the faster, more efficient successors to kprobes.
|
||||
|
||||
Compared to kprobes, fentry and fexit programs have higher performance and availability. In this example, we can directly access the pointers to the functions' parameters, just like in regular C code, without needing various read helpers. The main difference between fexit and kretprobe programs is that fexit programs can access both the input parameters and return values of a function, while kretprobe programs can only access the return value. Starting from the 5.5 kernel, fentry and fexit are available for eBPF programs.
|
||||
The big advantage of fentry/fexit over kprobes is performance and convenience. With fentry, you can directly access function parameters just like in regular C code - no need for special helpers like `BPF_CORE_READ`. This makes your code simpler and faster. fexit gives you an extra bonus - you can access both the input parameters and the return value at the same time, while kretprobe only gives you the return value.
|
||||
|
||||
> arm64 kernel version requires 6.0
|
||||
>
|
||||
> Refer to the learning eBPF documentation:
|
||||
>
|
||||
> A more efficient mechanism for tracing the entry to and exit from kernel functions
|
||||
> was introduced along with the idea of BPF trampoline in kernel version 5.5 (on x86
|
||||
> processors; BPF trampoline support doesn’t arrive for ARM processors until Linux
|
||||
> 6.0). If you’re using a recent enough kernel, fentry/fexit is now the preferred method
|
||||
> for tracing the entry to or exit from a kernel function
|
||||
>
|
||||
> Reference: https://kernelnewbies.org/Linux_6.0#ARM
|
||||
The performance difference is real. fentry/fexit programs run about 10x faster than kprobes because they use a BPF trampoline mechanism instead of the older breakpoint-based approach. If you're building production monitoring tools that run on every function call, this matters a lot.
|
||||
|
||||
Note that if you're on ARM, you'll need kernel 6.0 or newer. For x86, kernel 5.5+ works fine. If you're stuck on an older kernel, you'll need to use kprobes instead.
|
||||
|
||||
|
||||
|
||||
@@ -54,14 +46,11 @@ int BPF_PROG(do_unlinkat_exit, int dfd, struct filename *name, long ret)
|
||||
}
|
||||
```
|
||||
|
||||
This program is an eBPF (Extended Berkeley Packet Filter) program written in the C language. It uses BPF fentry and fexit probes to trace the Linux kernel function `do_unlinkat`. In this tutorial, we will use this program as an example to learn how to use fentry in eBPF to detect and capture unlink system calls.
|
||||
Let's break down how this program works. The fentry probe attaches to the entry of `do_unlinkat` and can access the function's parameters directly. Notice how we access `name->name` without any special helpers - this is one of the benefits of using fentry instead of kprobes.
|
||||
|
||||
The program consists of the following parts:
|
||||
The fexit probe is even more interesting. It gets triggered when the function returns, and it can access both the original parameters (dfd and name) and the return value (ret). This gives you complete visibility into what the function did. If ret is 0, the file was successfully deleted. If it's negative, something went wrong and you can see the error code.
|
||||
|
||||
1. Include header files: including vmlinux.h (for accessing kernel data structures), bpf/bpf_helpers.h (which includes eBPF helper functions), bpf/bpf_tracing.h (for eBPF tracing-related functionalities).
|
||||
2. Define license: Here, a character array named `LICENSE` is defined, containing the license information "Dual BSD/GPL".
|
||||
3. Define fentry probe: We define an fentry probe named `BPF_PROG(do_unlinkat)` that is triggered at the entry point of the `do_unlinkat` function. This probe retrieves the PID (Process ID) of the current process and prints it along with the filename to the kernel log.
|
||||
4. Define fexit probe: We also define an fexit probe named `BPF_PROG(do_unlinkat_exit)` that is triggered at the exit point of the `do_unlinkat` function. Similar to the fentry probe, this probe also retrieves the PID of the current process and prints it along with the filename and return value to the kernel log.
|
||||
The `BPF_PROG` macro is similar to `BPF_KPROBE` from the previous tutorial, but it's designed for fentry/fexit. It handles the parameter unwrapping automatically so you can focus on your logic.
|
||||
|
||||
Through this example, you can learn how to use fentry and fexit probes in eBPF to monitor and capture kernel function calls, such as the unlink system call in this tutorial. "eunomia-bpf is an open source eBPF dynamic loading runtime and development toolchain combined with Wasm. Its goal is to simplify the development, building, distribution, and running of eBPF programs. You can refer to [here](https://github.com/eunomia-bpf/eunomia-bpf) to download and install the ecc compilation toolchain and ecli runtime. We use eunomia-bpf to compile and run this example.
|
||||
|
||||
|
||||
@@ -37,20 +37,17 @@ int tracepoint__syscalls__sys_enter_openat(struct trace_event_raw_sys_enter* ctx
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
```
|
||||
|
||||
This eBPF program implements the following:
|
||||
Let's look at the key parts of this program. The global variable `pid_target` is declared with `const volatile`. The `const` means the eBPF program can't modify it (it's read-only from the kernel's perspective), while `volatile` tells the compiler that user-space can modify it before loading the program. This combination lets you pass runtime configuration from user-space to your eBPF program.
|
||||
|
||||
1. Include header files: <vmlinux.h> contains the definition of kernel data structures, and <bpf/bpf_helpers.h> contains the helper functions required by eBPF programs.
|
||||
2. Define the global variable `pid_target` for filtering a specified process ID. Setting it to 0 captures sys_openat calls from all processes.
|
||||
3. Use the `SEC` macro to define an eBPF program associated with the tracepoint "tracepoint/syscalls/sys_enter_openat". This tracepoint is triggered when a process initiates the `sys_openat` system call.
|
||||
4. Implement the eBPF program `tracepoint__syscalls__sys_enter_openat`, which takes a parameter `ctx` of type `struct trace_event_raw_sys_enter`. This structure contains information about the system call.
|
||||
5. Use the `bpf_get_current_pid_tgid()` function to retrieve the PID and TID (Thread ID) of the current process. Since we only care about the PID, we shift its value 32 bits to the right and assign it to the variable `pid` of Type `u32`.
|
||||
6. Check if the `pid_target` variable is equal to the current process's PID. If `pid_target` is not 0 and is not equal to the current process's PID, return `false` to skip capturing the `sys_openat` call of that process.
|
||||
7. Use the `bpf_printk()` function to print the captured process ID and relevant information about the `sys_openat` call. These information can be viewed in user space using BPF tools.
|
||||
8. Set the program license to "GPL", which is a necessary condition for running eBPF programs.
|
||||
When `pid_target` is 0, the program captures openat calls from all processes. If you set it to a specific PID, it only monitors that process. This filtering happens in the kernel, which is much more efficient than filtering in user-space - you're not wasting resources sending events you don't care about.
|
||||
|
||||
The filtering logic is straightforward. We get the current process ID using `bpf_get_current_pid_tgid()` and shift it right by 32 bits to extract just the PID (the function returns both PID and TID packed into a 64-bit value). Then we check if it matches our target. If not, we return early and skip the event.
|
||||
|
||||
The annotation `/// @description "Process ID to trace"` above the global variable is special - eunomia-bpf uses it to automatically generate command-line help text. This makes your tool more user-friendly without extra code.
|
||||
|
||||
This eBPF program can be loaded into the kernel and executed using tools like libbpf or eunomia-bpf. It captures the sys_openat system call of the specified process (or all processes) and outputs relevant information in user-space.
|
||||
|
||||
eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain combined with Wasm. Its purpose is to simplify the development, building, distribution, and execution of eBPF programs. You can refer to <https://github.com/eunomia-bpf/eunomia-bpf> to download and install the ecc compilation toolchain and ecli runtime. We will use eunomia-bpf to compile and run this example. The complete code of this example can be found at <https://github.com/eunomia-bpf/bpf-developer-tutorial/tree/main/src/4-opensnoop> .
|
||||
We use eunomia-bpf to compile and run this example. You can install it from <https://github.com/eunomia-bpf/eunomia-bpf>. The complete code is at <https://github.com/eunomia-bpf/bpf-developer-tutorial/tree/main/src/4-opensnoop>.
|
||||
|
||||
Compile and run the above code:
|
||||
|
||||
@@ -72,9 +69,9 @@ $ sudo cat /sys/kernel/debug/tracing/trace_pipe
|
||||
|
||||
At this point, we are able to capture the sys_openat system call for opening files by processes.
|
||||
|
||||
## Filtering Process PID in eBPF using Global Variables
|
||||
## How Global Variables Work in eBPF
|
||||
|
||||
Global variables act as a data sharing mechanism in eBPF programs, allowing data interaction between user space programs and eBPF programs. This is very useful when filtering specific conditions or modifying the behavior of eBPF programs. This design allows user space programs to dynamically control the behavior of eBPF programs at runtime.
|
||||
Global variables in eBPF are stored in the data section of your compiled program. When you load the eBPF program into the kernel, these variables get their initial values. The neat part is that user-space can modify these values before the program starts running, effectively passing configuration parameters into your kernel code.
|
||||
|
||||
In our example, the global variable `pid_target` is used to filter process PIDs. User space programs can set the value of this variable to capture only the `sys_openat` system calls related to the specified PID in the eBPF program.
|
||||
|
||||
|
||||
@@ -4,7 +4,9 @@ eBPF (Extended Berkeley Packet Filter) is a powerful network and performance ana
|
||||
|
||||
This article is the fifth part of the eBPF Tutorial by Example, which mainly introduces how to capture readline function calls in bash using uprobe.
|
||||
|
||||
## What is uprobe
|
||||
## What is uprobe and When Do You Need It?
|
||||
|
||||
While kprobes let you trace kernel functions, uprobes bring the same power to user-space programs. You can attach to any function in any binary: libraries, executables, even JIT-compiled code. This is incredibly useful for monitoring applications without modifying their code.
|
||||
|
||||
uprobe is a user-space probe that allows dynamic instrumentation in user-space programs. The probe locations include function entry, specific offsets, and function returns. When we define an uprobe, the kernel creates fast breakpoint instructions (int3 instructions on x86 machines) on the attached instructions. When the program executes this instruction, the kernel triggers an event, causing the program to enter kernel mode and call the probe function through a callback function. After executing the probe function, the program returns to user mode to continue executing subsequent instructions.
|
||||
|
||||
@@ -95,7 +97,7 @@ Finally, we use the `bpf_printk` function to output the PID, task name, and user
|
||||
bpf_printk("PID %d (%s) read: %s ", pid, comm, str);
|
||||
```
|
||||
|
||||
eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain combined with Wasm. Its purpose is to simplify the development, build, distribution, and running of eBPF programs. You can refer to <https://github.com/eunomia-bpf/eunomia-bpf> to download and install the ecc compiler toolchain and ecli runtime. We use eunomia-bpf to compile and run this example.
|
||||
We use eunomia-bpf to compile and run this example. You can install it from <https://github.com/eunomia-bpf/eunomia-bpf>.
|
||||
|
||||
Compile and run the above code:
|
||||
|
||||
|
||||
@@ -87,13 +87,15 @@ int kill_exit(struct trace_event_raw_sys_exit *ctx)
|
||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
||||
```
|
||||
|
||||
The above code defines an eBPF program for capturing system calls that send signals to processes, including kill, tkill, and tgkill. It captures the enter and exit events of system calls by using tracepoints, and executes specified probe functions such as `probe_entry` and `probe_exit` when these events occur.
|
||||
This program tracks signal sending across entry and exit events. The challenge is that we need to correlate data from two separate events; when the syscall starts and when it finishes. That's where hash maps come in.
|
||||
|
||||
In the probe function, we use the bpf_map to store the captured event information, including the process ID of the sending signal, the process ID of the receiving signal, the signal value, and the name of the executable for the current task. When the system call exits, we retrieve the event information stored in the bpf_map and use bpf_printk to print the process ID, process name, sent signal, and return value of the system call.
|
||||
When `probe_entry` runs, we save the signal information in a hash map using the thread ID (TID) as the key. When `probe_exit` runs for the same thread, we look up the saved data, add the return value, and print everything together. This gives us complete information about each signal - who sent it, who received it, what signal it was, and whether it succeeded.
|
||||
|
||||
The hash map is keyed by TID rather than PID because threads make syscalls independently. Using TID ensures we don't accidentally mix up events from different threads in the same process.
|
||||
|
||||
Finally, we also need to use the SEC macro to define the probe and specify the name of the system call to be captured and the probe function to be executed.
|
||||
|
||||
eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain that combines with Wasm. Its purpose is to simplify the development, building, distribution, and running of eBPF programs. You can refer to <https://github.com/eunomia-bpf/eunomia-bpf> for downloading and installing the ecc compilation toolchain and ecli runtime. We use eunomia-bpf to compile and run this example.
|
||||
We use eunomia-bpf to compile and run this example. You can install it from <https://github.com/eunomia-bpf/eunomia-bpf>.
|
||||
|
||||
Compile and run the above code:
|
||||
|
||||
|
||||
@@ -6,7 +6,9 @@ This article is the seventh part of the eBPF Tutorial by Example and mainly intr
|
||||
|
||||
## perf buffer
|
||||
|
||||
eBPF provides two circular buffers for transferring information from eBPF programs to user space controllers. The first one is the perf circular buffer, which has existed since at least kernel v4.15. The second one is the BPF circular buffer introduced later. This article only considers the perf circular buffer.
|
||||
When you need to send structured data from your eBPF program to user-space, `bpf_printk` isn't enough. That's where perf event arrays come in. They let you send custom data structures efficiently from kernel to user-space.
|
||||
|
||||
eBPF offers two types of ring buffers for this: the perf event array (available since kernel 4.15+) and the newer BPF ring buffer (kernel 5.8+). This tutorial uses the perf event array since it's more widely supported. The newer ring buffer is more efficient and easier to use, which we'll cover in the next tutorial.
|
||||
|
||||
## execsnoop
|
||||
|
||||
|
||||
@@ -106,23 +106,17 @@ int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
}
|
||||
```
|
||||
|
||||
This code demonstrates how to monitor process exit events using exitsnoop and print output to user space using a ring buffer:
|
||||
The ring buffer uses a reserve/submit pattern. First, you call `bpf_ringbuf_reserve` to allocate space in the buffer. This gives you a pointer where you can write your data. Once you've filled in all the fields, you call `bpf_ringbuf_submit` to make it available to user-space. If anything goes wrong before submit, you can call `bpf_ringbuf_discard` instead.
|
||||
|
||||
1. First, we include the required headers and exitsnoop.h.
|
||||
2. We define a global variable named "LICENSE" with the content "Dual BSD/GPL", which is the license requirement for eBPF programs.
|
||||
3. We define a mapping named rb of type BPF_MAP_TYPE_RINGBUF, which will be used to transfer data from kernel space to user space. We specify max_entries as 256 * 1024, representing the maximum capacity of the ring buffer.
|
||||
4. We define an eBPF program named handle_exit, which will be executed when a process exit event is triggered. It takes a trace_event_raw_sched_process_template struct pointer named ctx as the parameter.
|
||||
5. We use the bpf_get_current_pid_tgid() function to obtain the PID and TID of the current task. For the main thread, the PID and TID are the same; for child threads, they are different. Since we only care about the exit of the process (main thread), we return 0 if the PID and TID are different, ignoring the exit events of child threads.
|
||||
6. We use the bpf_ringbuf_reserve function to reserve space for the event struct e in the ring buffer. If the reservation fails, we return 0.
|
||||
7. We use the bpf_get_current_task() function to obtain a task_struct structure pointer for the current task.
|
||||
8. We fill in the process-related information into the reserved event struct e, including the duration of the process, PID, PPID, exit code, and process name.
|
||||
9. Finally, we use the bpf_ringbuf_submit function to submit the filled event struct e to the ring buffer, for further processing and output in user space.
|
||||
This is different from perf buffers where you pass a complete struct to `bpf_perf_event_output`. The reserve/submit pattern is more flexible and can be more efficient since you're writing directly into the buffer without extra copies.
|
||||
|
||||
The program filters out thread exits by checking `if (pid != tid)`. When a thread exits, its TID differs from the main process PID. We only want to track full process exits, not individual thread exits, so we skip those events.
|
||||
|
||||
This example demonstrates how to capture process exit events using exitsnoop and a ring buffer in an eBPF program, and transfer relevant information to user space. This is useful for analyzing process exit reasons and monitoring system behavior.
|
||||
|
||||
## Compile and Run
|
||||
|
||||
eunomia-bpf is an open-source eBPF dynamic loading runtime and development toolchain that combines with Wasm. Its purpose is to simplify the development, build, distribution, and execution of eBPF programs. You can refer to <https://github.com/eunomia-bpf/eunomia-bpf> to download and install the ecc compiler toolchain and ecli runtime. We will use eunomia-bpf to compile and run this example.
|
||||
We use eunomia-bpf to compile and run this example. You can install it from <https://github.com/eunomia-bpf/eunomia-bpf>.
|
||||
|
||||
Compile:
|
||||
|
||||
|
||||
Reference in New Issue
Block a user