08|内核跟踪(下):开发内核跟踪程序的进阶方法
BCC 方法
- 深入了解
- 翻译
- 解释
- 总结
本文介绍了使用BCC和libbpf两种进阶方法来开发内核跟踪程序。在使用BCC方法时,作者详细讲解了数据结构的定义,包括哈希映射和性能事件映射的创建,以及入口跟踪点处理的步骤,包括获取进程PID和名称,处理命令行参数的方法。通过代码示例和详细注释,读者能够清晰地了解每一步的操作和原理。文章还介绍了使用Python前端处理eBPF程序的步骤,并提到了BCC在加载字节码时会自动挂载跟踪点的特性。另外,文章还提到了在生产环境中使用BCC的难题,并探讨了使用libbpf进行eBPF程序开发的方法。作者详细介绍了使用libbpf开发eBPF程序的四个步骤,并给出了一个简化版本的Makefile示例。整体而言,本文内容详实,适合对eBPF有一定了解并希望深入学习的读者阅读。文章还总结了bpftrace、BCC和libbpf这三种方法的使用场景和特点,为读者提供了全面的eBPF程序开发方法选择建议。
《eBPF 核心技术与实战》,新⼈⾸单¥59
全部留言(24)
- 最新
- 精选
- 莫名1、对于参数问题,当前 BPF 程序尝试把所有参数一次性放入,受限于栈最大长度 512,很容易出现被截断现象。以 BCC 程序为例的解决方法:遍历参数列表 argv 时,每个参数读取之后直接调用 perf_submit 提交至 ringbuf,而不是读取所有参数后仅提交一次,最后用户态程序负责把这些字符串拼接起来。这样可以做到参数最大个数不受限制,且每个参数长度可接近栈最大长度 512(当前 BPF 程序限制 64 容易被截断)。 另一个解决方式应该可以采用 perf-cpu array 映射类型,避免占用有限的栈空间,具体没尝试过。 2、以 BCC 程序为例获取父进程,struct data_t 增加 ppid 字段,然后由 task->real_parent->tgid 赋值。 struct task_struct *task; task = (struct task_struct *)bpf_get_current_task(); data.ppid = task->real_parent->tgid;
作者回复: 非常棒的答案,赞一个👍
2022-02-02311 - piboyegolang + libbpf 是不是比BCC 方案更好?
作者回复: 从最终部署的角度来看是的,但对入门者来说BCC更简单一些
2022-03-1541 - 不了峰请教一下文章中关于 「libbpf 方法」 为什么 文章中没有提及 execsnoop.h 这个文件。。 而 execsnoop.bpf.c 要 #include "execsnoop.h" 这个文件 ? 关于 execsnoop.h 文件内容生成,是不是在前面的几章有涉及?
作者回复: 这个文件的内容不是自动生成的,跟C语言一样,头文件用来放类型定义和常量定义。 文件的源码见 https://github.com/feiskyer/ebpf-apps/blob/main/bpf-apps/execsnoop.h
2022-02-211 - 写点啥呢请教下老师: 1. bpf的辅助函数执行上下文是在当前进程下么,因为我看像execsnoop例子中get_current_pid_tgid调用能拿到的是新启动进程pid。 2. libbpf的开发模式能看到从编译器到bpf在背后做了很多工作,如果想了解bpf程序的实现,比如宏是如何定义了映射和挂载点,程序加载的时候如何从程序段中的信息实现的映射创建和挂载,老师能否给指一个学习路径?谢谢啦
作者回复: 1. eBPF程序是在内核空间执行,可以在内核空间获得进程的上下文信息。 2. 这些具体的步骤可以参考 https://github.com/libbpf/libbpf。比如,https://github.com/libbpf/libbpf/blob/master/src/libbpf.c#L8599-L8675 这儿定义了所有不同程序类型所对应的 SEC 名称及挂载方式。
2022-02-0331 - 龍蝦最新的代码 https://github.com/feiskyer/ebpf-apps/tree/b89ae0f 执行 make 出错,这是 gcc-toolset 导致吗?老师,各位同 学是否遇到过? ``` [root@Rocky-9 bpf-apps]# make clang -g -O2 -target bpf -D__TARGET_ARCH_x86 -Ilibbpf/usr/include -I../libbpf/include/uapi -I/usr/include/x86_64-linux-gnu -I. -c hello.bpf.c -o hello.bpf.o /usr/sbin/bpftool gen skeleton hello.bpf.o > hello.skel.h clang -g -O2 -Wall -Ilibbpf/usr/include -I../libbpf/include/uapi -I/usr/include/x86_64-linux-gnu -I. -c hello.c -o hello.o clang -Wall -O2 -g hello.o -static /root/ebpf-apps/bpf-apps/libbpf/libbpf.a -lelf -lz -o hello /usr/bin/ld: cannot find -lelf /usr/bin/ld: cannot find -lz /usr/bin/ld: cannot find -lc clang-14: error: linker command failed with exit code 1 (use -v to see invocation) make: *** [Makefile:14: hello] Error 1 [root@Rocky-9 bpf-apps]# [root@Rocky-9 bpf-apps]# clang -v clang version 14.0.6 (Red Hat 14.0.6-4.el9_1) Target: x86_64-redhat-linux-gnu Thread model: posix InstalledDir: /usr/bin Found candidate GCC installation: /opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/12 Selected GCC installation: /opt/rh/gcc-toolset-12/root/usr/lib/gcc/x86_64-redhat-linux/12 Candidate multilib: .;@m64 Candidate multilib: 32;@m32 Selected multilib: .;@m64 ```
作者回复: 这是依赖库没有安装导致的,请参考README的步骤安装依赖库
2023-02-21归属地:江苏2 - 从远方过来老师,我运行下面的代码,在另外一个shell上面执行chmod命令,但是bcc却没有输出,请问这个是什么原因? 内核版本: 4.14.15-1.el7.elrepo.x86_64 操作系统: CentOS Linux release 7.9.2009 (Core) bcc版本:bcc-0.21.0-1.el7.x86_64 bcc-tools-0.21.0-1.el7.x86_64 python-bcc-0.21.0-1.el7.noarch #!/usr/bin/env python3 # Tracing execve() system call. from bcc import BPF from bcc.utils import printb chmod_prog = """ /* Tracing execve system call. */ #include <uapi/linux/ptrace.h> #include <linux/sched.h> #include <linux/fs.h> // perf event map (sharing data to userspace) and hash map (sharing data between tracepoints) struct data_t { u32 pid; umode_t mode; char * filename; }; BPF_PERF_OUTPUT(events); // 生成一个event事件 // BPF_HASH(tasks, u32, struct data_t); // sys_enter_chmod tracepoint. TRACEPOINT_PROBE(syscalls, sys_enter_chmod) { struct data_t data = {}; u32 pid = bpf_get_current_pid_tgid(); umode_t mode = args->mode; char * filename = (char *)args->filename; data.pid = pid ; data.mode = mode; data.filename = filename; events.perf_submit(args, &data, sizeof(data)); return 0; } """ # 1) load BPF program b = BPF(text=chmod_prog) # b = BPF(src_file="chmod.c") # 2) print header print("%-6s %-16s %-3s %s" % ("PID", "COMM", "RET", "ARGS")) # 3) define the callback for perf event def print_event(cpu, data, size): # event data struct is generated from "struct data_t" by bcc event = b["events"].event(data) printb(b"%-6d %-16s %-3d" % (event.pid, event.mode, event.filename)) # 4) loop with callback to print_event b["events"].open_perf_buffer(print_event) while 1: try: b.perf_buffer_poll() except KeyboardInterrupt: exit()
作者回复: 有时候命令名跟syscall名字不是一致的,比如chmod命令很可能使用的是fchmodat而不是chmod系统调用
2022-03-172 - heyhd9475老师你好,我想请问为什么每次使用bpf_probe_read_user_str()读取后不在每次读取到的参数中间加上一个空格呢,我这边尝试加这个空格,为什么会提示如下的无效无限制可变偏移量栈写入呢: invalid unbounded variable-offset write to stack R2 报错的源码为: data.args[data.next_arg_index-1]=' '; 是因为bcc认为参数data.next_arg_index是没有限制范围的,不安全的吗。
作者回复: 看起来是验证器认为它是一个无界的变量,你的内核版本和BCC版本是什么?
2022-02-19 - hjydxy老师好: 在这个 execsnoop.skel.h 文件中,有一个“execsnoop_bpf__load”函数,你在说明中也说了这个函数是加载ebpf程序用的,我想请教下这个函数和/samples/bpf/下例子中使用的“bpf_object__load”有什么区别和联系,我的理解是这两个函数的作用差不多,都是加载ebpf程序用的,既然如此,使用“execsnoop_bpf__load”的意义或者好处在哪里?谢谢。
作者回复: 这是一个封装后的函数,使用起来更简单(当然直接用bpf_object__load也是没问题的)。
2022-02-16 - c1请教: Ubuntu 21.10 Linux u21 5.13.0-28-generic #31-Ubuntu SMP Thu Jan 13 17:41:06 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux 执行execsnoop.py报错: ... /virtual/main.c:47:42: error: incomplete definition of type 'struct tracepoint__syscalls__sys_enter_execve' const char **argv = (const char **)(args->argv); ... /virtual/main.c:82:22: error: incomplete definition of type 'struct tracepoint__syscalls__sys_exit_execve' data->retval = args->ret; ...
作者回复: 有没有引入头文件?完整的程序可以参考Github: https://github.com/feiskyer/ebpf-apps/blob/main/bcc-apps/python/execsnoop.c#L2-L4。
2022-02-092 - Haric文章提到,使用libbfp方式在开启了 BTF 的其他机器都可以运行,请问在嵌入式设备(需要交叉编译)能运行吗?
作者回复: 如果内核也支持BTF的话,理论上应该是可以运行的;但如果内核版本比较旧的话,可能不支持BTF,这时候可以使用条件编译的方式,为旧版本内核关闭BTF。
2022-02-03