【问题标题】:Tool to clearly visualize Memory Layout of a C Program清晰可视化 C 程序的内存布局的工具
【发布时间】:2011-04-22 05:42:56
【问题描述】:

假设我有这个代码:

int main() {
    int var1;  
    char *ptr = malloc(5 * sizeof(char));  
    //...........  
    do_something();  
    //...........    
    return 0;  
}

我们知道实际的内存布局会被划分为:.text.bss.data.heap.stack

我知道如何使用objdumpreadelf 等。但是,我想更好地了解内存堆栈,在那里我可以看到如下内容:

.heap       ptr  
.stack      do_something()  
.text       main()  
.bss        var1  

重点是:objdumpreadelf 等的输出中缺少实际的变量名称。

我正在用-g 编译这段代码,因此保留了符号表。

那么,为什么我看不到包含局部/全局变量名的内存布局?

objdump -x 显示变量的名称,如果类型是 static 否则不是。为什么?

【问题讨论】:

  • ptrmain的局部变量,就像var1一样。您希望在 .heap 中看到的对象是通过调用 malloc 分配的无名块,并且该块不知道其地址存储在名为 ptr 的变量中。这只是您不想看到这些细节的原因之一。
  • 除非你告诉我们你使用的是哪个编译器,否则“-g”是没有意义的。对于每个项目将出现在哪个块中有一般原则(例如,.data 或 .bss 中的静态变量 - .bss 中的变量没有初始值),但除此之外的任何内容都将由平台和编译器定义。事实上,即使是上面的 .data 和 .bss 位也不能保证 - 这是基于相当可靠原则的假设,但在某些特殊情况下可能是错误的 - 例如过去有 C 解释器可用。
  • 哦 - 你没有在目标文件/汇编器/任何东西中获得 .heap 或 .stack 部分。堆和堆栈是动态数据结构,堆栈由处理器管理(在某种程度上是 O/S),堆由运行时库管理(在某种程度上是 O/S)。所以编译器根本不能给你一个固定的布局——不存在固定的布局。对于堆栈,可以在特定函数中为局部变量生成布局,但对于现代编译器,它会比指导性更令人困惑 - 涉及范围和寄存器使用的复杂性。
  • 感谢史蒂夫,是的,我们无法获得堆栈和堆部分的固定视图,但仍然可以获得即时视图。我在 GNU/Linux 平台上使用 gcc。
  • 如果您想要在执行期间的某个时间点查看视图,您可能会从调试器中获得最好的结果——而且它们并不专注于提供内存布局可视化,所以不要指望一切立即变得清晰。无论如何,如果您使用的是 GCC,那基本上意味着 GDB,但这是一个命令行工具 - 不是很友好,而且您需要查看的信息似乎总是刚刚滚动到视野之外的信息 - 所以您可能想要众多基于 GDB 的 GUI 工具之一。抱歉,我没有真正的经验。

标签: c memory debug-symbols


【解决方案1】:

跟踪内存分配的方法很少,但它们都不是内置方法,所有这些方法都需要您做一些额外的工作。为了可视化内存,您必须使用代码检测和/或事件记录,即内存分配和释放事件,然后重放所有事件并从中生成图表。

看看这篇论文:Visualizing Dynamic Memory Allocations (in C programs)

GCSpy(用于堆可视化)可在此处获得:https://www.cs.kent.ac.uk/projects/gc/gcspy/。虽然最初用于 JVM,但您可以使用例如 dlmalloc 来可视化 C 程序的堆。

我完全理解您为什么要这样做 - 我也在寻找同样的东西。虽然我发现内存布局快照本身并不是很有用,但我发现观察内存如何随着时间的推移分配非常有趣,并且对于调试性能问题很有用。

我记得 XCode 内置了一些检测工具 - 虽然从未使用过它们,但也许值得探索它们提供的功能。

【讨论】:

  • @RobertLongson 谢谢,我按照你的建议更新了答案。
  • 不幸的是,这篇论文引用了一个断开的链接。
【解决方案2】:

很抱歉,您对此有些困惑。考虑:

  • 您的所有功能都在 .text 部分中
  • 堆栈上的所有非静态局部变量:它们可能是指针并且您打算为它们分配从 malloc 返回的值不会将它们放在堆上,它只是尝试创建一个指向对象在堆上。查看二进制文件的静态工具(如 objdump、readelf)无法知道 malloc 是返回内存还是失败。
  • 您的全局变量和static 变量可能最终会出现在已初始化或未初始化的数据段中 - 这取决于初始位模式是否完全为 0,以及编译器是否可以在编译时说服自己。李>

此外,如果您了解上述内容,那么您不需要任何东西来逐个变量地绘制小图表,您只需立即知道您正在使用什么类型的内存。

【讨论】:

  • 感谢您的快速回复。我同意你的看法,我可能知道我使用的是哪种内存。但我是从分析/调试的角度提出这个问题的,比如:我不想按照自己的观点行事。我想看到就相信。在使用 gdb 进行调试时,我们可以计算出变量的值可能是什么,但即便如此,我们还是使用:gdb$ p var。我只想查看一些实际的内存分配表,在那里我可以看到哪个函数/变量名称在哪个部分下;随着程序的进行和运行时堆栈的更改,我想再次获得更改后的视图。有可能吗?
  • @Sandeep:你不能轻易地查看用于堆的分配结构(至少在正常的堆实现中)——那些是 new/malloc 使用的。它们不应该被应用程序检查。您可以查看程序函数为局部变量创建的堆栈帧内部:使用frame N 更改帧,然后使用info locals 显示该帧中的变量。然后是 info variables [regexp] 在全局变量中搜索。查看 gdb 内置的“帮助”信息以了解详细信息 - 有点痛苦,但您需要熟悉它。
  • 至于看到程序运行时发生的变化:您可能会发现在 UI 环境中更容易。我想你可能会喜欢 DDD 之类的东西——数据显示调试器。它是 gdb 的前端,但可以更轻松地点击变量、跟随指针、检查结构等。它是免费的 OSS,应该在您的包管理器中可用。
  • @Sandeep - 愚蠢的问题,但如果您不相信调试器会告诉您变量的正确值(有时是明智的 - 至少在过去,他们可能会感到困惑),为什么要这样做你相信它会告诉你正确的内存布局吗?与在某个时间点在堆栈/堆上查找变量的位置相比,我希望解码该值非常可靠 - 唯一可能真正出错的是确定将其解码为的数据类型。
  • 嗨史蒂夫,这根本不是一个愚蠢的问题。我并不是说我不相信我的调试器打印的值。您无法清楚地理解问题。我对 gdb(不需要任何 GUI 工具)和大多数 unix 编程工具非常满意。我可以向您展示如何获取有关正在运行的程序的内存使用的一些(不是全部)信息。但是,必须有一些命令行工具来提供即时信息。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-31
  • 1970-01-01
  • 2016-04-11
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多