2008-09-08 21 views
4

这是一个有点假设和粗略简化,但...如何登录mallocs

假设一个程序,将调用第三方写的函数。这些政党可以被认为是非敌对的,但不能被认为是“有能力的”。每个函数都会接受一些参数,产生副作用并返回一个值。他们没有跑步时没有状态。

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

这可能吗?这是否实用?

附:对我而言,重要的一点是确保没有分配方式能够持续消除内存泄漏,而不会对我无用。

回答

4

不指定操作系统或环境中,这个答案假设Linux上,glibc的,和C

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

听起来好像你想在第三方函数返回时释放任何现场分配。有办法让gcc使用-finstrument-functions在每个函数入口和出口处自动插入调用,但是我认为这对你正在尝试做的事来说是不合适的。在调用其中一个第三方函数之后,你可以让自己的代码在你的内存跟踪库中调用一个函数吗?然后您可以检查是否有第三方功能尚未释放的任何分配。

1

难道你不能只是强迫他们把所有的内存分配到堆栈上吗?通过这种方式,在函数退出后可以释放它。

2

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

无限制,无能的内存使用情况可能与恶意代码一样具有破坏性。

3

您可以在单独的进程中运行第三方功能,并在完成使用库时关闭进程。

4

首先,您必须提供malloc()free()和朋友的入口点。由于此代码已编译(对吗?),因此您不能依赖#define进行重定向。

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

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

如果你确实需要“登录”而不是自己创建分配器,这里有一些想法。一,使用带指针和内部链接的散列表。另一种方法是在每个块前分配额外的空间,并将自己的结构包含在你的“日志表”中,然后保留一个自由列表的日志表条目(作为一个堆栈,以便获得一个免费的表)或者把一个免费的回来是O(1))。这需要更多的记忆,但应该快。

它是否实用?我认为,只要击中速度是可以接受的。

0

由于您担心内存泄漏并且在讨论malloc/free,我假设您使用的是C语言。我还根据您的问题假设您无权访问第三方的源代码图书馆。

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

1

在过去我写了一个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

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

0

如果你对Purify来说太穷了,试试Valgrind。它比6年前好很多,比Purify更容易深入。

+0

是啊 - valgrind的作品也很好。感谢您的提醒。 – 2008-10-21 04:14:29

0

Microsoft Windows提供(使用SUA,如果您需要POSIX),很可能是当今任何装运操作系统的最先进堆+(已知使用堆的其他api)基础架构。

__malloc()调试钩子和关联的CRT调试接口对于有测试源代码的情况很好,但是它们通常会错过标准库或其他链接代码的分配。这是预期的,因为它们是Visual Studio堆调试基础结构。

gflags是一个非常全面和详细的调试功能,已包含在Windows中多年。具有用于源和二进制的高级功能仅用于使用情况(因为它是OS堆调试基础结构)。

它可以在所有堆修改入口点(如果需要的话)串行地记录所有堆用户的完整堆栈跟踪(在后处理操作中重新标记符号信息)。而且,它可以修改具有可能对齐数据分配的分布式情况的堆,使得由VM系统提供的页面保护被最优地分配(即,在页面末尾分配请求的堆块,因此在溢出时也会检测到单字节溢出。

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

备注有2种模式;

  1. MODE 1,UMDH {-p:进程ID | -pn:ProcessName} [-f:文件名] [-g]
  2. MODE 2,UMDH [-d] {File1中} [文件2 ] [-f:文件名]

    我不知道什么疯狂抓住了开发人员选择在-p:foo参数说明符和参数的裸排序之间切换,但它可能会有点混乱。

调试SDK可与许多其他工具,memsnap是一个工具,这显然侧重于记忆leask等,但我没有用它,你milage可能会有所不同。

对于UI模式执行不带参数的gflags,+ arg和/ args是不同的“模式”也有用。