# bpf_practice **Repository Path**: yangrudan/bpf_practice ## Basic Information - **Project Name**: bpf_practice - **Description**: 练习学习bpf - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2024-03-18 - **Last Updated**: 2024-04-18 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # bcc 开发练习 # [追踪sync](./sys_sync_hello_word.py) 1. bpf_txt:BPF C 程序,包含一个函数hello_world_printk。 2. void *ctx: ctx有参数(如struct pt_regs *ctx),但由于我们在本例中没有使用它们,所以我们可以将其转换为void * 3. bpf_trace_printk():一个简单的内核工具,用于printf()到公共trace_pipe ( /sys/kernel/debug/tracing/trace_pipe )。 4. bpf_ctx = BPF(text=bpf_txt):这本质上加载 BPF C 程序并通过编译器、验证器等运行它,并返回我们用来启用事件的 BPF 对象。 5. Attach_kprobe(event='...', fn_name='...') :使用函数入口的内核动态跟踪来检测由event的值指定的内核函数,并附加我们的 C 定义的函数(由fn_name的值,在执行命中该事件时调用。 6. trace_print(fmt='...'):一个 BCC 例程,读取trace_pipe并以指定格式打印输出(可选)(与bpf_trace_printk一起使用)。 _`默认情况下(即仅trace_print()),从trace_pipe读取的每个数据都带有以下字段: {1}:进程 ID (PID) {2}:CPU# {3}:标志 {4}:Unix 时间戳 {5}、{6}、{7}:可选字段(如果使用这些字段)`_ 在我们的例子中,我们只想打印消息“Hello World!”,因此我们只指定第 5 个 ( {5} ) 字段,其中包含我们的消息 跟踪打印 * while 1:我们在这里使用无限 while 循环(就像大多数 BPF 程序一样),这样我们就可以连续轮询trace_pipe(或任何其他输出流)并检查另一端是否有数据要读取。 使用try:和except:进行更安全的处理 # [BPF_PERF_OUTPUT sync_hello_world](./sync_hello_pref_output.py) 1. BPF_PERF_OUTPUT(events):创建一个 BPF 表,用于通过 perf 环形缓冲区将自定义事件数据推送到用户空间。 * 将每个事件数据推送到用户空间的首选方法 * 在我们的示例中,输出表名为events,自定义事件数据(在struct data_t data中)通过以下方式推送到用户空间。events.perf_submit() * BPF_PERF_输出 * struct data_t:定义自定义 C 数据结构以将数据(通过 perf 的环形缓冲区)从内核传递到用户空间.在我们的示例中,我们发送的唯一数据是“Hello World!” string,所以我们定义一个长度>=字符串实际长度的char数组 2. __builtin_memcpy() : memcpy()的内置版本。 回想一下,BPF C 代码中不允许有循环,因此不允许memcpy() 3. events.perf_submit() :使用BPF_PERF_OUTPUT表将自定义事件数据提交到用户空间的方法。 4. open_perf_buffer bpf_ctx["events"].open_perf_buffer(handle_event):将 Python handle_event函数与BPF_PERF_OUTPUT(events)输出流关联。 5. perf_buffer_poll():轮询任何打开的性能缓冲区以获取新数据。 6. handle_event(cpu, data, size):处理从事件流中读取事件的Python函数。 * 函数参数cpu、data、size是必需的参数 * bpf_ctx["events"].event(data)将事件数据作为 Python 对象抓取,该对象是从 C 数据结构struct data_t自动生成的 * Python事件对象的字段与BPF C程序中定义的完全相同(例如output.str) # [追踪sync-fields](./hello-fields.py) (task, pid, cpu, flags, ts, msg) = b.trace_fields() ``` TIME(s) COMM PID MESSAGE 19701.696061000 postgres 880 Hello, World! 19739.556299000 systemd-logind 680 Hello, World! 19739.557980000 systemd-udevd 325 Hello, World! 19745.895216000 systemd-logind 680 Hello, World! 19745.896988000 systemd-udevd 325 Hello, World! ``` # [bite_hist](./bite_hist.py) 直方图编译BPF出错 # [sync_timing fields](./sync_timing.py) 控制台sync;sync;sync **触发** 1. bpf_ktime_get_ns():返回以纳秒为单位的时间。 2. BPF_HASH(last):创建一个BPF映射对象,它是一个散列(关联数组),称为“last”。我们没有指定任何进一步的参数,因此它默认为 u64 的键和值类型。 3. key = 0:我们只会在此哈希中存储一个键/值对,其中键被硬连接为零。 4. last.lookup(&key):在哈希中查找键,如果存在则返回指向其值的指针,否则返回NULL。我们将密钥作为地址传递给指针。 5. if (tsp != NULL) {:验证程序要求必须检查从映射查找派生的指针值是否为空值,然后才能取消引用和使用它们。 6. last.delete(&key):从哈希中删除密钥。由于4.8.10中的内核错误.update()(已在 4.8.10 中修复),目前需要这样做。 7. last.update(&key, &ts):将第二个参数中的值与键关联,覆盖任何先前的值。这记录了时间戳。 # [sync_count](./sync_count.py) 不能使用*curct++,可能是因为内核数据的原因,只能通过count.update更新; # [disksnoop](./disksnoop.py) TODO # [execve_data](./execve_data.py) TODO # [内核动态追踪](./do_sys_open.py)