这有点假设并且非常简化,但是......

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

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

这可能吗?这实用吗?

附注对我来说重要的是确保没有分配持续存在,因此不这样做而消除内存泄漏的方法对我来说没有用。

有帮助吗?

解决方案

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

您可以设置 __malloc_hook、__free_hook 和 __realloc_hook 分别指向将从 malloc()、realloc() 和 free() 调用的函数。有一个 __malloc_hook 联机帮助页显示了原型。您可以在这些钩子中添加跟踪分配,然后返回让 glibc 处理内存分配/释放。

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

其他提示

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

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

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

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

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

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

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

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

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

过去,我用 C 语言编写了一个软件库,它有一个内存管理子系统,其中包含记录分配和释放以及手动匹配每个分配和释放的功能。这在尝试查找内存泄漏时有一定用处,但使用起来很困难且耗时。日志数量巨大,需要花费大量时间来理解日志。

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

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

[2]: http://www.compuware.com/products/devpartner/visualc.htm 边界检查器

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

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

如果您有闲钱,请考虑使用 Purify 来跟踪问题。它的效果非常好,并且不需要源代码或重新编译。还有其他更便宜的调试 malloc 库可用。电围栏是我记得的一个名字。也就是说,Denton Gentry 提到的调试钩子似乎也很有趣。

如果您太穷而无法使用 Purify,请尝试 Valgrind。它比 6 年前好多了,而且比 Purify 更容易深入研究。

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

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

全局标志 是一套非常全面且详细的调试功能,多年来一直包含在 Windows 中。具有适用于仅源代码和二进制用例的高级功能(因为它是操作系统堆调试基础设施)。

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

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

笔记 有2种模式;

  1. 模式 1,umdh {-p:进程 ID|-pn:进程名称} [-f:文件名] [-g]
  2. 模式 2,umdh [-d] {文件 1} [文件 2] [-f:文件名]

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

调试 sdk 可与许多其他工具配合使用, 内存快照 是一个显然专注于内存泄漏等问题的工具,但我没有使用过它,你的情况可能会有所不同。

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

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top