第4章:栈溢出检测工具:checkStack()函数、windView栈分析、Task Monitor

栈溢出这个问题,说大不大,说小不小。我见过太多项目,前期跑得稳稳当当,一上线就莫名其妙死机。查来查去,最后发现就是栈溢出了。嗯,今天咱们就聊聊VxWorks里几个实用的检测工具。

4.1 checkStack()函数:最直接的检测手段

先说说我最常用的一个函数——checkStack()。这玩意儿说白了就是帮你看看任务栈还剩多少空间。

STATUS checkStack
    (
    int tid,        /* 任务ID,0表示当前任务 */
    int *pFree,     /* 返回剩余栈空间 */
    int *pSize      /* 返回总栈大小 */
    )

调用方式很简单:

int freeStack, totalStack;
if (checkStack(0, &freeStack, &totalStack) == OK) {
    printf("当前任务:剩余 %d 字节,总大小 %d 字节\n", 
           freeStack, totalStack);
}

我个人习惯在任务的主循环里定期调用一下。尤其是那些处理网络数据包的任务,你想想看,数据量一上来,栈使用量可能瞬间飙升。

关键点:checkStack()返回的是当前时刻的剩余栈空间,不是历史最低值。所以最好在任务最繁忙的时候调用,才能看到真实水位。

4.2 windView栈分析:可视化追踪

光靠checkStack()打日志,说实话有点原始。windView才是真正的利器。

windView能记录每个任务的栈使用情况,而且是时间轴上的连续记录。我记得有一次调试一个间歇性死机的问题,就是靠windView抓到了栈溢出的瞬间。

使用步骤大致如下:

  1. 在VxWorks内核配置中启用windView组件
  2. 在目标机上启动windView日志记录
  3. 在主机端用WindSh或Workbench连接查看

windView会记录每个任务的栈指针变化。你可以在时间轴上看到:

  • 任务切换时的栈使用量
  • 函数调用深度
  • 中断嵌套时的栈叠加情况

小技巧:windView的日志缓冲区默认可能不够大。我建议把日志缓冲区设成至少4KB,否则关键事件可能被覆盖掉。

4.3 Task Monitor:实时监控利器

Task Monitor是VxWorks自带的另一个好工具。它能在运行时显示所有任务的状态,包括栈使用率。

启动方式:

-> taskMonitorShow

  NAME         TID    PRI   STATUS     STACK_SIZE   STACK_USED   STACK_FREE
  tShell       0x1a   5     READY      4096         1248         2848
  tNetTask     0x2b   50    PEND       8192         5632         2560
  tLogTask     0x3c   60    DELAY      2048         896          1152
  tMyTask      0x4d   100   READY      4096         3840         256    <-- 危险!

看到最后一行了吗?tMyTask的栈使用率已经超过93%了。这种任务随时可能溢出。

警告:Task Monitor显示的栈使用量是采样值,不是精确值。我曾经遇到过Task Monitor显示还有500字节空闲,但实际上任务已经溢出了。为什么?因为采样时刻刚好是栈回退之后。

4.4 实战经验:三种工具怎么配合

这三种工具各有侧重,我一般这样搭配使用:

工具 适用场景 我的用法
checkStack() 快速定位、在线调试 在关键函数入口/出口调用,打印日志
windView 深度分析、时序问题 复现死机问题,抓取栈溢出瞬间
Task Monitor 日常监控、压力测试 长时间运行,观察栈使用趋势

举个例子。有一次我在做通信网关项目,设备运行几小时后会随机重启。我先用Task Monitor观察,发现tNetTask的栈使用率在缓慢增长。然后用checkStack()在任务循环里打日志,确认了增长趋势。最后用windView抓到了关键证据——某个协议解析函数在收到畸形数据包时,递归调用导致栈溢出。

我的习惯:在压力测试阶段,我会写一个shell脚本,每隔5秒执行一次taskMonitorShow,把结果保存到文件。跑一晚上,第二天看哪个任务的栈使用率最高。这个方法虽然土,但很有效。

4.5 避坑指南

最后说几个我踩过的坑:

  • 中断栈和任务栈是分开的。checkStack()只能检查任务栈,中断栈要用intStackSizeGet()查看。我曾经忽略了中断栈,结果中断嵌套一多就崩了。
  • 栈溢出不一定立即崩溃。有时候溢出了几个字节,刚好没踩到关键数据,系统还能跑。但这是定时炸弹,迟早会炸。
  • 浮点运算会额外消耗栈空间。如果任务里用了浮点,VxWorks会分配额外的上下文保存空间。这个在计算栈大小时要算进去。

嗯,工具就这些。关键是要养成习惯,在开发阶段就定期检查栈使用情况。别等到现场出问题了再查,那时候就晚了。

专注资料整理