如何记录 mallocs

How to log mallocs

提问人:BCS 提问时间:9/9/2008 最后编辑:BCS 更新时间:2/17/2020 访问量:2128

问:

这有点假设和严重简化,但是......

假设一个程序将调用由第三方编写的函数。这些当事方可以被认为是非敌对的,但不能被认为是“有能力的”。每个函数都会接受一些参数,有副作用并返回一个值。它们在不运行时没有状态。

目的是通过记录所有 malloc(等)然后在函数退出后释放所有内容来确保它们不会导致内存泄漏。

这可能吗?这实用吗?

p.s. 对我来说,重要的部分是确保没有分配持续存在,因此在不这样做的情况下消除内存泄漏的方法对我没有用。

内存 memory-management malloc

评论


答:

1赞 Mo. 9/9/2008 #1

你不能强迫他们在堆栈上分配所有内存吗?这样,在函数退出后,它就可以被释放。

2赞 Yes - that Jake. 9/9/2008 #2

比尝试记录 malloc 更好的解决方案可能是在调用函数时对函数进行沙盒处理 - 允许它们访问固定的内存段,然后在函数完成运行时释放该段。

不受限制、不称职的内存使用可能与恶意代码一样具有破坏性。

3赞 user4891 9/9/2008 #3

您可以在单独的进程中运行第三方函数,并在使用完库后关闭该进程。

5赞 Jason Cohen 9/9/2008 #4

首先,您必须提供 和 和 朋友的入口点。因为这段代码已经编译好了(对吧?),所以你不能依赖它来重定向。malloc()free()#define

然后,您可以以显而易见的方式实现这些例程,并通过将这些例程链接到这些模块来记录它们来自某个模块。

最快的方法根本不涉及日志记录。如果他们使用的内存量是有限制的,为什么不预先分配他们需要的所有“堆”,并从中写出一个分配器呢?然后当它完成时,释放整个“堆”,你就完成了!你可以将这个想法扩展到多个堆,如果它比这更复杂。

如果你真的需要“记录”而不是创建自己的分配器,这里有一些想法。第一,使用带有指针和内部链接的哈希表。另一种方法是在每个块前面分配额外的空间,并将你自己的结构放在那里,比如说,在你的“日志表”中包含一个索引,然后保留一个日志表条目的空闲列表(作为一个堆栈,所以得到一个空闲的或放回一个空闲的就是 O(1))。这需要更多的内存,但应该很快。

实用吗?我认为是,只要速度命中是可以接受的。

0赞 17 of 26 9/9/2008 #5

既然你担心内存泄漏并谈论 malloc/free,我假设你是 C。我还根据您的问题假设您无权访问第三方库的源代码。

我唯一能想到的就是在调用之前和之后检查应用程序的内存消耗,如果它们不同,则记录错误消息,并说服第三方供应商修复您发现的任何泄漏。

6赞 DGentry 9/9/2008 #6

您没有指定操作系统或环境,此答案假定 Linux、glibc 和 C。

您可以将 __malloc_hook、__free_hook 和 __realloc_hook 设置为指向将分别从 malloc()、realloc() 和 free() 调用的函数。有一个显示原型的__malloc_hook手册页。您可以在这些钩子中添加轨道分配,然后返回让 glibc 处理内存分配/解除分配。

听起来您希望在第三方函数返回时释放任何实时分配。有一些方法可以让 gcc 使用 -finstrument-functions 在每个函数入口和出口处自动插入调用,但我认为这对于您尝试做的事情来说是不优雅的。在调用这些第三方函数之一后,是否可以让自己的代码调用内存跟踪库中的函数?然后,您可以检查是否有第三方函数尚未释放的任何分配。

1赞 Steve Wranovsky 9/9/2008 #7

过去,我用 C 语言编写了一个软件库,它有一个内存管理子系统,它能够记录分配和释放,并手动匹配每个分配和释放。这在尝试查找内存泄漏时有一些用处,但使用起来既困难又耗时。日志的数量是压倒性的,需要花费大量时间来理解日志。

话虽如此,如果您的第三方库有大量分配,那么通过日志记录来跟踪它很可能是不切实际的。如果你在 Windows 环境中运行,我建议使用 Purify[1] 或 BoundsChecker[2] 等工具,它们应该能够检测第三方库中的泄漏。对该工具的投资应该在节省的时间中收回成本。

[1]:http://www-01.ibm.com/software/awdtools/purify/ 净化

