2008-09-24 75 views
29

有没有办法在C编译时知道并输出函数所需的堆栈大小? 这里是我想知道:在编译时检查堆栈使用情况

让我们一些功能:

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

当编译这个功能,我想知道需要多少堆栈空间消耗了when它被调用。这可能对检测隐藏大缓冲区的结构的堆栈声明有用。

我要寻找的东西,将打印这样的:

foo.c文件:函数foo堆栈用量是n字节

有没有办法不看生成的汇编知道?或者可以为编译器设置一个限制?我试图避免给定进程的运行时堆栈溢出,我正在寻找一种方法来在运行时查找,如果由编译器确定的函数堆栈使用情况可用作编译过程。

让我们换一种方式:是否可以知道函数中所有对象的大小?我猜编译器优化不会是我的朋友,因为有些变量会消失,但优越的限制是好的。

+0

如果你想知道,我输入了秘密的'}'字符 – 2008-09-24 08:33:08

+0

这个问题似乎还不清楚。我猜如果你写更多的关于你为什么想知道这个以及为什么检查反汇编或可执行文件(这是检查编译器输出的最简单方法)是不可接受的,也许有人可以找到一些简单的解决方案? – Suma 2008-09-24 11:28:11

回答

10

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

我想我已经在很久以前使用过它用于用户空间的二进制文件,如果你知道一些perl编程,很容易修复它,如果它被破坏。

无论如何,它的基本功能是自动查看GCC的输出。而内核黑客写这样一个工具的事实意味着没有静态的方式来做到这一点与GCC(或者,它可能是最近添加的,但我怀疑是这样)。

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

1

只有编译器才会真正知道,因为它是把所有东西放在一起的人。您必须查看生成的程序集并查看序言中保留了多少空间,但这并不代表像alloca这些在运行时执行它们的事情。

+0

理论上静态代码分析工具,如lint可以完成这项工作,实际上我不认为他们这样做。 – Ilya 2008-09-24 08:49:48

-1

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

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

+0

您谈论的是进程堆栈,而不是每次调用函数将使用多少次 – shodanex 2008-09-24 10:18:33

+0

暂停问题不会停止静态分析(即在编译器中),并且通过查看程序文本来给出近似的答案。事实上,一个巨大的挑战是计算两个不同的程序是否相同,因此在静态分析下两个等效程序可能会给出不同的结果。 – Blaisorblade 2009-01-12 08:46:19

3

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

找到任何给定函数中的所有局部变量都很简单,每个变量的大小可以通过C标准(对于内置类型)或通过计算(对于结构和联合之类的复杂类型)来找到, 。

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

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

如果你想有一个100%准确的数字,那么你就必须来看看从编译器的输出或运行时检查(像拉尔夫在his reply建议)

+1

StackAnalyzer看起来不错,但它没有完成所要求的工作,因为它分析了可执行文件,而不是源代码。 从理论上说写这样的工具应该是可能的,但我并不存在这样的工具 - 检查堆栈使用运行时或基于组装是非常实用的。 – Suma 2008-09-24 11:25:25

8

StackAnlyser似乎examinate可执行代码本身加上一些调试信息。 什么是this reply描述,我正在寻找,堆栈分析仪看起来像矫枉过正给我。

与ADA存在类似的东西会很好。看看这个手​​册页从蚊蚋手册:

22.2静态堆栈使用分析

与-fstack使用率编译会生成一个额外的文件,指定使用的堆栈的最大数额的单位,在每个功能基础。该文件与具有.su扩展名的目标对象文件具有相同的基本名称。这个文件的每一行由三个字段组成:

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

第二字段对应于该函数帧的已知部分的大小。

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

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

1

假设你在一个嵌入式平台上,你可能会发现你的工具链有一个这样的问题。良好的商用嵌入式编译器(例如Arm/Keil编译器)通常会生成堆栈使用报告。

当然,中断和递归通常会超出它们,但是如果有人在某处堆栈中放置了一个多兆字节的缓冲区,那么它会给你一个粗略的想法。

1

不正是“编译时”,但我会做到这一点的生成后步骤:

  • 让连接器为您创建
  • 的地图文件在地图文件中的每个函数读取的对应部分的可执行文件,并分析功能序言。

这与StackAnalyzer所做的相似,但要简单得多。我认为分析可执行文件或反汇编是最容易获得编译器输出的方法。虽然编译器在内部知道这些东西,但是恐怕你无法从中得到它(你可能会要求编译器厂商实现这个功能,或者如果使用开源编译器,你可以自己做或者让别人去做为你)。

要实现这一点,你需要:

  • 能够解析映射文件
  • 了解可执行文件的格式
  • 知道函数序言可以是什么样子,并能够“解码”它

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

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