计算C++堆栈需求;如何获得可读的交易品种表

Computing stack demand for C++; How to get readable symbol table?

本文关键字:交易品 何获得 堆栈 C++ 需求 计算      更新时间:2023-10-16

假设您不仅有可执行文件,还有源代码文件。

我的问题是仅针对局部变量、返回地址、传递参数计算正在运行的进程的正确堆栈大小。我试图使用MS开发的VMMap。因为它可以捕获系统中具有堆栈等类别的分配内存。但是,它还包含保护页、分页文件等。因此,VMMap的堆栈大小被高估了。

我想改变解决问题的方式。我将跟踪堆栈以使用 WinAPI StackWalker64绘制实际的调用树,并从可执行文件或源代码中获取符号表。但是存在一个问题,即可执行文件(如ELF)中的符号表不可读。

现在,我计划使用编译器的词法分析器应用开源项目doxygen。因为doxygen只提供函数列表及其返回类型和函数参数,所以我不知道局部变量。因此,我还需要词法分析器制作完整的符号表作为预处理。但这有点复杂。我不确定这是最好的解决方案。有没有更好的解决方法?

OP 想要做的是静态计算最坏情况下的堆栈深度。

这是很难做到的。 为了实现这一目标,他需要:

  • 每个函数的源代码(作为用于推导以下事实的原材料)
  • 每个函数的"符号表"(所有声明)
  • 跨编译单元的应用程序代码调用图
  • 深入了解他的特定编译器如何生成和优化代码

"符号表"需要与编译器精确,以便估计过程知道哪些声明的数据(概念上)进入堆栈。 OP将需要相当于他特定的C++方言的完整编译器前端。 [OP错误地认为"词法分析器"会给他一个符号表。不会的]。

现在考虑构建全局调用图。 基础知识似乎很简单;如果函数 "bar" 包含 "foo(x)",则 "bar" 调用 "foo"。但是有很多复杂性:过载、虚拟函数、间接调用和强制转换。 重载大概是通过名称和类型解析来解决的;面对模板,这变得混乱(考虑 SFINAE)。虚函数和间接调用迫使人们构建一个保守的点到分析器;足够丑陋的强制转换可能会强制假设对任何参数兼容函数的调用。 点到式分析仪具有不同程度的精度;低精度可能会产生具有许多虚假(保守)边缘的调用图,这将抛弃大小估计。编译器不会提供此全局调用图,因为它仅在单个编译单元上运行。

最后,我们可以考虑构建堆栈大小估计值。 在这里,需要了解用于表示每个声明数据类型的大小和对齐方式,以及感兴趣的特定编译器如何将局部变量分配给堆栈。通常顺序编码块 { .... }{ ... }重叠堆栈位置。 还需要了解如何计算表达式以及如何传递参数,因为这些参数会影响堆栈的使用。 最后,需要了解编译器如何分配寄存器以及编译器可以/did(?)应用哪些优化,因为此类优化将影响表达式堆栈的使用以及实际分配给堆栈的局部变量数量。 这是一个非常多的知识,唯一值得信赖的知识来源是编译器本身。 与其试图复制所有这些机器,不如让编译器为每个函数提供其实际的堆栈大小分配(我相信 GCC 会这样做),或者放弃获得精确的结果并通过假设每个声明的本地消耗堆栈空间来保守地估计堆栈需求(在这种情况下,不清楚应该怎么做来估计表达式堆栈使用情况;人们可能会假设每个变量和中间表达式结果根据其类型占用堆栈空间)。

通过每个函数的堆栈

空间估计和调用图,对调用图的简单分析可以从根开始生成每个调用链的堆栈需求。 其中最大值是所需的堆栈估计值。 (注意:这假设每个函数对每次调用都使用其全栈需求;这显然是一个保守的估计)。 这部分非常简单。

总的来说,这是一个复杂的分析。 理想情况下,您可以让编译器提供堆栈大小估计和基本的调用图事实。 构建点到分析很困难,并且随着应用程序的大小变大而变得非常困难。

可以说,您可以弯曲 GCC 以帮助提供编译单元级别的数据。 Clang可能被设计为弯曲以提供相同的数据。据我所知,两者都没有为全局点分析提供具体支持。 目前尚不清楚海湾合作委员会和Clang处理C++的Windows方言;他们可能会。

我们的DMS软件再造工具包及其C++前端旨在提供符号表,名称解析的结果(例如,解决重载),并可以轻松提取本地呼叫事实。 它处理C++的海湾合作委员会和MS方言。 DMS 还为构建全局点分析和全局调用图提供支持;虽然我们没有专门用于C++,但我们已经使用它来处理大约 1600 万行的 C 应用程序。

所有这些困难解释了为什么人们经常使用动态分析来查看堆栈的大小。 如果OP想要一个静态分析器,他需要准备好投入大量的精力来获得它。