[2]:http://www.compuware.com/products/devpartner/visualc.htm BoundsChecker

0赞 Jonathan Leffler 10/21/2008 #8

如果您有闲钱,请考虑使用 Purify 来跟踪问题。它创造了奇迹,并且不需要源代码或重新编译。还有其他更便宜的调试 malloc 库可用。电篱笆是我记得的一个名字。也就是说,Denton Gentry 提到的调试钩子看起来也很有趣。

0赞 Mitch Haile 10/21/2008 #9

如果你太穷了,无法净化,试试Valgrind。它比 6 年前好得多,而且比 Purify 更容易深入研究。

评论

0赞 Jonathan Leffler 10/21/2008
是的 - Valgrind 也运行良好。感谢您的提醒。
0赞 RandomNickName42 6/2/2009 #10

Microsoft Windows提供了(如果你需要POSIX,请使用SUA),很可能是当今任何交付操作系统中最先进的堆+(已知使用堆的其他api)基础设施。

__malloc() 调试钩子和关联的 CRT 调试接口非常适合您拥有测试源代码的情况,但是它们经常会错过标准库或其他链接代码的分配。这是意料之中的,因为它们是 Visual Studio 堆调试基础结构。

gflags 是一组非常全面和详细的调试功能,多年来一直包含在 Windows 中。为仅源代码和二进制用例提供高级功能(因为它是操作系统堆调试基础结构)。

如果需要,它可以记录所有堆用户的完整堆栈跟踪(在后处理操作中重新标记符号信息),用于所有堆修改入口点。此外,它可能会使用病理情况修改堆,这些情况可能会调整数据分配,以便以最佳方式分配 VM 系统提供的页面保护(即在页面末尾分配您请求的堆块,因此在溢出时甚至检测到单个字节溢出。

UMDH 是一种工具,可以帮助评估各个检查点的状态,但是在目标执行过程中,数据会不断累积,或者在传统上下文中它不是简单的检查点调试停止。另外,警告,至少上次我检查过,存储堆栈信息的循环缓冲区的总大小,对于每个请求都有些小(64k 个条目(条目 + 堆栈)),因此您可能需要为大量堆用户快速转储。还有其他方法可以访问这些数据,但 umdh 相当简单。

注意:有 2 种模式;

  1. 模式 1, umdh {-p:Process-id|-pn:ProcessName} [-f:文件名] [-g]
  2. 模式 2,umdh [-d] {File1} [File2] [-f:文件名]

    我不知道选择在 -p:foo 参数说明符和参数的裸露排序之间交替的开发人员是什么疯狂,但这可能会有点令人困惑。

调试 sdk 可以与许多其他工具一起使用,memsnap 是一个显然专注于内存 leask 等的工具,但我没有使用过它,您的里程可能会有所不同。

在UI模式下执行不带参数的gflags,+args和/args也是不同的使用“模式”。

0赞 Ruslan 2/17/2020 #11

On Linux I've successfully used mtrace(3) to log allocations and freeings. Its usage is as simple as

  1. Modify your program to call when you need to begin tracing (e.g. at the top of ),mtrace()main()
  2. Set environment variable to the file path where the trace should be saved and run the program.MALLOC_TRACE

After that the output file will contain something like this (excerpt from the middle to show a failed allocation):

@ /usr/lib/tls/libnvidia-tls.so.390.116:[0xf44b795c] + 0x99e5e20 0x49
@ /opt/gcc-7/lib/libstdc++.so.6:(_ZdlPv+0x18)[0xf6a80f78] - 0x99beba0
@ /usr/lib/tls/libnvidia-tls.so.390.116:[0xf44b795c] + 0x9a23ec0 0x10
@ /opt/gcc-7/lib/libstdc++.so.6:(_ZdlPv+0x18)[0xf6a80f78] - 0x9a23ec0
@ /opt/Xorg/lib/video-libs/libGL.so.1:[0xf668ee49] + 0x99c67c0 0x8
@ /opt/Xorg/lib/video-libs/libGL.so.1:[0xf668f14f] - 0x99c67c0
@ /opt/Xorg/lib/video-libs/libGL.so.1:[0xf668ee49] + (nil) 0x30000000
@ /lib/libc.so.6:[0xf677f8eb] + 0x99c21f0 0x158
@ /lib/libc.so.6:(_IO_file_doallocate+0x91)[0xf677ee61] + 0xbfb00480 0x400
@ /lib/libc.so.6:(_IO_setb+0x59)[0xf678d7f9] - 0xbfb00480