嵌入式软件静态分析都干了些啥?

strongerHuang 2026-06-14 08:00

 转自 | 最后一个bug


做嵌入式软件开发都听说过静态分享,但很多小伙伴都没明白静态分析都干了些啥,今天围绕堆栈给大家讲讲堆栈的静态分析都干了些啥。

1

堆栈静态分析 

当我们的嵌入式程序还没有运行的时候,我们能拿到的无非是程序的源码、函数的调用关系、编译后的二进制,当然这里面也包含对堆栈的操作。

有了这些信息的话,静态堆栈分析就能去做一些事情了,最直接的就是单函数栈帧的计算,编译器可以准确计算出每个独立函数的栈帧大小,这部分是完全确定的:

比如 GCC 编译器提供的-fstack-usage选项,GCC会为每个编译单元(.c / .cpp)生成一个对应的 .su 文件(Stack Usage 文件)。该文件记录了该编译单元内每一个函数的堆栈帧大小信息,类似于这样的格式

嵌入式软件静态分析都干了些啥?图1

其实这里的:

static 表示函数的堆栈帧大小完全在编译时确定,全部是静态分配的局部变量、保存的寄存器、参数区域等。

dynamic 表示函数中使用了运行时动态栈分配,比如 alloca 或可变长度数组(VLA),因此报告中的数值只包含静态部分,实际运行时会动态增加。

当然前面只是简单的单函数栈帧的分析,相对全面一点是静态调用图的理论栈深估算

静态分析工具会扫描所有确定的函数调用关系,构建调用树(Call Graph),然后找到最深的那条调用路径,把路径上所有函数的栈帧大小累加起来,得到一个理论上的最大栈深。

嵌入式软件静态分析都干了些啥?图2


比如 IAR、Keil 等 IDE 的静态栈分析功能,就是通过这个逻辑,在 map 文件中输出类似这样的报告:

*************************************************************************
*** STACK USAGE
***
Call Graph Root Category Max Use Total Use
------------------------ ------- ---------
Program entry 288 288
Maximum call chain 288 bytes
"__iar_program_start" 4
"_main" 8
"_printf" 8
"__PrintfFullNoMb" 152
"__LdtobFullNoMb" 80


2

堆栈动态分析

没办法,动态堆栈信息还必须得程序跑起来才能获取,因为静态分析的所有结论,都建立在一个假设上:程序的执行路径、调用关系、触发时机都是编译期可预测的但在嵌入式系统实际运行的运行过程中,这个前提似乎很不全面。

1、动态调用:编译期根本不知道你会调用谁

嵌入式代码中充满了大量的间接调用

这就导致前面我们分析的静态分析的调用图没法一层一层往下调用,这也就是我常常说的“断链”,当然如果编译器够聪明,把所有路径的栈开销都加起来,找到最深的栈,否则就会导致漏算,bug菌觉得当你程序比较大的时候,编译器去跟你这样分析也是非常吃力的。

2、谈到堆栈必定要聊递归调用

因为递归调用尝尝是导致爆栈的元凶,因为递归的深度完全取决于输入数据:比如快速排序的递归深度,取决于输入数组的有序程度;通常静态分析都是直接忽略递归的,要么你手动指定一个最大递归深度。

3、异常堆栈的隐形栈开销

大家都知道中断是异步的!它随时可能打断当前正在执行的代码,不管你现在的函数调用到了哪一层。

嵌入式软件静态分析都干了些啥?图3


比如说我们的主程序正在执行最深的调用链,已经用了 2KB 的栈空间;这时候一个高优先级中断触发了,CPU 立刻跳转到中断服务程序;中断服务程序自己又调用了 FFT 函数,又用了 1.5KB 的栈;如果你的总栈空间只有 3KB,直接就溢出了~

所以中断随时可能插队,把两个栈开销叠加起来,这个叠加效应只有运行时才能测到。再来个中断嵌套:如果你的系统支持中断嵌套,那可能一个中断里又来另一个中断,栈开销会层层叠加。

4、RTOS多任务更复杂

大家都知道RTOS任务的栈都是独立的、动态的,高优先级任务随时可以抢占低优先级任务;而且中断的栈开销,对于单堆栈的CPU来说是随机扣在某个任务的栈上的!嵌入式软件静态分析都干了些啥?图4

总的来说动态堆栈就还是在程序跑起来的复杂工况下去测试吧。

3

运行中的堆栈检测

堆栈的静态分析还是有一些局限,进行堆栈的动态分析也就是我们常说的“栈水印“(High Watermark)方法进行检测:

嵌入式软件静态分析都干了些啥?图5

以前的文章有写,再翻一翻:

大致就是:做栈标记->暴力测试->看水位,比如 IAR 的 C-SPY 调试器、FreeRTOS 的uxTaskGetStackHighWaterMark()函数,也基本都是这个原理。

bug菌做一些稳定性、可靠性要求较高的产品,基本上都是:

1、先用静态分析做第一轮评估,设置初始的堆栈大小;

2、然后各种工况下做长时间的压力测试,用动态分析拿到真实的栈峰值;最后

3、最后预留至少 20%~40% 的安全余量,确保极端情况下也不会溢出。

没错就是这么稳~

------------ END ------------

声明:内容取材于网络,仅代表作者观点,如有内容违规问题,请联系处理。 
嵌入式 软件
more
邀请函:TI嵌入式技术实验室和DLP®技术免费培训:AI加速ADAS、AI DSP、精密电机控制等(北京 西安 )
邀请:【10月28日上海 10月30日杭州】TI嵌入式技术实验室和DLP®技术免费培训,即将开讲!ADAS|AIDSP|精密电机
聊聊嵌入式开发的“偷懒”艺术
【RISC-V Now!亮点解析】先楫半导体:高性能MCU如何开启嵌入式新生态?
官宣!TI 嵌入式技术实验室研讨会与 DLP® 技术创新研讨会 | 6 城巡回,即将启程
开源RISC-V汽车电子芯片创新联盟巡礼 | TASKING:发挥嵌入式开发工具优势,赋能RISC-V 在汽车电子领域 “狂飙”
嵌入式的风向变了:2026纽伦堡嵌入式展透露这些趋势
押注千亿智能家电市场,海思嵌入式AI芯片有何大招?
嵌入式底层的代码,还需要一步一步手写吗?
嵌入式软件静态分析都干了些啥?
Copyright © 2025 成都区角科技有限公司
蜀ICP备2025143415号-1
  
川公网安备51015602001305号