内核模块中tracepoints的使用方法及自定义irq跟踪事件咨询
跟踪内核中到达设备驱动前的IRQ事件
嘿,看你正在啃内核跟踪这块的硬骨头——kprobes、BPF/eBPF、tracepoints、perf这些工具都是内核调试和性能分析的利器,选对方向了!你想要跟踪到达设备驱动之前的IRQ事件,并且已经尝试在do_IRQ()里定义vector_irq_rcv这个tracepoint,这个思路非常准,毕竟do_IRQ()是内核处理硬中断的入口,确实是在设备驱动的中断 handler 之前执行的环节。
接下来我把你没写完的tracepoint代码补全,再一步步讲清楚怎么用:
先把你的tracepoint补全写规范
你贴的代码没写完,完整的vector_irq_rcv tracepoint应该是这样的:
TRACE_EVENT(vector_irq_rcv, TP_PROTO(unsigned int vector, unsigned int irq), TP_ARGS(vector, irq), TP_STRUCT__entry( __field( unsigned int, vector ) __field( unsigned int, irq ) ), TP_fast_assign( __entry->vector = vector; __entry->irq = irq; ), TP_printk("vector=%u, irq=%u", __entry->vector, __entry->irq) );
简单解释下每个部分:
TP_PROTO:声明触发tracepoint时要传的参数类型TP_ARGS:指定实际传递的参数名TP_STRUCT__entry:定义要记录的事件数据结构,这里我们存了中断向量vector和对应的IRQ号irq——这俩是跟踪IRQ最核心的信息TP_fast_assign:把参数值赋值给tracepoint的入口结构体,要高效TP_printk:定义默认的打印格式,用trace-cmd或perf直接看的时候会用这个格式输出
把tracepoint挂到do_IRQ()里
找到内核源码里的do_IRQ()函数(不同架构位置不一样:x86在arch/x86/kernel/irq.c,通用路径可能是kernel/irq/irqdesc.c),在函数最开头(刚进入中断处理,还没调用驱动的中断处理函数之前)加触发代码:
void do_IRQ(struct pt_regs *regs) { // 先拿到当前中断的vector和对应的irq号(x86架构示例) unsigned int vector = regs->orig_ax & 0xff; unsigned int irq = __this_cpu_read(vector_irq[vector]); // 触发我们的tracepoint,把数据打进去 trace_vector_irq_rcv(vector, irq); // 下面是内核原本的IRQ处理逻辑,别碰 struct irq_desc *desc = irq_to_desc(irq); // ... 后面的代码原样保留 }
注意:如果是ARM/ARM64架构,获取vector和irq的方式会不一样,得对应调整——比如ARM可能要从GIC寄存器里读,或者用架构提供的函数获取。
怎么验证和用这个tracepoint?
1. 编译内核要开对配置
确保内核config里打开了这些选项:
CONFIG_TRACEPOINTS=y(必须开,tracepoint的基础)CONFIG_PERF_EVENTS=y(如果要用perf工具的话)- 有些架构还有
CONFIG_IRQ_TRACEPOINTS=y,能开就开
2. 用trace-cmd实时看事件
# 启动trace,只跟踪我们的vector_irq_rcv事件 sudo trace-cmd record -e vector_irq_rcv # 这时候去触发你的硬件中断(比如插个USB设备,或者跑个测试用例) # 按Ctrl+C停止trace,然后看结果 sudo trace-cmd report
3. 用eBPF玩出花(更灵活)
既然你在学eBPF,用它来分析这个tracepoint的数据会更方便——比如过滤特定IRQ、统计频率啥的。给你整个BCC的脚本示例:
from bcc import BPF # BPF程序代码 bpf_code = """ TRACEPOINT_PROBE(kernel, vector_irq_rcv) { // 从tracepoint里拿参数 u32 vector = args->vector; u32 irq = args->irq; // 打印到trace管道 bpf_trace_printk("Pre-driver IRQ: vector=%u, irq=%u\\n", vector, irq); return 0; } """ # 加载BPF程序 b = BPF(text=bpf_code) # 实时打印输出 print("Waiting for IRQ events...") b.trace_print()
运行这个脚本,就能实时看到所有到达do_IRQ()的IRQ事件——这些都是还没到设备驱动处理的阶段,完全符合你的需求。
额外给你几个小技巧
- 如果不想跟踪所有IRQ,可以在触发tracepoint的时候加个判断,比如只跟踪IRQ 44:
if (irq == 44) trace_vector_irq_rcv(vector, irq); - 除了
do_IRQ(),还可以盯handle_IRQ_event()——这是调用设备驱动中断handler的前一步,在这里加tracepoint也能精准抓驱动处理前的IRQ - 用
perf也能直接看:sudo perf trace -e vector_irq_rcv,输出更简洁
内容的提问来源于stack exchange,提问作者cooshal
相关产品推荐
相关产品推荐

