设置struct perf_event_attr标志位触发syscall错误,perf工具正常的问题求助
我在处理perf事件相关问题时遇到过不少类似的情况,结合你描述的场景(自定义v4.6内核、perf命令行正常但自己的syscall调用报错),大概率是以下几个原因之一,你可以按步骤排查:
1. 未正确设置perf_event_attr.size字段
这是最常见的坑!内核会通过attr.size字段判断你传入的结构体版本,如果没有显式设置这个值,内核会默认使用旧版本的结构体大小,而exclude_kernel、exclude_user、exclude_hv这些位可能不在旧版本结构体的有效范围内,内核会直接判定参数无效,返回EOPNOTSUPP。
解决方法:初始化结构体时必须显式设置:
struct perf_event_attr attr = {0}; attr.size = sizeof(struct perf_event_attr); // 这行绝对不能省略!
2. 头文件与自定义内核版本不匹配
你自己编译了perf工具,但测试代码可能使用了系统默认的linux/perf_event.h头文件,而非自定义v4.6内核对应的头文件。如果头文件版本和内核不一致,struct perf_event_attr的定义可能存在差异(比如字段偏移、位的位置不同),导致内核无法正确识别你设置的exclude_*位。
解决方法:编译测试程序时,指定自定义内核的头文件路径:
gcc -o perf_test perf_test.c -I/path/to/your/custom/kernel/include
替换/path/to/your/custom/kernel/include为你实际的v4.6内核头文件目录(比如/usr/src/linux-4.6/include)。
3. 所选事件类型不支持exclude_*过滤
不是所有perf事件都支持内核/用户空间过滤,比如某些tracepoint事件或特定硬件事件可能不具备这个功能。但你提到perf命令行工具能正常工作,说明至少有部分事件支持该过滤,你可以先复制perf命令行使用的事件类型来测试。
比如perf默认常用的CPU周期事件,你可以在代码里这样设置:
attr.type = PERF_TYPE_HARDWARE; attr.config = PERF_COUNT_HW_CPU_CYCLES; attr.exclude_kernel = 1; // 尝试设置该位
4. 查看内核日志获取更详细的错误信息
内核通常会在dmesg中输出更具体的错误原因,你可以执行dmesg | grep perf查找相关日志,比如可能会看到“perf_event_attr size invalid”或者“event X does not support exclude_kernel”这样的提示,能帮你快速定位问题。
5. 对比perf工具的源码实现
既然perf命令行能正常工作,你可以查看perf工具中调用perf_event_open的代码(比如perf_event_open.c文件),看看它是如何初始化perf_event_attr结构体的,对比自己的代码找出差异。perf工具肯定处理了结构体版本兼容和参数校验的问题,参考它的实现能帮你快速修正自己的代码。
内容的提问来源于stack exchange,提问作者user3527764

