4. 中断与异常入口分析
中断与异常是RTOS实时性的核心来源,也是调试中最容易“看不见”的部分。任务栈回溯能告诉你“谁在跑”,但中断入口分析要回答的是“谁打断了它、打断了多久、上下文有没有被正确保存”。TRACE32 对中断/异常的支持贯穿静态符号、运行时断点、时间戳测量和栈帧检查四条线,本节按“先定位入口、再测量执行、最后验证上下文”的顺序展开。
一、从向量表定位入口地址
中断入口分析的第一步是确认向量表在内存中的真实位置。以 Cortex-M 为例,向量表由 SCB->VTOR 指向,复位后默认在 0x00000000,RTOS 或 Bootloader 常把它重定位到 Flash 或 RAM。在 TRACE32 中可用以下命令读取并列出向量表内容:
; 读取向量表基址寄存器
Data.Long(0xE000ED08) ; 读 VTOR
; 以 VTOR 为基址列出前 16 个异常向量
Data.List (Data.Long(0xE000ED08)) /Long
; 查看某个外设中断向量(例如 IRQ 编号 n)
Data.Long (Data.Long(0xE000ED08)+4*(16+n))
读出的每个 32 位值就是异常/中断服务程序的入口地址。把它与 sYmbol.List 或 sYmbol.Browse 中的符号对照,即可确认该向量对应的是 SysTick_Handler、PendSV_Handler 还是某个外设的 xxx_IRQHandler。若向量表被重定位到 RAM,还要注意 RAM 中的表项是否已被动态改写——这在动态注册中断的系统中很常见,静态符号表可能已经过时。
二、异常入口断点与执行时间测量
定位到入口地址后,最直接的观测手段是在入口处下断点。TRACE32 支持在符号上直接下断点:
Break.Set SysTick_Handler /Program
Break.Set PendSV_Handler /Program
Break.List
断点命中后,用 Register.View 查看 PC、LR、SP 以及 xPSR 中的 IPSR 字段,即可确认当前异常号。若使用 Trace 功能,可开启时间戳:
Trace.METHOD Onchip
Trace.STAMP ON
Trace.List
Trace 列表会给出每条记录的时间戳,入口与出口(如 BX LR 或 POP {PC})之间的差值就是该 ISR 的执行时间。需要注意:时间戳分辨率取决于芯片的 ITM/DWT 时钟,例如 DWT 周期计数器在 100 MHz 下分辨率为 10 ns,测量前应确认 DWT->CYCCNT 已使能。若 ISR 内部有分支或循环,单次测量只能反映一次执行,建议连续采样多次取最大值,因为最坏执行时间(WCET)才是实时性判断的依据。
三、中断嵌套跟踪
嵌套是中断分析中最容易出错的地方。Cortex-M 的优先级由 NVIC->IPR 配置,数值越小优先级越高。当高优先级中断到来时,硬件会自动压栈并跳转,此时 IPSR 会更新为新的异常号。在 TRACE32 中跟踪嵌套有两种方式:
- 断点法:在多个 ISR 入口同时下断点,观察命中顺序和
IPSR变化,可确认嵌套层级。 - Trace 法:开启
Trace.List后,每条记录都带有异常号和时间戳,能直接看到“A 进入 → B 抢占 → B 返回 → A 继续”的完整序列。
判断嵌套是否合法,关键看两点:一是抢占是否发生在 PRIMASK 或 BASEPRI 屏蔽窗口之外;二是嵌套深度是否超过栈容量。若在 Trace 中看到异常号反复跳变但时间戳间隔极短,往往说明存在优先级反转或中断风暴。
四、上下文保存与恢复检查
上下文检查的核心是栈。Cortex-M 在异常入口自动压入 xPSR、PC、LR、R12、R3~R0 八个寄存器,出口自动弹出。若 ISR 中调用了函数或使用了浮点,编译器还会额外压栈。在 TRACE32 中可用 Frame.View 或 Register.View /SP 检查:
; 断在 ISR 入口,查看自动压栈区
Register.View /SP
Data.List (Register.SP) /Long
; 断在 ISR 出口,确认 SP 是否回到入口值
Register.View /SP
若出口处 SP 与入口处不一致,说明栈未平衡,常见原因是 ISR 中手动压栈后未弹出,或使用了不匹配的调用约定。另一个检查点是 LR:入口时 LR 被硬件改写为 EXC_RETURN(如 0xFFFFFFF9),若在 ISR 中看到 LR 被普通函数调用覆盖,返回时将无法正确恢复,必须通过 BX LR 或 POP {PC} 使用正确的返回指令。
PSP 是否指向新任务栈。TRACE32 的 Task.List 与 Frame.View 结合,可以同时看到任务栈和异常栈的切换关系。五、一次完整的检查流程示例
假设某 Cortex-M4 系统出现“偶发任务延迟”,可按以下顺序排查:先读 VTOR 确认向量表位置,再在 SysTick_Handler 和 PendSV_Handler 入口下断点,开启 Trace 时间戳,连续采集 100 次中断记录。若发现 SysTick 执行时间从 2 μs 突增到 15 μs,进一步在 ISR 内部下断点,检查是否有未预期的循环或阻塞调用。最后在出口处核对 SP 与入口值,确认上下文完整恢复。整个过程不需要修改目标代码,全部通过 TRACE32 的符号、断点和 Trace 功能完成。
中断入口分析的价值在于把“看不见的打断”变成可测量、可比较的数据。向量表告诉你入口在哪,断点和 Trace 告诉你执行了多久、嵌套了几层,栈检查告诉你上下文是否安全。三者结合,才能对 RTOS 的实时行为给出可信判断。