mirror of
https://github.com/eunomia-bpf/bpf-developer-tutorial.git
synced 2026-02-03 10:14:44 +08:00
fix code for sigsnoop and exec/exit
This commit is contained in:
5
.vscode/settings.json
vendored
Normal file
5
.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
{
|
||||
"files.associations": {
|
||||
"numbers": "c"
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ char LICENSE[] SEC("license") = "GPL";
|
||||
|
||||
```
|
||||
|
||||
上面的 eBPF 程序通过定义两个函数 tracepoint__syscalls__sys_enter_open 和 tracepoint__syscalls__sys_enter_openat 并使用 SEC 宏把它们附加到 sys_enter_open 和 sys_enter_openat 两个 tracepoint(即在进入 open 和 openat 系统调用时执行)。这两个函数通过使用 bpf_get_current_pid_tgid 函数获取调用 open 或 openat 系统调用的进程 ID,并使用 bpf_printk 函数在内核日志中打印出来。
|
||||
上面的 eBPF 程序通过定义函数 tracepoint__syscalls__sys_enter_openat 并使用 SEC 宏把它们附加到 sys_enter_openat 的 tracepoint(即在进入 openat 系统调用时执行)。这个函数通过使用 bpf_get_current_pid_tgid 函数获取调用 openat 系统调用的进程 ID,并使用 bpf_printk 函数在内核日志中打印出来。
|
||||
|
||||
编译运行上述代码:
|
||||
|
||||
@@ -94,6 +94,6 @@ $ sudo cat /sys/kernel/debug/tracing/trace_pipe
|
||||
|
||||
## 总结
|
||||
|
||||
本文介绍了如何使用 eBPF 程序来捕获进程打开文件的系统调用。在 eBPF 程序中,我们可以通过定义 tracepoint__syscalls__sys_enter_open 和 tracepoint__syscalls__sys_enter_openat 函数并使用 SEC 宏把它们附加到 sys_enter_open 和 sys_enter_openat 两个 tracepoint 来捕获进程打开文件的系统调用。在这两个函数中,我们可以使用 bpf_get_current_pid_tgid 函数获取调用 open 或 openat 系统调用的进程 ID,并使用 bpf_printk 函数在内核日志中打印出来。在 eBPF 程序中,我们还可以通过定义一个全局变量 pid_target 来指定要捕获的进程的 pid,从而过滤输出,只输出指定的进程的信息。
|
||||
本文介绍了如何使用 eBPF 程序来捕获进程打开文件的系统调用。在 eBPF 程序中,我们可以通过定义 tracepoint__syscalls__sys_enter_open 和 tracepoint__syscalls__sys_enter_openat 函数并使用 SEC 宏把它们附加到 sys_enter_open 和 sys_enter_openat 两个 tracepoint 来捕获进程打开文件的系统调用。我们可以使用 bpf_get_current_pid_tgid 函数获取调用 open 或 openat 系统调用的进程 ID,并使用 bpf_printk 函数在内核日志中打印出来。在 eBPF 程序中,我们还可以通过定义一个全局变量 pid_target 来指定要捕获的进程的 pid,从而过滤输出,只输出指定的进程的信息。
|
||||
|
||||
更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档:<https://github.com/eunomia-bpf/eunomia-bpf>
|
||||
|
||||
@@ -83,7 +83,6 @@ BPF_KRETPROBE(printret, const void *ret)
|
||||
|
||||
编译运行上述代码:
|
||||
|
||||
|
||||
```console
|
||||
$ ecc bashreadline.bpf.c bashreadline.h
|
||||
Compiling bpf object...
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
# eBPF 入门开发实践指南六:捕获进程发送信号的系统调用集合,使用 hash map 保存状态
|
||||
|
||||
eBPF (Extended Berkeley Packet Filter) 是 Linux 内核上的一个强大的网络和性能分析工具,它允许开发者在内核运行时动态加载、更新和运行用户定义的代码。
|
||||
|
||||
本文是 eBPF 入门开发实践指南的第六篇,主要介绍如何实现一个 eBPF 工具,捕获进程发送信号的系统调用集合,使用 hash map 保存状态。
|
||||
|
||||
## sigsnoop
|
||||
|
||||
示例代码如下:
|
||||
@@ -7,9 +11,18 @@
|
||||
```c
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include "sigsnoop.h"
|
||||
#include <bpf/bpf_tracing.h>
|
||||
|
||||
#define MAX_ENTRIES 10240
|
||||
#define TASK_COMM_LEN 16
|
||||
|
||||
struct event {
|
||||
unsigned int pid;
|
||||
unsigned int tpid;
|
||||
int sig;
|
||||
int ret;
|
||||
char comm[TASK_COMM_LEN];
|
||||
};
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
@@ -23,11 +36,11 @@ static int probe_entry(pid_t tpid, int sig)
|
||||
{
|
||||
struct event event = {};
|
||||
__u64 pid_tgid;
|
||||
__u32 pid, tid;
|
||||
__u32 tid;
|
||||
|
||||
pid_tgid = bpf_get_current_pid_tgid();
|
||||
pid = pid_tgid >> 32;
|
||||
event.pid = pid;
|
||||
tid = (__u32)pid_tgid;
|
||||
event.pid = pid_tgid >> 32;
|
||||
event.tpid = tpid;
|
||||
event.sig = sig;
|
||||
bpf_get_current_comm(event.comm, sizeof(event.comm));
|
||||
@@ -47,7 +60,7 @@ static int probe_exit(void *ctx, int ret)
|
||||
|
||||
eventp->ret = ret;
|
||||
bpf_printk("PID %d (%s) sent signal %d to PID %d, ret = %d",
|
||||
eventp->pid, eventp->comm, eventp->sig, eventp->tpid, eventp->ret);
|
||||
eventp->pid, eventp->comm, eventp->sig, eventp->tpid, ret);
|
||||
|
||||
cleanup:
|
||||
bpf_map_delete_elem(&values, &tid);
|
||||
@@ -69,21 +82,6 @@ int kill_exit(struct trace_event_raw_sys_exit *ctx)
|
||||
return probe_exit(ctx, ctx->ret);
|
||||
}
|
||||
|
||||
SEC("tracepoint/syscalls/sys_enter_tkill")
|
||||
int tkill_entry(struct trace_event_raw_sys_enter *ctx)
|
||||
{
|
||||
pid_t tpid = (pid_t)ctx->args[0];
|
||||
int sig = (int)ctx->args[1];
|
||||
|
||||
return probe_entry(tpid, sig);
|
||||
}
|
||||
|
||||
SEC("tracepoint/syscalls/sys_exit_tkill")
|
||||
int tkill_exit(struct trace_event_raw_sys_exit *ctx)
|
||||
{
|
||||
return probe_exit(ctx, ctx->ret);
|
||||
}
|
||||
|
||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
||||
```
|
||||
|
||||
@@ -93,52 +91,34 @@ char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
||||
|
||||
最后,我们还需要使用 SEC 宏来定义探针,并指定要捕获的系统调用的名称,以及要执行的探针函数。
|
||||
|
||||
origin from:
|
||||
|
||||
https://github.com/iovisor/bcc/blob/master/libbpf-tools/sigsnoop.bpf.c
|
||||
|
||||
## Compile and Run
|
||||
|
||||
Compile:
|
||||
编译运行上述代码:
|
||||
|
||||
```shell
|
||||
docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
|
||||
```
|
||||
|
||||
Or compile with `ecc`:
|
||||
或者
|
||||
|
||||
```console
|
||||
$ ecc sigsnoop.bpf.c sigsnoop.h
|
||||
Compiling bpf object...
|
||||
Generating export types...
|
||||
Packing ebpf object and config into package.json...
|
||||
$ sudo ecli package.json
|
||||
Runing eBPF program...
|
||||
```
|
||||
|
||||
Run:
|
||||
运行这段程序后,可以通过查看 /sys/kernel/debug/tracing/trace_pipe 文件来查看 eBPF 程序的输出:
|
||||
|
||||
```console
|
||||
$ sudo ./ecli examples/bpftools/sigsnoop/package.json
|
||||
TIME PID TPID SIG RET COMM
|
||||
20:43:44 21276 3054 0 0 cpptools-srv
|
||||
20:43:44 22407 3054 0 0 cpptools-srv
|
||||
20:43:44 20222 3054 0 0 cpptools-srv
|
||||
20:43:44 8933 3054 0 0 cpptools-srv
|
||||
20:43:44 2915 2803 0 0 node
|
||||
20:43:44 2943 2803 0 0 node
|
||||
20:43:44 31453 3054 0 0 cpptools-srv
|
||||
$ sudo ./ecli examples/bpftools/sigsnoop/package.json -h
|
||||
Usage: sigsnoop_bpf [--help] [--version] [--verbose] [--filtered_pid VAR] [--target_signal VAR] [--failed_only]
|
||||
|
||||
A simple eBPF program
|
||||
|
||||
Optional arguments:
|
||||
-h, --help shows help message and exits
|
||||
-v, --version prints version information and exits
|
||||
--verbose prints libbpf debug information
|
||||
--filtered_pid set value of pid_t variable filtered_pid
|
||||
--target_signal set value of int variable target_signal
|
||||
--failed_only set value of bool variable failed_only
|
||||
|
||||
Built with eunomia-bpf framework.
|
||||
See https://github.com/eunomia-bpf/eunomia-bpf for more information.
|
||||
$ sudo cat /sys/kernel/debug/tracing/trace_pipe
|
||||
node-3517 [003] d..31 82575.798191: bpf_trace_printk: PID 3517 (node) sent signal 0 to PID 3427, ret = 0
|
||||
node-15194 [003] d..31 82575.849227: bpf_trace_printk: PID 15194 (node) sent signal 0 to PID 3427, ret = 0
|
||||
node-30016 [003] d..31 82576.001361: bpf_trace_printk: PID 30016 (node) sent signal 0 to PID 3427, ret = 0
|
||||
cpptools-srv-38617 [002] d..31 82576.461085: bpf_trace_printk: PID 38617 (cpptools-srv) sent signal 0 to PID 30496, ret = 0
|
||||
node-30040 [002] d..31 82576.467720: bpf_trace_printk: PID 30016 (node) sent signal 0 to PID 3427, ret = 0
|
||||
```
|
||||
|
||||
## 总结
|
||||
|
||||
更多的例子和详细的开发指南,请参考 eunomia-bpf 的官方文档:https://github.com/eunomia-bpf/eunomia-bpf
|
||||
@@ -25,11 +25,11 @@ static int probe_entry(pid_t tpid, int sig)
|
||||
{
|
||||
struct event event = {};
|
||||
__u64 pid_tgid;
|
||||
__u32 pid, tid;
|
||||
__u32 tid;
|
||||
|
||||
pid_tgid = bpf_get_current_pid_tgid();
|
||||
pid = pid_tgid >> 32;
|
||||
event.pid = pid;
|
||||
tid = (__u32)pid_tgid;
|
||||
event.pid = pid_tgid >> 32;
|
||||
event.tpid = tpid;
|
||||
event.sig = sig;
|
||||
bpf_get_current_comm(event.comm, sizeof(event.comm));
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
## eBPF 入门实践教程:编写 eBPF 程序 sigsnoop 工具监控全局 signal 事件
|
||||
|
||||
### 背景
|
||||
|
||||
### 实现原理
|
||||
|
||||
`sigsnoop` 在利用了linux的tracepoint挂载点,其在syscall进入和退出的各个关键挂载点均挂载了执行函数。
|
||||
```c
|
||||
|
||||
```
|
||||
|
||||
|
||||
### Eunomia中使用方式
|
||||
|
||||

|
||||

|
||||
|
||||
### 总结
|
||||
2
7-execsnoop/.gitignore
vendored
2
7-execsnoop/.gitignore
vendored
@@ -1,3 +1,3 @@
|
||||
ecli
|
||||
package.json
|
||||
*.json
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ struct event {
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include "execsnoop.bpf.h"
|
||||
#include "execsnoop.h"
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||
@@ -48,14 +48,10 @@ int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx
|
||||
{
|
||||
u64 id;
|
||||
pid_t pid, tgid;
|
||||
unsigned int ret;
|
||||
struct event event;
|
||||
struct task_struct *task;
|
||||
const char **args = (const char **)(ctx->args[1]);
|
||||
const char *argp;
|
||||
|
||||
uid_t uid = (u32)bpf_get_current_uid_gid();
|
||||
int i;
|
||||
id = bpf_get_current_pid_tgid();
|
||||
pid = (pid_t)id;
|
||||
tgid = id >> 32;
|
||||
@@ -63,70 +59,43 @@ int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx
|
||||
event.pid = tgid;
|
||||
event.uid = uid;
|
||||
task = (struct task_struct*)bpf_get_current_task();
|
||||
bpf_probe_read_str(&event.comm, sizeof(event.comm), task->comm);
|
||||
event.is_exit = false;
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tracepoint/syscalls/sys_exit_execve")
|
||||
int tracepoint__syscalls__sys_exit_execve(struct trace_event_raw_sys_exit* ctx)
|
||||
{
|
||||
u64 id;
|
||||
pid_t pid;
|
||||
int ret;
|
||||
struct event event;
|
||||
|
||||
u32 uid = (u32)bpf_get_current_uid_gid();
|
||||
|
||||
id = bpf_get_current_pid_tgid();
|
||||
pid = (pid_t)id;
|
||||
|
||||
ret = ctx->ret;
|
||||
event.retval = ret;
|
||||
event.pid = pid;
|
||||
event.uid = uid;
|
||||
event.is_exit = true;
|
||||
event.ppid = BPF_CORE_READ(task, real_parent, tgid);
|
||||
bpf_get_current_comm(&event.comm, sizeof(event.comm));
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
|
||||
return 0;
|
||||
}
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
|
||||
```
|
||||
|
||||
这段代码定义了两个 eBPF 程序,一个用于捕获进程执行 execve 系统调用的入口,另一个用于捕获进程执行 execve 系统调用的出口。
|
||||
这段代码定义了个 eBPF 程序,用于捕获进程执行 execve 系统调用的入口。
|
||||
|
||||
在入口程序中,我们首先获取了当前进程的进程 ID 和用户 ID,然后通过 bpf_get_current_task 函数获取了当前进程的 task_struct 结构体,并通过 bpf_probe_read_str 函数读取了进程名称。最后,我们通过 bpf_perf_event_output 函数将进程执行事件输出到 perf buffer。
|
||||
|
||||
在出口程序中,我们首先获取了进程的进程 ID 和用户 ID,然后通过 bpf_get_current_comm 函数获取了进程的名称,最后通过 bpf_perf_event_output 函数将进程执行事件输出到 perf buffer。
|
||||
使用这段代码,我们就可以捕获 Linux 内核中进程执行的事件, 并分析进程的执行情况。
|
||||
|
||||
使用这段代码,我们就可以捕获 Linux 内核中进程执行的事件。我们可以通过工具(例如 eunomia-bpf)来查看这些事件,并分析进程的执行情况。
|
||||
|
||||
## Compile and Run
|
||||
|
||||
Compile:
|
||||
使用容器编译:
|
||||
|
||||
```shell
|
||||
docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
|
||||
```
|
||||
|
||||
Run:
|
||||
或者使用 ecc 编译:
|
||||
|
||||
```shell
|
||||
ecc bootstrap.bpf.c bootstrap.h
|
||||
```
|
||||
$ sudo ./ecli run package.json
|
||||
|
||||
running and waiting for the ebpf events from perf event...
|
||||
time pid ppid uid retval args_count args_size comm args
|
||||
23:07:35 32940 32783 1000 0 1 13 cat /usr/bin/cat
|
||||
23:07:43 32946 24577 1000 0 1 10 bash /bin/bash
|
||||
23:07:43 32948 32946 1000 0 1 18 lesspipe /usr/bin/lesspipe
|
||||
23:07:43 32949 32948 1000 0 2 36 basename /usr/bin/basename
|
||||
23:07:43 32951 32950 1000 0 2 35 dirname /usr/bin/dirname
|
||||
23:07:43 32952 32946 1000 0 2 22 dircolors /usr/bin/dircolors
|
||||
23:07:48 32953 32946 1000 0 2 25 ls /usr/bin/ls
|
||||
23:07:53 32957 32946 1000 0 2 17 sleep /usr/bin/sleep
|
||||
23:07:57 32959 32946 1000 0 1 17 oneko /usr/games/oneko
|
||||
运行
|
||||
|
||||
```console
|
||||
$ sudo ./ecli run package.json
|
||||
TIME PID PPID UID COMM
|
||||
21:28:30 40747 3517 1000 node
|
||||
21:28:30 40748 40747 1000 sh
|
||||
21:28:30 40749 3517 1000 node
|
||||
21:28:30 40750 40749 1000 sh
|
||||
21:28:30 40751 3517 1000 node
|
||||
21:28:30 40752 40751 1000 sh
|
||||
21:28:30 40753 40752 1000 cpuUsage.sh
|
||||
```
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include "execsnoop.bpf.h"
|
||||
#include "execsnoop.h"
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||
@@ -15,14 +15,10 @@ int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx
|
||||
{
|
||||
u64 id;
|
||||
pid_t pid, tgid;
|
||||
unsigned int ret;
|
||||
struct event event;
|
||||
struct task_struct *task;
|
||||
const char **args = (const char **)(ctx->args[1]);
|
||||
const char *argp;
|
||||
|
||||
uid_t uid = (u32)bpf_get_current_uid_gid();
|
||||
int i;
|
||||
id = bpf_get_current_pid_tgid();
|
||||
pid = (pid_t)id;
|
||||
tgid = id >> 32;
|
||||
@@ -30,30 +26,7 @@ int tracepoint__syscalls__sys_enter_execve(struct trace_event_raw_sys_enter* ctx
|
||||
event.pid = tgid;
|
||||
event.uid = uid;
|
||||
task = (struct task_struct*)bpf_get_current_task();
|
||||
bpf_probe_read_str(&event.comm, sizeof(event.comm), task->comm);
|
||||
event.is_exit = false;
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tracepoint/syscalls/sys_exit_execve")
|
||||
int tracepoint__syscalls__sys_exit_execve(struct trace_event_raw_sys_exit* ctx)
|
||||
{
|
||||
u64 id;
|
||||
pid_t pid;
|
||||
int ret;
|
||||
struct event event;
|
||||
|
||||
u32 uid = (u32)bpf_get_current_uid_gid();
|
||||
|
||||
id = bpf_get_current_pid_tgid();
|
||||
pid = (pid_t)id;
|
||||
|
||||
ret = ctx->ret;
|
||||
event.retval = ret;
|
||||
event.pid = pid;
|
||||
event.uid = uid;
|
||||
event.is_exit = true;
|
||||
event.ppid = BPF_CORE_READ(task, real_parent, tgid);
|
||||
bpf_get_current_comm(&event.comm, sizeof(event.comm));
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU, &event, sizeof(event));
|
||||
return 0;
|
||||
|
||||
@@ -8,8 +8,6 @@ struct event {
|
||||
int pid;
|
||||
int ppid;
|
||||
int uid;
|
||||
int retval;
|
||||
bool is_exit;
|
||||
char comm[TASK_COMM_LEN];
|
||||
};
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
.vscode
|
||||
package.json
|
||||
eunomia-exporter
|
||||
ecli
|
||||
*.json
|
||||
72
8-exitsnoop/README.md
Normal file
72
8-exitsnoop/README.md
Normal file
@@ -0,0 +1,72 @@
|
||||
## eBPF 入门实践教程:
|
||||
|
||||
## origin
|
||||
|
||||
origin from:
|
||||
|
||||
https://github.com/iovisor/bcc/blob/master/libbpf-tools/runqslower.bpf.c
|
||||
|
||||
result:
|
||||
|
||||
```
|
||||
$ sudo ecli/build/bin/Release/ecli run examples/bpftools/runqslower/package.json
|
||||
|
||||
running and waiting for the ebpf events from perf event...
|
||||
time task prev_task delta_us pid prev_pid
|
||||
20:11:59 gnome-shell swapper/0 32 2202 0
|
||||
20:11:59 ecli swapper/3 23 3437 0
|
||||
20:11:59 rcu_sched swapper/1 1 14 0
|
||||
20:11:59 gnome-terminal- swapper/1 13 2714 0
|
||||
20:11:59 ecli swapper/3 2 3437 0
|
||||
20:11:59 kworker/3:3 swapper/3 3 215 0
|
||||
20:11:59 containerd swapper/1 8 1088 0
|
||||
20:11:59 ecli swapper/2 5 3437 0
|
||||
20:11:59 HangDetector swapper/3 6 854 0
|
||||
20:11:59 ecli swapper/2 60 3437 0
|
||||
20:11:59 rcu_sched swapper/1 26 14 0
|
||||
20:11:59 kworker/0:1 swapper/0 26 3414 0
|
||||
20:11:59 ecli swapper/2 6 3437 0
|
||||
```
|
||||
|
||||
这段代码定义了一个 eBPF 程序,该程序用于跟踪进程在运行队列中的等待时间。它通过使用 tracepoint 和 perf event 输出来实现。
|
||||
|
||||
程序首先定义了两个 BPF 内核映射:start 映射用于存储每个进程在被调度运行之前的时间戳,events 映射用于存储 perf 事件。
|
||||
|
||||
然后,程序定义了一些帮助函数,用于跟踪每个进程的调度状态。 trace_enqueue 函数用于在进程被调度运行之前记录时间戳, handle_switch 函数用于处理进程切换,并计算进程在队列中等待的时间。
|
||||
|
||||
接下来,程序定义了五个 tracepoint 程序,用于捕获不同的调度器事件。 sched_wakeup 和 sched_wakeup_new 程序用于捕获新进程被唤醒的事件, sched_switch 程序用于捕获进程切换事件, handle_sched_wakeup 和 handle_sched_wakeup_new 程序用于捕获 raw tracepoint 事件。这些 tracepoint 程序调用了前面定义的帮助函数来跟踪进程的调度状态。
|
||||
|
||||
最后,程序将计算得到的等待时间输出到 perf 事件中,供用户空间工具进行捕获和分析。
|
||||
|
||||
## Compile and Run
|
||||
|
||||
Compile:
|
||||
|
||||
```
|
||||
docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
|
||||
```
|
||||
|
||||
Or
|
||||
|
||||
```console
|
||||
$ ecc exitsnoop.bpf.c exitsnoop.h
|
||||
Compiling bpf object...
|
||||
Generating export types...
|
||||
Packing ebpf object and config into package.json...
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```console
|
||||
$ sudo ./ecli run package.json
|
||||
TIME PID PPID EXIT_CODE DURATION_NS COMM
|
||||
21:40:09 42050 42049 0 0 which
|
||||
21:40:09 42049 3517 0 0 sh
|
||||
21:40:09 42052 42051 0 0 ps
|
||||
21:40:09 42051 3517 0 0 sh
|
||||
21:40:09 42055 42054 0 0 sed
|
||||
21:40:09 42056 42054 0 0 cat
|
||||
21:40:09 42057 42054 0 0 cat
|
||||
21:40:09 42058 42054 0 0 cat
|
||||
21:40:09 42059 42054 0 0 cat
|
||||
```
|
||||
50
8-exitsnoop/exitsnoop.bpf.c
Normal file
50
8-exitsnoop/exitsnoop.bpf.c
Normal file
@@ -0,0 +1,50 @@
|
||||
// SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause
|
||||
/* Copyright (c) 2020 Facebook */
|
||||
#include "vmlinux.h"
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include "exitsnoop.h"
|
||||
|
||||
char LICENSE[] SEC("license") = "Dual BSD/GPL";
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_RINGBUF);
|
||||
__uint(max_entries, 256 * 1024);
|
||||
} rb SEC(".maps");
|
||||
|
||||
SEC("tp/sched/sched_process_exit")
|
||||
int handle_exit(struct trace_event_raw_sched_process_template* ctx)
|
||||
{
|
||||
struct task_struct *task;
|
||||
struct event *e;
|
||||
pid_t pid, tid;
|
||||
u64 id, ts, *start_ts, duration_ns = 0;
|
||||
|
||||
/* get PID and TID of exiting thread/process */
|
||||
id = bpf_get_current_pid_tgid();
|
||||
pid = id >> 32;
|
||||
tid = (u32)id;
|
||||
|
||||
/* ignore thread exits */
|
||||
if (pid != tid)
|
||||
return 0;
|
||||
|
||||
/* reserve sample from BPF ringbuf */
|
||||
e = bpf_ringbuf_reserve(&rb, sizeof(*e), 0);
|
||||
if (!e)
|
||||
return 0;
|
||||
|
||||
/* fill out the sample with data */
|
||||
task = (struct task_struct *)bpf_get_current_task();
|
||||
|
||||
e->duration_ns = duration_ns;
|
||||
e->pid = pid;
|
||||
e->ppid = BPF_CORE_READ(task, real_parent, tgid);
|
||||
e->exit_code = (BPF_CORE_READ(task, exit_code) >> 8) & 0xff;
|
||||
bpf_get_current_comm(&e->comm, sizeof(e->comm));
|
||||
|
||||
/* send data to user-space for post-processing */
|
||||
bpf_ringbuf_submit(e, 0);
|
||||
return 0;
|
||||
}
|
||||
15
8-exitsnoop/exitsnoop.h
Normal file
15
8-exitsnoop/exitsnoop.h
Normal file
@@ -0,0 +1,15 @@
|
||||
#ifndef __BOOTSTRAP_H
|
||||
#define __BOOTSTRAP_H
|
||||
|
||||
#define TASK_COMM_LEN 16
|
||||
#define MAX_FILENAME_LEN 127
|
||||
|
||||
struct event {
|
||||
int pid;
|
||||
int ppid;
|
||||
unsigned exit_code;
|
||||
unsigned long long duration_ns;
|
||||
char comm[TASK_COMM_LEN];
|
||||
};
|
||||
|
||||
#endif /* __BOOTSTRAP_H */
|
||||
@@ -1,155 +0,0 @@
|
||||
## eBPF 入门实践教程:
|
||||
|
||||
## origin
|
||||
|
||||
origin from:
|
||||
|
||||
https://github.com/iovisor/bcc/blob/master/libbpf-tools/runqslower.bpf.c
|
||||
|
||||
result:
|
||||
|
||||
```
|
||||
$ sudo ecli/build/bin/Release/ecli run examples/bpftools/runqslower/package.json
|
||||
|
||||
running and waiting for the ebpf events from perf event...
|
||||
time task prev_task delta_us pid prev_pid
|
||||
20:11:59 gnome-shell swapper/0 32 2202 0
|
||||
20:11:59 ecli swapper/3 23 3437 0
|
||||
20:11:59 rcu_sched swapper/1 1 14 0
|
||||
20:11:59 gnome-terminal- swapper/1 13 2714 0
|
||||
20:11:59 ecli swapper/3 2 3437 0
|
||||
20:11:59 kworker/3:3 swapper/3 3 215 0
|
||||
20:11:59 containerd swapper/1 8 1088 0
|
||||
20:11:59 ecli swapper/2 5 3437 0
|
||||
20:11:59 HangDetector swapper/3 6 854 0
|
||||
20:11:59 ecli swapper/2 60 3437 0
|
||||
20:11:59 rcu_sched swapper/1 26 14 0
|
||||
20:11:59 kworker/0:1 swapper/0 26 3414 0
|
||||
20:11:59 ecli swapper/2 6 3437 0
|
||||
```
|
||||
|
||||
这段代码定义了一个 eBPF 程序,该程序用于跟踪进程在运行队列中的等待时间。它通过使用 tracepoint 和 perf event 输出来实现。
|
||||
|
||||
程序首先定义了两个 BPF 内核映射:start 映射用于存储每个进程在被调度运行之前的时间戳,events 映射用于存储 perf 事件。
|
||||
|
||||
然后,程序定义了一些帮助函数,用于跟踪每个进程的调度状态。 trace_enqueue 函数用于在进程被调度运行之前记录时间戳, handle_switch 函数用于处理进程切换,并计算进程在队列中等待的时间。
|
||||
|
||||
接下来,程序定义了五个 tracepoint 程序,用于捕获不同的调度器事件。 sched_wakeup 和 sched_wakeup_new 程序用于捕获新进程被唤醒的事件, sched_switch 程序用于捕获进程切换事件, handle_sched_wakeup 和 handle_sched_wakeup_new 程序用于捕获 raw tracepoint 事件。这些 tracepoint 程序调用了前面定义的帮助函数来跟踪进程的调度状态。
|
||||
|
||||
最后,程序将计算得到的等待时间输出到 perf 事件中,供用户空间工具进行捕获和分析。
|
||||
|
||||
## Compile and Run
|
||||
|
||||
Compile:
|
||||
|
||||
```
|
||||
docker run -it -v `pwd`/:/src/ yunwei37/ebpm:latest
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```
|
||||
sudo ./ecli run examples/bpftools/runqslower/package.json
|
||||
```
|
||||
|
||||
## details in bcc
|
||||
|
||||
Demonstrations of runqslower, the Linux eBPF/bcc version.
|
||||
|
||||
runqslower traces high scheduling delays between tasks being ready to run and them running on CPU after that. Example output:
|
||||
|
||||
```
|
||||
# ./runqslower
|
||||
Tracing run queue latency higher than 10000 us
|
||||
TIME COMM TID LAT(us)
|
||||
13:11:43 b'kworker/0:2' 8680 10250
|
||||
13:12:18 b'irq/16-vmwgfx' 422 10838
|
||||
13:12:18 b'systemd-oomd' 753 11012
|
||||
13:12:18 b'containerd' 8272 11254
|
||||
13:12:18 b'HangDetector' 764 12042
|
||||
^C
|
||||
``
|
||||
This measures the time a task spends waiting on a run queue for a turn on-CPU, and shows this time as a individual events. This time should be small, but a task may need to wait its turn due to CPU load.
|
||||
|
||||
This measures two types of run queue latency:
|
||||
1. The time from a task being enqueued on a run queue to its context switch and execution. This traces ttwu_do_wakeup(), wake_up_new_task() -> finish_task_switch() with either raw tracepoints (if supported) or kprobes and instruments the run queue latency after a voluntary context switch.
|
||||
2. The time from when a task was involuntary context switched and still in the runnable state, to when it next executed. This is instrumented from finish_task_switch() alone.
|
||||
|
||||
The overhead of this tool may become significant for some workloads: see the OVERHEAD section.
|
||||
|
||||
This works by tracing various kernel scheduler functions using dynamic tracing, and will need updating to match any changes to these functions.
|
||||
|
||||
Since this uses BPF, only the root user can use this tool.
|
||||
|
||||
```console
|
||||
Usage: runqslower [-h] [-p PID | -t TID | -P] [min_us]
|
||||
```
|
||||
|
||||
The min_us option sets the latency of the run queue to track:
|
||||
|
||||
```
|
||||
# ./runqslower 100
|
||||
Tracing run queue latency higher than 100 us
|
||||
TIME COMM TID LAT(us)
|
||||
20:48:26 b'gnome-shell' 3005 201
|
||||
20:48:26 b'gnome-shell' 3005 202
|
||||
20:48:26 b'gnome-shell' 3005 254
|
||||
20:48:26 b'gnome-shell' 3005 208
|
||||
20:48:26 b'gnome-shell' 3005 132
|
||||
20:48:26 b'gnome-shell' 3005 213
|
||||
20:48:26 b'gnome-shell' 3005 205
|
||||
20:48:26 b'python3' 5224 127
|
||||
20:48:26 b'gnome-shell' 3005 214
|
||||
20:48:26 b'gnome-shell' 3005 126
|
||||
20:48:26 b'gnome-shell' 3005 285
|
||||
20:48:26 b'Xorg' 2869 296
|
||||
20:48:26 b'gnome-shell' 3005 119
|
||||
20:48:26 b'gnome-shell' 3005 206
|
||||
```
|
||||
|
||||
The -p PID option only traces this PID:
|
||||
|
||||
```
|
||||
# ./runqslower -p 3005
|
||||
Tracing run queue latency higher than 10000 us
|
||||
TIME COMM TID LAT(us)
|
||||
20:46:22 b'gnome-shell' 3005 16024
|
||||
20:46:45 b'gnome-shell' 3005 11494
|
||||
20:46:45 b'gnome-shell' 3005 21430
|
||||
20:46:45 b'gnome-shell' 3005 14948
|
||||
20:47:16 b'gnome-shell' 3005 10164
|
||||
20:47:16 b'gnome-shell' 3005 18070
|
||||
20:47:17 b'gnome-shell' 3005 13272
|
||||
20:47:18 b'gnome-shell' 3005 10451
|
||||
20:47:18 b'gnome-shell' 3005 15010
|
||||
20:47:18 b'gnome-shell' 3005 19449
|
||||
20:47:22 b'gnome-shell' 3005 19327
|
||||
20:47:23 b'gnome-shell' 3005 13178
|
||||
20:47:23 b'gnome-shell' 3005 13483
|
||||
20:47:23 b'gnome-shell' 3005 15562
|
||||
20:47:23 b'gnome-shell' 3005 13655
|
||||
20:47:23 b'gnome-shell' 3005 19571
|
||||
```
|
||||
|
||||
The -P option also shows previous task name and TID:
|
||||
|
||||
```
|
||||
# ./runqslower -P
|
||||
Tracing run queue latency higher than 10000 us
|
||||
TIME COMM TID LAT(us) PREV COMM PREV TID
|
||||
20:42:48 b'sysbench' 5159 10562 b'sysbench' 5152
|
||||
20:42:48 b'sysbench' 5159 10367 b'sysbench' 5152
|
||||
20:42:49 b'sysbench' 5158 11818 b'sysbench' 5159
|
||||
20:42:49 b'sysbench' 5160 16913 b'sysbench' 5153
|
||||
20:42:49 b'sysbench' 5157 13742 b'sysbench' 5160
|
||||
20:42:49 b'sysbench' 5152 13746 b'sysbench' 5160
|
||||
20:42:49 b'sysbench' 5153 13731 b'sysbench' 5160
|
||||
20:42:49 b'sysbench' 5158 14688 b'sysbench' 5161
|
||||
20:42:50 b'sysbench' 5155 10468 b'sysbench' 5152
|
||||
20:42:50 b'sysbench' 5156 17695 b'sysbench' 5158
|
||||
20:42:50 b'sysbench' 5155 11251 b'sysbench' 5152
|
||||
20:42:50 b'sysbench' 5154 13283 b'sysbench' 5152
|
||||
20:42:50 b'sysbench' 5158 22278 b'sysbench' 5157
|
||||
```
|
||||
|
||||
For more details, see docs/special_filtering.md
|
||||
@@ -1,112 +0,0 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
/* Copyright (c) 2021 Hengqi Chen */
|
||||
|
||||
#ifndef __CORE_FIXES_BPF_H
|
||||
#define __CORE_FIXES_BPF_H
|
||||
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
|
||||
/**
|
||||
* commit 2f064a59a1 ("sched: Change task_struct::state") changes
|
||||
* the name of task_struct::state to task_struct::__state
|
||||
* see:
|
||||
* https://github.com/torvalds/linux/commit/2f064a59a1
|
||||
*/
|
||||
struct task_struct___o {
|
||||
volatile long int state;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct task_struct___x {
|
||||
unsigned int __state;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
static __always_inline __s64 get_task_state(void *task)
|
||||
{
|
||||
struct task_struct___x *t = task;
|
||||
|
||||
if (bpf_core_field_exists(t->__state))
|
||||
return BPF_CORE_READ(t, __state);
|
||||
return BPF_CORE_READ((struct task_struct___o *)task, state);
|
||||
}
|
||||
|
||||
/**
|
||||
* commit 309dca309fc3 ("block: store a block_device pointer in struct bio")
|
||||
* adds a new member bi_bdev which is a pointer to struct block_device
|
||||
* see:
|
||||
* https://github.com/torvalds/linux/commit/309dca309fc3
|
||||
*/
|
||||
struct bio___o {
|
||||
struct gendisk *bi_disk;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct bio___x {
|
||||
struct block_device *bi_bdev;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
static __always_inline struct gendisk *get_gendisk(void *bio)
|
||||
{
|
||||
struct bio___x *b = bio;
|
||||
|
||||
if (bpf_core_field_exists(b->bi_bdev))
|
||||
return BPF_CORE_READ(b, bi_bdev, bd_disk);
|
||||
return BPF_CORE_READ((struct bio___o *)bio, bi_disk);
|
||||
}
|
||||
|
||||
/**
|
||||
* commit d5869fdc189f ("block: introduce block_rq_error tracepoint")
|
||||
* adds a new tracepoint block_rq_error and it shares the same arguments
|
||||
* with tracepoint block_rq_complete. As a result, the kernel BTF now has
|
||||
* a `struct trace_event_raw_block_rq_completion` instead of
|
||||
* `struct trace_event_raw_block_rq_complete`.
|
||||
* see:
|
||||
* https://github.com/torvalds/linux/commit/d5869fdc189f
|
||||
*/
|
||||
struct trace_event_raw_block_rq_complete___x {
|
||||
dev_t dev;
|
||||
sector_t sector;
|
||||
unsigned int nr_sector;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct trace_event_raw_block_rq_completion___x {
|
||||
dev_t dev;
|
||||
sector_t sector;
|
||||
unsigned int nr_sector;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
static __always_inline bool has_block_rq_completion()
|
||||
{
|
||||
if (bpf_core_type_exists(struct trace_event_raw_block_rq_completion___x))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* commit d152c682f03c ("block: add an explicit ->disk backpointer to the
|
||||
* request_queue") and commit f3fa33acca9f ("block: remove the ->rq_disk
|
||||
* field in struct request") make some changes to `struct request` and
|
||||
* `struct request_queue`. Now, to get the `struct gendisk *` field in a CO-RE
|
||||
* way, we need both `struct request` and `struct request_queue`.
|
||||
* see:
|
||||
* https://github.com/torvalds/linux/commit/d152c682f03c
|
||||
* https://github.com/torvalds/linux/commit/f3fa33acca9f
|
||||
*/
|
||||
struct request_queue___x {
|
||||
struct gendisk *disk;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
struct request___x {
|
||||
struct request_queue___x *q;
|
||||
struct gendisk *rq_disk;
|
||||
} __attribute__((preserve_access_index));
|
||||
|
||||
static __always_inline struct gendisk *get_disk(void *request)
|
||||
{
|
||||
struct request___x *r = request;
|
||||
|
||||
if (bpf_core_field_exists(r->rq_disk))
|
||||
return BPF_CORE_READ(r, rq_disk);
|
||||
return BPF_CORE_READ(r, q, disk);
|
||||
}
|
||||
|
||||
#endif /* __CORE_FIXES_BPF_H */
|
||||
@@ -1,117 +0,0 @@
|
||||
// SPDX-License-Identifier: GPL-2.0
|
||||
// Copyright (c) 2019 Facebook
|
||||
#include <vmlinux.h>
|
||||
#include <bpf/bpf_core_read.h>
|
||||
#include <bpf/bpf_helpers.h>
|
||||
#include <bpf/bpf_tracing.h>
|
||||
#include "runqslower.h"
|
||||
#include "core_fixes.bpf.h"
|
||||
|
||||
#define TASK_RUNNING 0
|
||||
|
||||
const volatile __u64 min_us = 0;
|
||||
const volatile pid_t targ_pid = 0;
|
||||
const volatile pid_t targ_tgid = 0;
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_HASH);
|
||||
__uint(max_entries, 10240);
|
||||
__type(key, u32);
|
||||
__type(value, u64);
|
||||
} start SEC(".maps");
|
||||
|
||||
struct {
|
||||
__uint(type, BPF_MAP_TYPE_PERF_EVENT_ARRAY);
|
||||
__uint(key_size, sizeof(u32));
|
||||
__uint(value_size, sizeof(u32));
|
||||
} events SEC(".maps");
|
||||
|
||||
/* record enqueue timestamp */
|
||||
static int trace_enqueue(u32 tgid, u32 pid)
|
||||
{
|
||||
u64 ts;
|
||||
|
||||
if (!pid)
|
||||
return 0;
|
||||
if (targ_tgid && targ_tgid != tgid)
|
||||
return 0;
|
||||
if (targ_pid && targ_pid != pid)
|
||||
return 0;
|
||||
|
||||
ts = bpf_ktime_get_ns();
|
||||
bpf_map_update_elem(&start, &pid, &ts, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int handle_switch(void *ctx, struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
struct event event = {};
|
||||
u64 *tsp, delta_us;
|
||||
u32 pid;
|
||||
|
||||
/* ivcsw: treat like an enqueue event and store timestamp */
|
||||
if (get_task_state(prev) == TASK_RUNNING)
|
||||
trace_enqueue(BPF_CORE_READ(prev, tgid), BPF_CORE_READ(prev, pid));
|
||||
|
||||
pid = BPF_CORE_READ(next, pid);
|
||||
|
||||
/* fetch timestamp and calculate delta */
|
||||
tsp = bpf_map_lookup_elem(&start, &pid);
|
||||
if (!tsp)
|
||||
return 0; /* missed enqueue */
|
||||
|
||||
delta_us = (bpf_ktime_get_ns() - *tsp) / 1000;
|
||||
if (min_us && delta_us <= min_us)
|
||||
return 0;
|
||||
|
||||
event.pid = pid;
|
||||
event.prev_pid = BPF_CORE_READ(prev, pid);
|
||||
event.delta_us = delta_us;
|
||||
bpf_probe_read_kernel_str(&event.task, sizeof(event.task), next->comm);
|
||||
bpf_probe_read_kernel_str(&event.prev_task, sizeof(event.prev_task), prev->comm);
|
||||
|
||||
/* output */
|
||||
bpf_perf_event_output(ctx, &events, BPF_F_CURRENT_CPU,
|
||||
&event, sizeof(event));
|
||||
|
||||
bpf_map_delete_elem(&start, &pid);
|
||||
return 0;
|
||||
}
|
||||
|
||||
SEC("tp_btf/sched_wakeup")
|
||||
int BPF_PROG(sched_wakeup, struct task_struct *p)
|
||||
{
|
||||
return trace_enqueue(p->tgid, p->pid);
|
||||
}
|
||||
|
||||
SEC("tp_btf/sched_wakeup_new")
|
||||
int BPF_PROG(sched_wakeup_new, struct task_struct *p)
|
||||
{
|
||||
return trace_enqueue(p->tgid, p->pid);
|
||||
}
|
||||
|
||||
SEC("tp_btf/sched_switch")
|
||||
int BPF_PROG(sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
return handle_switch(ctx, prev, next);
|
||||
}
|
||||
|
||||
SEC("raw_tp/sched_wakeup")
|
||||
int BPF_PROG(handle_sched_wakeup, struct task_struct *p)
|
||||
{
|
||||
return trace_enqueue(BPF_CORE_READ(p, tgid), BPF_CORE_READ(p, pid));
|
||||
}
|
||||
|
||||
SEC("raw_tp/sched_wakeup_new")
|
||||
int BPF_PROG(handle_sched_wakeup_new, struct task_struct *p)
|
||||
{
|
||||
return trace_enqueue(BPF_CORE_READ(p, tgid), BPF_CORE_READ(p, pid));
|
||||
}
|
||||
|
||||
SEC("raw_tp/sched_switch")
|
||||
int BPF_PROG(handle_sched_switch, bool preempt, struct task_struct *prev, struct task_struct *next)
|
||||
{
|
||||
return handle_switch(ctx, prev, next);
|
||||
}
|
||||
|
||||
char LICENSE[] SEC("license") = "GPL";
|
||||
@@ -1,15 +0,0 @@
|
||||
/* SPDX-License-Identifier: (LGPL-2.1 OR BSD-2-Clause) */
|
||||
#ifndef __RUNQSLOWER_H
|
||||
#define __RUNQSLOWER_H
|
||||
|
||||
#define TASK_COMM_LEN 16
|
||||
|
||||
struct event {
|
||||
char task[TASK_COMM_LEN];
|
||||
char prev_task[TASK_COMM_LEN];
|
||||
__u64 delta_us;
|
||||
int pid;
|
||||
int prev_pid;
|
||||
};
|
||||
|
||||
#endif /* __RUNQSLOWER_H */
|
||||
28
README.md
28
README.md
@@ -1,4 +1,4 @@
|
||||
# bpf-develop-tutorial: learn CO-RE ebpf with example tools
|
||||
# bpf-develop-tutorial: learn CO-RE ebpf with example tools
|
||||
|
||||
这是一个基于 `CO-RE`(一次编译,到处运行)的 `libbpf` 的 eBPF 的开发教程,提供了从入门到进阶的 eBPF 开发实践指南,包括基本概念、代码实例、实际应用等内容。我们主要提供了一些 eBPF 工具的案例,帮助开发者学习 eBPF 的开发方法和技巧。教程内容可以在目录中找到,每个目录都是一个独立的 eBPF 工具案例。
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
|
||||
本项目主要基于 [libbpf-boostrap](https://github.com/libbpf/libbpf-bootstrap) 和 [eunomia-bpf](https://github.com/eunomia-bpf/eunomia-bpf) 两个框架完成,并使用 eunomia-bpf 帮助简化一部分 libbpf eBPF 用户态代码的编写。
|
||||
|
||||
教程主要关注于可观察性,并简要介绍了 eBPF 的其他应用。
|
||||
|
||||
教程主要关注于可观察性,并简要介绍了 eBPF 的其他应用,例如网络、安全等等。
|
||||
|
||||
## 让 chatGPT 来帮助我们
|
||||
|
||||
@@ -21,7 +20,7 @@
|
||||
|
||||

|
||||
|
||||
完整的对话可以在这里找到: [chatGPT.md](chatGPT.md)
|
||||
完整的对话记录可以在这里找到: [chatGPT.md](chatGPT.md)
|
||||
|
||||
## 目录
|
||||
|
||||
@@ -32,8 +31,8 @@
|
||||
- [lesson 4-opensnoop](4-opensnoop/README.md) 使用 eBPF 捕获进程打开文件的系统调用集合,使用全局变量在 eBPF 中过滤进程 pid
|
||||
- [lesson 5-uprobe-bashreadline](5-uprobe-bashreadline/README.md) 在 eBPF 中使用 uprobe 捕获 bash 的 readline 函数调用
|
||||
- [lesson 6-sigsnoop](6-sigsnoop/README.md) 捕获进程发送信号的系统调用集合,使用 hash map 保存状态
|
||||
- [lesson 7-execsnoop](7-execsnoop/README.md) 捕获进程执行/退出时间,通过 perf event array 向用户态打印输出
|
||||
- [lesson 8-runqslower](8-runqslower/README.md) 捕获进程调度事件,使用 ring buffer 向用户态打印输出
|
||||
- [lesson 7-execsnoop](7-execsnoop/README.md) 捕获进程执行时间,通过 perf event array 向用户态打印输出
|
||||
- [lesson 8-execsnoop](8-exitsnoop/README.md) 捕获进程退出事件,使用 ring buffer 向用户态打印输出
|
||||
- [lesson 9-runqlat](9-runqlat/README.md) 捕获进程调度延迟,以直方图方式记录
|
||||
- [lesson 10-hardirqs](20-hardirqs/README.md) 使用 hardirqs 或 softirqs 捕获中断事件
|
||||
- [lesson 11-bootstrap](11-bootstrap/README.md) 使用 libbpf-boostrap 为 eBPF 编写原生的的用户态代码
|
||||
@@ -51,10 +50,11 @@
|
||||
## 为什么需要基于 libbpf 和 BPF CO-RE 的教程?
|
||||
|
||||
> 历史上,当需要开发一个BPF应用时可以选择BCC 框架,在实现各种用于Tracepoints的BPF程序时需要将BPF程序加载到内核中。BCC提供了内置的Clang编译器,可以在运行时编译BPF代码,并将其定制为符合特定主机内核的程序。这是在不断变化的内核内部下开发可维护的BPF应用程序的唯一方法。在BPF的可移植性和CO-RE一文中详细介绍了为什么会这样,以及为什么BCC是之前唯一的可行方式,此外还解释了为什么 libbpf 是目前比较好的选择。去年,Libbpf的功能和复杂性得到了重大提升,消除了与BCC之间的很多差异(特别是对Tracepoints应用来说),并增加了很多BCC不支持的新的且强大的特性(如全局变量和BPF skeletons)。
|
||||
>
|
||||
>
|
||||
> 诚然,BCC会竭尽全力简化BPF开发人员的工作,但有时在获取便利性的同时也增加了问题定位和修复的困难度。用户必须记住其命名规范以及自动生成的用于Tracepoints的结构体,且必须依赖这些代码的重写来读取内核数据和获取kprobe参数。当使用BPF map时,需要编写一个半面向对象的C代码,这与内核中发生的情况并不完全匹配。除此之外,BCC使得用户在用户空间编写了大量样板代码,且需要手动配置最琐碎的部分。
|
||||
>
|
||||
>
|
||||
> 如上所述,BCC依赖运行时编译,且本身嵌入了庞大的LLVM/Clang库,由于这些原因,BCC与理想的使用有一定差距:
|
||||
>
|
||||
> - 编译时的高资源利用率(内存和CPU),在繁忙的服务器上时有可能干扰主流程。
|
||||
> - 依赖内核头文件包,不得不在每台目标主机上进行安装。即使这样,如果需要某些没有通过公共头文件暴露的内核内容时,需要将类型定义拷贝黏贴到BPF代码中,通过这种方式达成目的。
|
||||
> - 即使是很小的编译时错误也只能在运行时被检测到,之后不得不重新编译并重启用户层的应用;这大大影响了开发的迭代时间(并增加了挫败感...)
|
||||
@@ -79,15 +79,3 @@ eunomia-bpf 由一个编译工具链和一个运行时库组成, 对比传统的
|
||||
|
||||
> - eunomia-bpf 项目 Github 地址: <https://github.com/eunomia-bpf/eunomia-bpf>
|
||||
> - gitee 镜像: <https://gitee.com/anolis/eunomia>
|
||||
|
||||
## 让 chatGPT 来帮助我们
|
||||
|
||||
> 本教程大部分内容由 chatGPT 生成,我们尝试教会 chatGPT 编写 eBPF 程序:
|
||||
>
|
||||
> 1. 告诉它基本的 eBPF 编程相关的常识
|
||||
> 2. 一些案例:hello world,eBPF 程序的基本结构,如何使用 eBPF 程序等等,并且让它开始编写教程
|
||||
> 3. 手动调整并纠正代码和文档中的错误
|
||||
> 4. 把修改后的代码再喂给 chatGPT,让它继续学习
|
||||
> 5. 尝试让 chatGPT 自动生成 eBPF 程序!
|
||||
>
|
||||
|
||||
|
||||
1333
chatGPT.md
1333
chatGPT.md
File diff suppressed because it is too large
Load Diff
1080
chatGPT1.md
1080
chatGPT1.md
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user