如何记录malloc

时间:2020-03-05 18:49:42  来源:igfitidea点击:

这有点假设,并且经过了简化,但是...

假设一个程序将调用第三方编写的函数。这些团体可以被认为是非敌对的,但不能被认为是"有能力的"。每个函数将带有一些参数,具有副作用并返回一个值。当它们不运行时,它们没有任何状态。

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

这可能吗?这可行吗?

p.s.对我来说,重要的一点是确保没有分配持续存在,因此删除内存泄漏而不采取任何措施的方法对我没有用。

解决方案

回答

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

回答

与尝试记录malloc相比,更好的解决方案是在调用函数时将它们沙盒化,使它们访问固定的内存段,然后在函数运行完毕后释放该段。

无限制的,不称职的内存使用与恶意代码一样有害。

回答

我们可以在单独的过程中运行第三方功能,并在使用库完成后关闭该过程。

回答

首先,我们必须提供malloc()free()以及朋友的入口点。因为此代码已经编译(正确?),所以我们不能依靠#define进行重定向。

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

最快的方法根本不需要任何日志记录。如果他们使用的内存量有限,为什么不预先分配他们将需要的所有"堆"并在其中分配一个分配器呢?然后,当完成时,释放整个"堆",我们就完成了!如果想法更复杂,则可以将其扩展到多个堆。

如果我们确实需要"登录"而不是自己创建分配器,那么这里有一些想法。一,使用带有指针和内部链接的哈希表。另一种方法是在每个块的前面分配额外的空间,并在其中放置自己的结构,其中包含例如进入"日志表"的索引,然后保留日志表项的自由列表(作为堆栈,因此获得一个自由或者放回免费的是O(1))。这会占用更多内存,但是应该很快。

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

回答

由于我们担心内存泄漏和谈论malloc / free,因此我假设我们使用的是C。根据问题,我还假设我们无权访问第三方库的源代码。

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

回答

我们未指定操作系统或者环境,此答案假定为Linux,glibc和C。

我们可以设置__malloc_hook,__free_hook和__realloc_hook指向分别从malloc(),realloc()和free()调用的函数。有一个__malloc_hook联机帮助页,显示了原型。我们可以在这些挂钩中添加轨道分配,然后返回让glibc处理内存分配/重新分配。

听起来我们想在第三方函数返回时释放任何实时分配。有多种方法可以让gcc使用-finstrument-functions在每个函数入口和出口处自动插入调用,但是我认为这对我们尝试执行的操作而言是微不足道的。调用这些第三方函数之一后,是否可以在自己的代码中调用内存跟踪库中的某个函数?然后,我们可以检查是否有第三方功能尚未释放的任何分配。

回答

过去,我用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

回答

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

回答

如果我们对Purify太穷了,请尝试Valgrind。它比6年前要好很多,比Purify更容易涉足。

回答

Microsoft Windows提供(如果需要POSIX,则使用SUA),很可能是当今任何运输OS的最先进的heap +(其他已知使用堆的api)基础结构。

__malloc()调试钩子和关联的CRT调试接口非常适合我们具有测试源代码的情况,但是它们经常会丢失标准库或者其他链接的代码的分配。这是预期的,因为它们是Visual Studio堆调试基础结构。

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

它可以为所有堆修改入口点记录所有堆用户的完整堆栈跟踪记录(在后处理操作中重新显示符号信息),如果需要的话,可以串行记录。而且,它可能会在病理情况下修改堆,从而使数据分配一致,从而优化分配VM系统提供的页面保护(即在页面末尾分配我们请求的堆块,因此甚至单个字节溢出)在溢出时被检测到。

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

注意有两种模式。

  • 模式1,umdh {-p:进程ID | -pn:进程名} [-f:文件名] [-g]
  • 模式2,umdh [-d] {File1} [File2] [-f:Filename]我不知道是什么疯狂使选择-p:foo参数说明符和参数的裸顺序之间切换的开发人员感到困惑,但是它可以得到一个有点混乱。

调试sdk可以与许多其他工具一起使用,memsnap是显然专注于内存泄漏的工具,但我没有使用过,因此使用期限可能会有所不同。

对于UI模式,不带参数执行gflag,+ arg和/ args也是不同的"模式"。