第一个程序是使用kprobe
挂钩到sys_sync
系统调用。第一次实现时,先用bpf_trace_printk
把跟踪结果输出到用户态。
bpf_trace_printk
这个程序作用是:当用户每次执行sync
命令,它就会打印出hello world
。
#!/usr/bin/pythonfrom bcc import BPF
import
# Define BPF program
bpf_txt = """int hello_world_printk(void *ctx) {
bpf_trace_printk("Hello World!\\n");
return 0;
}
"""
# Load BPF program
bpf_ctx = BPF(text=bpf_txt)
bpf_ctx.attach_kprobe(event=bpf_ctx.get_syscall_fnname("sync"),
fn_name="hello_world_printk")
# Print header
print("%s" % "MESSAGE")
# Format output
while 1:
try:
bpf_ctx.trace_print(fmt="{5}")
except KeyboardInterrupt:
print()
sys.exit(1)
这个程序分为两部分:BPF
的C
语言片段和python
片段。
bpf_txt
: BPF
片段, 只包括着一个函数hello_world_printk
。它的作用如下
每次系统调用 sys_sync
命中,bpf_trace_printk
会打印"hello world"到/sys/kernel/debug/tracing/trace_pipe
每个在 BPF
片段要在probe
或跟踪点执行的C
函数的参数都要包括pt_regs *ctx
(在这个例子不需要用上,直接用void* ctx
)。返回值一定要int
类型
bpf_ctx = BPF(text=bpf_txt)
: 加载BPF
片段,并且编译,校验和在内核运行它,再返回一个BPF
对象用于获取事件。
attach_kprobe(event='...', fn_name='...')
:指定BPF
片段里函数(fn_name
指定)挂钩到哪些跟踪点(event
指定)
bpf_ctx.trace_print(fmt="{5}")
:从/sys/kernel/debug/debug/tracing/trace_pipe
读取BPF
片段的结果,每条记录都会包括这些字段:
unix
时间戳BPF_PERF_OUTPUT
由于trace_pipe
是通用的,所以,从它出来的结果可能会和其它BPF
程序结果交叠。所以改用BPF_PERF_OUTPUT
来获得单独通道获取BPF
的结果。
#!/usr/bin/pythonfrom bcc import BPF
import sys
# Define BPF program
bpf_txt = """#define MY_STR_LEN 12
BPF_PERF_OUTPUT(events);
struct data_t {
char str[MY_STR_LEN];
};
int hello_world_perf(struct pt_regs *ctx) {
struct data_t data = {};
__builtin_memcpy(&data.str, "Hello World!", sizeof(data.str));
events.perf_submit(ctx, &data, sizeof(data));
return 0;
}
"""
def handle_event(cpu, data, size):
output = bpf_ctx["events"].event(data)
print("%s" % output.str)
# Load BPF program
bpf_ctx = BPF(text=bpf_txt)
bpf_ctx.attach_kprobe(event=bpf_ctx.get_syscall_fnname("sync"),
fn_name="hello_world_perf")
# Print header
print("%s" % "MESSAGE")
# Open perf "events" buffer, loop with callback to handle_event
bpf_ctx["events"].open_perf_buffer(handle_event)
# Format output
while 1:
try:
bpf_ctx.perf_buffer_poll()
except KeyboardInterrupt:
print()
sys.exit(1)
和第一次的实现有些不一样:
BPF_PERF_OUTPUT
创建一个events
表,允许BPF
片段通过环形缓存向用户态推送结果struct data_t
定义推送结果的数据结构__builtin_memcpy
用于初始化单个数据events.perf_submit
往events
推送结果bpf_ctx["events"].open_perf_buffer(handle_event)
把环形缓存的数据流和python
处理函数handle_event
关联起来perf_buffer_poll
执行从环形缓存抽取结果的动作handle_event
必须要有cpu
,data
,size
三个参数bpf_ctx["events"].event(data)
从events
获取结果。这个events
一定要在BPF
有定义,这个执行的返回结果一定是在events
里的数据格式,在这里的是data_t
暗号:05c7c