在编译时检查堆栈使用情况

时间:2008-09-24 08:30:33

标签: c compiler-construction callstack

有没有办法在C编译时知道并输出函数所需的堆栈大小? 以下是我想知道的事情:

让我们采取一些功能:

void foo(int a) {
    char c[5];
    char * s;
    //do something
    return;
}

编译这个函数时,我想知道它被调用时会消耗多少堆栈空间。这可能对检测隐藏大缓冲区的结构的堆栈声明很有用。

我正在寻找能打印出类似内容的东西:

file foo.c:function foo stack usage是n bytes

有没有办法不查看生成的程序集知道?或者可以为编译器设置限制?

更新:我不是要避免给定进程的运行时堆栈溢出,我正在寻找一种在运行时之前查找的方法,如果由编译器确定的函数堆栈使用可用作编译的输出过程

让我们换一种说法:是否可以知道函数本地所有对象的大小?我想编译器优化不会成为我的朋友,因为某些变量会消失但是上限很好。

7 个答案:

答案 0 :(得分:10)

Linux内核代码在x86上的4K堆栈上运行。因此他们关心。他们用来检查它的是他们编写的perl脚本,你可以在最近的内核tarball中找到script / checkstack.pl(2.6.25已经得到它)。它运行在objdump的输出上,使用文档在初始注释中。

我认为我很久以前就已经将它用于用户空间二进制文件了,如果你知道一些perl编程,如果它被破坏就很容易解决。

无论如何,它基本上做的是自动查看GCC的输出。内核黑客编写这样一个工具的事实意味着没有静态的方法来使用GCC(或者可能是最近添加的,但我对此表示怀疑)。

顺便说一下,使用来自mingw项目和ActivePerl的objdump,或者使用Cygwin,你也应该能够在Windows上以及使用其他编译器获得的二进制文件上做到这一点。

答案 1 :(得分:8)

StackAnlyser似乎在检查可执行代码本身以及一些调试信息。 this reply描述的是我正在寻找的东西,堆栈分析器看起来对我来说太过分了。

与ADA相似的东西会很好。从gnat手册中查看本手册页:

22.2静态堆栈使用分析

使用-fstack-usage编译的单元将生成一个额外的文件,该文件指定每个函数的最大堆栈使用量。该文件与具有.su扩展名的目标对象文件具有相同的基本名称。该文件的每一行由三个字段组成:

* The name of the function.
* A number of bytes.
* One or more qualifiers: static, dynamic, bounded. 

第二个字段对应于功能框架的已知部分的大小。

限定符static意味着函数框架大小是纯静态的。它通常意味着所有局部变量都具有静态大小。在这种情况下,第二个字段是功能堆栈利用率的可靠度量。

限定符动态意味着函数框架大小不是静态的。它主要发生在某些局部变量具有动态大小时。当此限定符单独出现时,第二个字段不是功能堆栈分析的可靠度量。当它有界限限定时,意味着第二个字段是功能堆栈利用率的可靠最大值。

答案 2 :(得分:3)

我不明白为什么静态代码分析无法为此提供足够好的数据。

在任何给定函数中找到所有局部变量是微不足道的,并且每个变量的大小可以通过C标准(对于内置类型)或通过计算(对于结构和联合等复杂类型)找到。

当然,答案不能保证100%准确,因为编译器可以进行各种优化,如填充,将变量放入寄存器或完全删除不必要的变量。但它给出的任何答案至少应该是一个很好的估计。

我做了一个快速的谷歌搜索,发现StackAnalyzer但我的猜测是其他静态代码分析工具具有类似的功能。

如果您想要100%准确的数字,那么您必须查看编译器的输出或在运行时检查它(如his reply中的Ralph建议)

答案 3 :(得分:1)

只有编译器真的知道,因为它是将你所有东西放在一起的人。您必须查看生成的程序集,并查看前导码中保留了多少空间,但这并不能解决像alloca这样在运行时执行其操作的内容。

答案 4 :(得分:1)

假设您使用的是嵌入式平台,您可能会发现您的工具链可以解决这个问题。良好的商业嵌入式编译器(例如Arm / Keil编译器)通常会生成堆栈使用情况的报告。

当然,中断和递归通常有点超出它们的范围,但是如果有人在某个地方使用多兆字节缓冲区进行了一些可怕的搞砸,它会给你一个粗略的想法。

答案 5 :(得分:1)

不完全是“编译时间”,但我会在构建后的步骤中执行此操作:

  • 让链接器为您创建一个地图文件
  • 为map文件中的每个函数读取可执行文件的相应部分,并分析函数序言。

这类似于StackAnalyzer的功能,但更简单。我认为分析可执行文件或反汇编是获得编译器输出的最简单方法。虽然编译器在内部知道这些事情,但我担心你无法从中获取它(你可能会要求编译器供应商实现这些功能,或者如果使用开源编译器,你可以自己做或者让别人去做为你)。

要实现这一点,您需要:

  • 能够解析地图文件
  • 了解可执行文件的格式
  • 知道一个功能序言可以是什么样子并且能够“解码”它

这将是多么容易或困难取决于您的目标平台。 (嵌入式?哪种CPU架构?什么编译器?)

所有这些绝对可以在x86 / Win32中完成,但是如果你从未做过这样的事情并且必须从头开始创建所有这些,那么你可能需要几天才能完成并且有一些工作。< / p>

答案 6 :(得分:-1)

不一般。理论计算机科学中的暂停问题表明,你甚至无法预测一般程序是否会在给定输入上停止。计算用于程序运行的堆栈通常会更复杂。所以不行。也许在特殊情况下。

假设您有一个递归函数,其递归级别取决于输入,该输入可以是任意长度,并且您已经不幸了。