Linux ebpf跟踪技术4:第一个程序
2023-3-21 14:21:16 Author: 奶牛安全(查看原文) 阅读量:9 收藏

Linux ebpf跟踪技术1:Linux跟踪技术纵览
Linux ebpf跟踪技术2:BCC介绍
Linux ebpf跟踪技术3:BCC工具开发提示

第一个程序是使用kprobe挂钩到sys_sync系统调用。第一次实现时,先用bpf_trace_printk把跟踪结果输出到用户态。

使用bpf_trace_printk

这个程序作用是:当用户每次执行sync命令,它就会打印出hello world

#!/usr/bin/python

from 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)

这个程序分为两部分:BPFC语言片段和python片段。

bpf_txt: BPF片段, 只包括着一个函数hello_world_printk。它的作用如下

  1. 每次系统调用sys_sync命中,bpf_trace_printk会打印"hello world"到/sys/kernel/debug/tracing/trace_pipe
  2. 每个在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片段的结果,每条记录都会包括这些字段:

  1. {1}: 进程ID
  2. {2}: 执行的CPU核
  3. {3}: 标志
  4. {4}: unix时间戳
  5. {5},{6},{7}:自定义字段,这里只用了{5},是"hello world"

使用BPF_PERF_OUTPUT

由于trace_pipe是通用的,所以,从它出来的结果可能会和其它BPF程序结果交叠。所以改用BPF_PERF_OUTPUT来获得单独通道获取BPF的结果。

#!/usr/bin/python

from 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_submitevents推送结果
  • 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


文章来源: http://mp.weixin.qq.com/s?__biz=MzU4NjY0NTExNA==&mid=2247488559&idx=1&sn=2fcba659e766b505bb39168a2723fcac&chksm=fdf97f3aca8ef62c51193d2706475465df25d116a70a45b78402d7f715525ea205032f8ae731#rd
如有侵权请联系:admin#unsafe.sh