BPF程序tracepoint上下文flags字段访问及打印方法问询
问题:BPF程序无法正确识别openat调用的O_RDONLY标志,如何调试flags字段?
我想要实现一个BPF程序,仅当openat系统调用携带O_RDONLY标志时,才向trace_pipe写入内容。我参照/sys/kernel/debug/tracing/events/syscalls/sys_enter_open/format的格式构建了结构体,但第二个if语句始终为假,推测没有正确访问flags字段。
我的疑问是:
- 我是否正确访问了
flags字段? - 有没有办法打印
flags变量的内容?
我的代码如下:
struct syscalls_enter_openat_args { __u64 pad; int __syscall_nr; const char * filename; int flags; unsigned short modep; }; SEC("tracepoint/syscalls/sys_enter_openat") int bpf_sys(struct syscalls_enter_openat_args *ctx) { char fmt[] = "llo\n"; int flags = ctx->flags; if (flags){ if (flags == O_RDONLY) bpf_trace_printk(fmt, sizeof(fmt)); } return 0; } char _license[] SEC("license") = "GPL";
问题分析与解决方案
首先,你的代码存在两个核心问题,导致无法正确识别O_RDONLY标志:
1. 结构体定义错误,导致flags字段访问偏移错误
你参照的是sys_enter_open的tracepoint格式,但sys_enter_openat的参数结构和open不一样!openat系统调用的原型是:
int openat(int dfd, const char *pathname, int flags, mode_t mode);
对应的tracepoint结构体syscalls_enter_openat_args会比sys_enter_open多一个dfd(文件描述符)字段。你当前的结构体跳过了这个字段,导致ctx->flags访问的是内存中错误的位置,拿到的根本不是真实的flags值。
你可以通过以下命令查看sys_enter_openat的正确格式:
cat /sys/kernel/debug/tracing/events/syscalls/sys_enter_openat/format
正确的结构体应该是这样的:
struct syscalls_enter_openat_args { __u64 pad; int __syscall_nr; int dfd; // 你漏掉的关键字段 const char *filename; int flags; unsigned short mode; };
2. if (flags)的判断直接过滤掉了O_RDONLY的情况
O_RDONLY的宏定义是0,当调用openat使用纯只读模式时,flags的值就是0,你的第一个if (flags)条件会直接为假,跳过后续所有逻辑,自然永远不会触发第二个if的判断。
如何打印flags的内容调试
要打印flags的数值,只需要修改bpf_trace_printk的格式字符串,添加整型占位符%d,并将flags作为参数传入:
char fmt[] = "openat flags value: %d\n"; bpf_trace_printk(fmt, sizeof(fmt), ctx->flags);
之后你可以通过以下命令查看输出:
cat /sys/kernel/debug/tracing/trace_pipe
修正后的完整代码
// 注意:要确保结构体与sys_enter_openat的格式完全匹配 struct syscalls_enter_openat_args { __u64 pad; int __syscall_nr; int dfd; const char *filename; int flags; unsigned short mode; }; SEC("tracepoint/syscalls/sys_enter_openat") int bpf_sys(struct syscalls_enter_openat_args *ctx) { char fmt[] = "Detected O_RDONLY openat! Flags: %d\n"; int flags = ctx->flags; // 直接判断严格的O_RDONLY模式 if (flags == O_RDONLY) { bpf_trace_printk(fmt, sizeof(fmt), flags); } // 如果需要匹配所有包含只读权限的调用(比如O_RDONLY | O_CLOEXEC),可以用掩码判断: // if ((flags & O_ACCMODE) == O_RDONLY) { // bpf_trace_printk(fmt, sizeof(fmt), flags); // } return 0; } char _license[] SEC("license") = "GPL";
内容的提问来源于stack exchange,提问作者Maicake
相关产品推荐
相关产品推荐

