Dav*_*ary 38 embedded gcc code-analysis static-analysis
我正在为嵌入式系统编写启动代码 - 在跳转到main()函数之前加载初始堆栈指针的代码 - 我需要告诉它我的应用程序将使用多少字节的堆栈(或更大的,保守估计).
我被告知gcc编译器现在有一个-fstack-usage选项和-fcallgraph-info选项,它可以以某种方式用于静态计算我的"最大堆栈使用率".(" Botcazou,Comar和Hainque的编译时堆栈需求分析与GCC").
奈杰尔琼斯说,递归在嵌入式系统中是一个非常糟糕的主意("计算你的堆栈大小"2009),所以我一直小心不要在这段代码中做任何相互递归的函数.
另外,我确保我的中断处理程序都没有重新启用中断,直到它们最终从中断返回指令,所以我不需要担心重入中断处理程序.
如果没有递归或重入中断处理程序,则应该可以静态地确定最大堆栈使用情况.(所以大多数答案如何确定最大堆栈使用量?不适用).我的理解是我(或者最好是我的PC上的一些代码在每次重建可执行文件时自动运行)首先找到每个中断处理程序的最大堆栈深度,当它没有被更高优先级的中断中断时,最大值当main()函数没有被中断时,它的堆栈深度.然后我将它们全部添加到找到总(最坏情况)最大堆栈深度.当main()后台任务在被最低优先级中断中断时处于最大深度时发生(在我的嵌入式系统中),并且当中断被下一个最低优先级中断时,该中断处于其最大深度中断,等等.
我正在使用YAGARTO和gcc 4.6.0来编译LM3S1968 ARM Cortex-M3的代码.
那么如何使用-fstack-usage选项和-fcallgraph-info选项与gcc一起计算最大堆栈深度?或者是否有更好的方法来确定最大堆栈使用量?
(有关针对Keil编译器的几乎相同问题,请参阅如何确定嵌入式系统中的最大堆栈使用情况?)
τεκ*_*τεκ 21
海湾合作委员会文件:
-fstack使用率
基于每个函数为程序生成编译器输出堆栈使用信息.转储的文件名是通过将.su附加到auxname来实现的.auxname是从输出文件的名称生成的,如果明确指定并且它不是可执行文件,否则它是源文件的基本名称.条目由三个字段组成:
- 功能的名称.
- 一些字节.
- 一个或多个限定符:静态,动态,有界.
限定符static表示该函数静态操作堆栈:在函数入口处为帧分配固定数量的字节,并在函数出口处释放; 否则在功能中不进行堆栈调整.第二个字段是这个固定的字节数.
限定符动态意味着函数动态地操作堆栈:除了上面描述的静态分配之外,还在函数体中进行堆栈调整,例如围绕函数调用推送/弹出参数.如果限定条件也存在,则这些调整的数量在编译时受限,第二个字段是函数使用的堆栈总量的上限.如果不存在,则这些调整的数量在编译时不受限制,第二个字段仅表示有界部分.
我找不到任何对-fcallgraph-info的引用
您可以从-fstack-usage和-fdump-tree-optimized创建所需的信息
对于-fdump-tree-optimized中的每个叶子,获取其父项并将其堆栈大小数加起来(请记住,这个数字适用于任何具有"动态"但不是"有界"的函数)来自-fstack-usage,找到最大值这些值应该是您的最大堆栈使用量.
很晚,但是对于任何看过这个问题的人来说,将fstack-usage和调用图形工具(如cflow)的输出结合起来的答案最终可能会对任何动态分配产生严重错误,甚至是有限的,因为没有关于何时动态堆栈的信息分配发生.因此,不可能知道应该将值应用于哪些函数.作为一个人为的例子,如果(简化)fstack-usage输出是:
main 1024 dynamic,bounded
functionA 512 static
functionB 16 static
Run Code Online (Sandbox Code Playgroud)
一个非常简单的调用树是:
main
functionA
functionB
Run Code Online (Sandbox Code Playgroud)
将这些结合起来的简单方法可能会导致main - > functionA被选为1536字节的最大堆栈使用路径.但是,如果main()中最大的动态堆栈分配是将一个像记录这样的大参数直接推送到一个调用functionB的条件块中的堆栈上的函数(我已经说过这个是设计的),那么真正的主要 - > functionB是最大堆栈使用的路径,为1040字节.根据现有的软件设计,以及其他通过堆栈中所有内容的更受限制的目标,累积错误可能会迅速导致您查看声称显着夸大的最大堆栈大小的完全错误的路径.
此外,根据您在谈论中断时"重入"的分类,可能会完全错过一些堆栈分配.例如,许多Coldfire处理器的7级中断是边沿敏感的,因此忽略了中断禁用掩码,所以如果使用信号量提前离开指令,你可能不会认为它是可重入的,但是初始堆栈分配仍然会发生在信号量被检查.
简而言之,您必须非常小心使用此方法.