【ebpf】first try

03 | 初窥门径:开发并运行你的第一个 eBPF 程序

环境

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[root@ningan ~]# uname -a
Linux ningan 5.13.0-51-generic #58~20.04.1-Ubuntu SMP Tue Jun 14 11:29:12 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

[root@ningan ebpf]# sudo apt-get install -y make clang llvm libelf-dev libbpf-dev bpfcc-tools libbpfcc-dev linux-tools-$(uname -r) linux-headers-$(uname -r)
Reading package lists... Done
Building dependency tree
Reading state information... Done
linux-headers-5.8.0-25-generic is already the newest version (5.8.0-25.26).
linux-tools-5.8.0-25-generic is already the newest version (5.8.0-25.26).
make is already the newest version (4.3-4ubuntu1).
bpfcc-tools is already the newest version (0.12.0-2).
clang is already the newest version (1:11.0-51~exp1).
libbpf-dev is already the newest version (1:0.1.0-1).
libbpfcc-dev is already the newest version (0.12.0-2).
llvm is already the newest version (1:11.0-51~exp1).
libelf-dev is already the newest version (0.181-1ubuntu0.1).
0 upgraded, 0 newly installed, 0 to remove and 147 not upgraded.

image-20220726130910879

操作流程

开发第一个 eBPF 程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
[root@ningan ebpf]# cat hello.c

int hello_world(void *ctx)
{
bpf_trace_printk("Hello, World!");
return 0;
}




[root@ningan ebpf]# cat hello.py

#!/usr/bin/env python3
# 1) import bcc library
# 导入了 BCC 库的 BPF 模块,以便接下来调用;
from bcc import BPF

# 2) load BPF program
# 调用 BPF() 加载第一步开发的 BPF 源代码;
b = BPF(src_file="hello.c")
# 3) attach kprobe
# 将 BPF 程序挂载到内核探针(简称 kprobe),其中 do_sys_openat2() 是系统调用 openat() 在内核中的实现;
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")
# 4) read and print /sys/kernel/debug/tracing/trace_pipe
# 读取内核调试文件 /sys/kernel/debug/tracing/trace_pipe 的内容,并打印到标准输出中。
b.trace_print()

image-20220726132704120

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
[root@ningan ebpf]# cat /sys/kernel/debug/tracing/trace_options
print-parent
nosym-offset
nosym-addr
noverbose
noraw
nohex
nobin
noblock
trace_printk
annotate
nouserstacktrace
nosym-userobj
noprintk-msg-only
context-info
nolatency-format
record-cmd
norecord-tgid
overwrite
nodisable_on_free
irq-info
markers
noevent-fork
nopause-on-trace
function-trace
nofunction-fork
nodisplay-graph
nostacktrace
notest_nop_accept
notest_nop_refuse

改进第一个 eBPF 程序

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
[root@ningan ebpf]# cat trace-open.c

// 包含头文件
#include <uapi/linux/openat2.h>
#include <linux/sched.h>

// 定义数据结构
struct data_t {
u32 pid;
u64 ts;
char comm[TASK_COMM_LEN];
char fname[NAME_MAX];
};

// 定义性能事件映射
BPF_PERF_OUTPUT(events);



// 定义kprobe处理函数
int hello_world(struct pt_regs *ctx, int dfd, const char __user * filename, struct open_how *how)
{
struct data_t data = { };

// 获取PID和时间
data.pid = bpf_get_current_pid_tgid();
data.ts = bpf_ktime_get_ns();

// 获取进程名
if (bpf_get_current_comm(&data.comm, sizeof(data.comm)) == 0)
{
bpf_probe_read(&data.fname, sizeof(data.fname), (void *)filename);
}

// 提交性能事件
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}






[root@ningan ebpf]# cat trace-open.py

from bcc import BPF

# 1) load BPF program
# 加载 eBPF 程序并挂载到内核探针上
b = BPF(src_file="trace-open.c")
b.attach_kprobe(event="do_sys_openat2", fn_name="hello_world")

# 2) print header
# 输出一行 Header 字符串表示数据的格式
print("%-18s %-16s %-6s %-16s" % ("TIME(s)", "COMM", "PID", "FILE"))

# 3) define the callback for perf event
# print_event 定义一个数据处理的回调函数,打印进程的名字、PID 以及它调用 openat 时打开的文件
start = 0
def print_event(cpu, data, size):
global start
event = b["events"].event(data)
if start == 0:
start = event.ts
time_s = (float(event.ts - start)) / 1000000000
print("%-18.9f %-16s %-6d %-16s" % (time_s, event.comm, event.pid, event.fname))

# 4) loop with callback to print_event
# open_perf_buffer 定义了名为 “events” 的 Perf 事件映射,而后通过一个循环调用 perf_buffer_poll 读取映射的内容,并执行回调函数输出进程信息
b["events"].open_perf_buffer(print_event)
while 1:
try:
b.perf_buffer_poll()
except KeyboardInterrupt:
exit()

image-20220726133833600

大功告成!


【ebpf】first try
http://example.com/2022/07/25/ebpf/【ebpf】first try/
作者
ningan123
发布于
2022年7月25日
许可协议