如何确定最大堆栈使用量?

时间:2008-12-23 15:50:36

标签: memory embedded stack code-analysis

有哪些方法可用于确定嵌入式/内存受限系统的最佳堆栈大小?如果它太大那么浪费的内存可以在其他地方使用。但是,如果它太小,我们就会得到这个网站的同名......

尝试跳起点:Jack Ganssle在 The Art of Designing Embedded Systems 中声明,“根据经验,我们学习了一种标准的,科学的方法来计算堆栈的正确大小:随意选择一个大小并希望。“任何人都可以做得更好吗?

要求提供更具体的例子。那么,在没有操作系统的情况下使用MSP430 MCU工具链来定位IAR Embedded Workbench带有2 kB RAM的C程序怎么样?使用JTAG调试器时,此IDE可以显示堆栈内容和使用情况。

5 个答案:

答案 0 :(得分:29)

确定最深堆栈使用情况的最常用方法是使用一些已知但不寻常的值初始化堆栈内存,然后定期(或在大型测试运行结束时)查看该模式停止的位置。

这正是IAR IDE确定使用的堆栈量的方式。

答案 1 :(得分:22)

您使用静态分析标记了您的问题,但这是一个难以通过静态分析解决的问题。堆栈使用情况取决于程序的运行时配置文件,特别是,如果您正在使用递归或alloca。鉴于这是一个嵌入式平台,我想也很难运行像 ps top 这样的东西,看看你的应用程序使用了多少堆栈。

一种有趣的方法是使用当前堆栈帧的地址来确定使用了多少堆栈。您可以通过获取函数参数或局部变量的地址来完成此操作。为 main 函数和您认为使用最多堆栈的函数执行此操作。差异将告诉您应用程序所需的堆栈数量。这是一个例子(假设通常的从高到低的堆栈增长)。

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;
    // ...
}

如果你的编译器允许你指定一个自定义函数序言(很多都是为了允许基于图形的程序分析),你甚至可以安排所有函数调用这样的测量代码。那么你的测量功能就像是

void
stack_measurement_function(void)
{
    int a;
    stack_bottom = min(stack_bottom, (char *)&a);
    // ...
}

我使用类似于我所描述的方法来生成these charts

答案 2 :(得分:5)

使用良好的源代码静态分析工具,您可以为您的应用程序生成调用图。鉴于此,以及编译器生成的本地/临时数量的估计,您可以直接计算堆栈需求的保守估计。

我所说的“好”分析工具是指能够读取所有涉及的编译单元,可以确定直接函数调用,可以确定间接指针,在compilaiton单元中,可以计算整个保守点到分析系统,可以构建一个考虑点到分析的调用图。这消除了很多工具,这就是人们看到特殊方法的原因,例如“在运行时填充堆栈并查看会发生什么”。您还需要估计编译器在堆栈上放置的堆栈需求;通过简单地了解所有类型的存储需求有多大,您可以近似大量,这对于嵌入式系统C程序来说通常相当容易确定。最后,您需要相信您的应用程序没有递归调用,或者该工具知道最深的递归(可能是您告诉它)。

DMS Software Reengineering Toolkit满足C程序的所有这些要求。 见http://www.semanticdesigns.com/Products/DMS/DMSToolkit.html 您仍然需要通过爬网调用图并使用各种大小估计来配置它来计算堆栈需求。

如果您想快速回答,请使用堆叠填充技巧。如果你想要一个你可以在每个源代码更改后重新计算的答案,你将需要静态分析方法。

答案 3 :(得分:2)

我正在研究这个问题 - 即stacksize的分析计算。 它显然是一个高度递归的代码片段,因为函数调用可以将索引数组作为其一个或多个参数,并且一个或多个数组索引可能涉及函数调用!!

然而,一些实现可以减轻复杂性:

(1)当使用高级语言编译器时,每个语句/代码行执行结束时的堆栈指针应该与开始时的位置相同。 (至少这是一个很好的规则,否则你会遇到问题!)

(2)从每个函数或子程序调用返回后的stackpointer应该与它预先调用时相同。因此,对于程序中的所有语句,最大堆栈大小是每个语句中达到的峰值堆栈大小的最大值。 (至少这是一个很好的规则,否则你会遇到问题!)

当然一个语句可以包含我上面提到的递归问题,但至少找到整个程序的最大stacksize要求的问题然后归结为找到每个语句的最大stacksize要求,然后选择最大值那些。

在调用所有调用的函数之前,无法完成此操作。所以我为每个编译的模块生成一个文件,为每个语句记录一些堆栈大小 (基本上是每个函数调用之前的峰值和每个函数调用之前的值(不包括由函数调用引起的任何尚未知的stacksize添加),以及涉及的函数名称。然后我使用递归回溯处理这些文件例程,一旦编译完所有函数,就可以确定峰值堆栈大小。

幸运的是,除了递归例程之外,最大可能的堆栈大小要求不依赖于程序流,尽管在典型的流程(与数据相关)中,可能永远不会达到这个最大可能的堆栈大小。

示例:假设函数1调用函数2并且两者的程序流程都取决于数据值X.假设有一个X范围导致函数1执行其最差语句,其中包括对函数2的调用,其中因为我们通过同时使用函数1和函数2的最坏情况来计算最大可能的堆栈大小,所以我们可能高估了堆栈大小。至少我们在安全方面犯了错误。

我喜欢在操作系统堆栈上给中断例程提供自己的堆栈空间(如果需要),除了从中断返回地址之外,它们不会增加程序堆栈要求

答案 4 :(得分:-7)

  • 使用递归或递归算法永远。 (当心正则表达式库)
  • 使用数组,请始终使用malloc()。
  • 不要使用alloca(),有些编译器甚至在这个函数中有bug。

然后手动检查 代码部分,寻找堆栈使用率可能最高的位置(记住我说没有数组)

  • 检查代码本身怀疑 高点的堆栈使用情况,登录调试器界面。
  • 根据估计的堆栈使用情况设置上限,并使用该上限。例如限制服务器连接。