如何确定最大的堆栈使用情况?
有哪些方法可用于确定embedded式/内存受限系统的最佳堆栈大小? 如果它太大,那么浪费的内存可能会在其他地方使用。 但是,如果它太小,那么我们得到这个网站的同名…
尝试跳跃式的开始:Jack Ganssle在“deviseembedded式系统的艺术”中指出: “凭借经验,学习计算堆栈的适当大小的标准和科学方法:随机挑选一个大小,并希望。 任何人都可以做得比这更好?
要求更具体的例子。 那么,如何使用IARembedded式工作台工具链,针对带有2 kB RAM的MSP430 MCU的C程序,无需操作系统? 该IDE可以在使用JTAGdebugging器时显示堆栈内容和使用情况。
确定最深的堆栈使用情况的最常用的方法是用一些已知但不寻常的值初始化堆栈内存,然后定期(或在一个大的testing运行结束时)查看模式停止的地方。
这正是IAR IDE如何确定使用的堆栈的数量。
你用静态分析来标记你的问题,但这是一个很难通过静态分析来解决的问题。 堆栈使用情况取决于程序的运行时configuration文件,特别是在使用recursion或alloca时。 鉴于这是一个embedded式平台,我想也很难运行像ps或top这样的东西,看看你的应用程序使用了多less堆栈。
一个有趣的方法是使用当前堆栈帧的地址来确定使用了多less堆栈。 你可以通过获取一个函数的参数或局部variables的地址来做到这一点。 这样做的主要function和你认为使用最多的堆栈function。 差异会告诉你应用程序需要的堆栈数量。 这里是一个例子(假设通常从高到低的堆栈增长)。
char *stack_top, stack_bottom; int main(int argc, char *argv[]) { stack_top = (char *)&argc; // ... printf("Stack usage: %d\n", stack_top - stack_bottom); } void deeply_nested_function(void) { int a; stack_bottom = (char *)&a; // ... }
如果你的编译器允许你指定一个自定义的函数序言(许多它允许基于graphics的程序分析),你甚至可以安排所有的函数调用这样的测量代码。 那么你的测量function就是类似的东西
void stack_measurement_function(void) { int a; stack_bottom = min(stack_bottom, (char *)&a); // ... }
我使用了类似于我所描述的方法来生成这些图表 。
使用一个好的源代码静态分析工具,您可以为您的应用程序生成一个调用图。 鉴于此,以及对编译器生成的本地/临时数量的估计,您可以直接计算堆栈需求的保守估计。
我所说的“好”的分析工具是指可以读取所有涉及的编译单元,可以确定直接函数调用,可以确定间接指针,在合并单元中,可以计算整个系统的保守点到点分析,可以考虑点分析来构build呼叫图。 这消除了很多工具,这就是为什么人们会看到临时的方法,例如“在运行时填充堆栈,看看会发生什么”。 您还需要编译器放置在堆栈上的堆栈估计值; 只需知道所有types的存储需求有多大,通过embedded式系统C程序一般来说相当容易确定,就可以估算出大量的数据。 最后,你需要相信你的应用程序没有recursion调用,或者这个工具有最深的recursion(可能是你说的)。
DMS软件再造工具包满足C程序的所有这些要求。 请参阅http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html您仍然必须对其进行configuration,以便通过抓取调用图并使用各种大小估计来计算堆栈需求。;
如果你想要一个快速的答案,使用堆栈填充技巧。 如果你想要一个你可以在每个源代码更改后重新计算的答案,你将需要静态分析方法。
我现在正在处理这个问题 – 即stacksize的分析计算。 这显然是一个高度recursion的代码,因为一个函数调用可能有一个索引数组作为其一个或多个参数,一个或多个数组索引可能涉及函数调用!
然而,一些实现允许从复杂性中解脱出来:
(1)使用高级语言编译器时,每个语句/每行代码执行结束时的堆栈指针应该与开始时的位置相同。 (至less这是一个很好的规则,否则你会遇到问题!)
(2)从每个函数或子程序调用返回后的堆栈指针应该与调用前相同。 因此,在每个语句中达到峰值堆栈大小的最大堆栈大小是程序中所有语句的最大值。 (至less这是一个很好的规则,否则你会遇到问题!)
当然,一个语句可以包括上面提到的recursion问题,但是至less要find整个程序的最大堆栈大小要求,然后find每个语句的最大堆栈大小要求,然后select最大值。
在所有被调用的函数都被编译之前,这是不能完成的。 因此,我为每个编译的模块生成一个文件,logging每个语句的堆栈大小(基本上是每个函数调用之前的峰值和每个函数调用之前的值)(除了由函数引起的堆栈大小之外的任何未知的添加调用)以及函数名称,然后在所有函数编译完成后,使用recursion例程回溯处理这些文件,以确定峰值堆栈大小。
幸运的是,除了recursion例程之外,最大可能的堆栈大小要求不取决于程序stream,尽pipe在典型的数据stream(取决于数据)中,这种最大可能的堆栈大小可能永远达不到。
例子:假设函数1调用函数2,两者的程序stream依赖于数据值X.假设有一个X的范围,导致函数1执行其最坏的语句,其中包括对函数2的调用,该函数不执行对于X的相同范围来说,它是最坏的情况。由于我们同时使用函数1和函数2的最坏情况计算了最大可能的堆栈大小,所以我们可能高估了堆栈大小。 至less我们错在安全的一面。
如果需要的话,我喜欢在OS堆栈上给中断例程自己的堆栈空间,所以除了从中断返回地址以外,它们不会增加程序堆栈的要求
- 不要使用recursion或recursionalgorithm。 (谨防正则expression式库)
- 不要使用数组,总是使用malloc()。
- 不要使用alloca(),有些编译器甚至在这个函数中有bug。
然后手工检查代码的一部分,寻找堆栈使用情况可能最高的地方(记得我说过没有数组)
- 检查代码本身的可疑 高点处的堆栈使用情况,login到debugging器接口。
- 根据估计的堆栈使用情况放置一个上限,并使用该上限。 例如限制服务器连接。