这有点假设并且非常简化但是......
假设一个程序将调用由第三方编写的函数。可以假定这些当事方是非敌对的,但不能被认为是“有能力的”。每个函数都会带一些参数,有副作用并返回一个值。他们没有跑步时没有状态。
目标是通过记录所有malloc(等等)然后在函数退出后释放所有内容来确保它们不会导致内存泄漏。
这可能吗?这有用吗?
P.S。对我来说最重要的部分是确保没有分配持续存在,所以如果不这样做就消除内存泄漏的方法对我没用。
答案 0 :(得分:4)
首先,您必须为malloc()
和free()
以及朋友提供入口点。因为此代码已经编译(对吗?),所以您不能依赖#define
重定向。
然后,您可以通过将这些例程链接到这些模块,以明显的方式实现这些并记录它们来自某个模块。
最快的方法是根本没有日志记录。如果他们使用的内存量有限,为什么不预先分配他们将需要的所有“堆”并写出一个分配器那?然后当它完成后,释放整个“堆”,你就完成了!如果它更复杂,你可以将这个想法扩展到多个堆。
如果你真的需要“登录”而不是制作自己的分配器,这里有一些想法。一,使用带指针和内部链接的哈希表。另一种方法是在每个块前面分配额外的空间,并将自己的结构放在那里,例如,在“日志表”中包含一个索引,然后保留一个日志表条目的自由列表(作为一个堆栈,以便获得一个空闲的或者放回一个是O(1))。这需要更多的内存,但应该很快。
实用吗?我认为只要速度可以接受就可以了。
答案 1 :(得分:4)
您没有指定操作系统或环境,此答案假定为Linux,glibc和C.
您可以设置__malloc_hook,__ free_hook和__realloc_hook以指向将分别从malloc(),realloc()和free()调用的函数。有一个__malloc_hook手册页显示原型。您可以在这些钩子中添加轨道分配,然后返回让glibc处理内存分配/释放。
听起来你想在第三方函数返回时释放任何实时分配。有一些方法可以让gcc在每个函数入口和出口处使用-finstrument-functions自动插入调用,但我认为这对于你想要做的事情来说是不优雅的。在调用其中一个第三方函数后,您是否可以将自己的代码调用到内存跟踪库中的函数?然后,您可以检查是否存在第三方功能尚未释放的任何分配。
答案 2 :(得分:3)
您可以在单独的进程中运行第三方函数,并在使用完库后关闭该进程。
答案 3 :(得分:2)
比尝试记录mallocs更好的解决方案可能是在调用函数时对函数进行沙盒化 - 让它们访问固定的内存段,然后在函数运行完后释放该段。
无限制的,无能的内存使用可能与恶意代码一样具有破坏性。
答案 4 :(得分:1)
难道你不能强迫他们在堆栈上分配所有内存吗?这样就可以保证在函数退出后被释放。
答案 5 :(得分: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
答案 6 :(得分:0)
由于你担心内存泄漏和谈论malloc / free,我假设你在C里。我也假设你的问题是你无法访问第三方库的源代码
我唯一能想到的就是先检查你应用的内存消耗情况。在通话结束后,如果它们不同则记录错误消息并说服第三方供应商修复您发现的任何泄漏。
答案 7 :(得分:0)
如果您有足够的资金,请考虑使用Purify跟踪问题。它可以创建奇迹,并且不需要源代码或重新编译。还有其他调试malloc库可用更便宜。电子围栏是我记得的一个名字。也就是说,Denton Gentry提到的调试钩子似乎也很有趣。
答案 8 :(得分:0)
如果您对Purify太穷,请尝试Valgrind。它比6年前好多了,比Purify更容易潜入。
答案 9 :(得分:0)
Microsoft Windows提供(如果您需要POSIX,则使用SUA),很可能是当今任何运输操作系统中最先进的堆+(已知使用堆的api)基础架构。
__malloc()调试挂钩和相关的CRT调试接口适用于您拥有测试源代码的情况,但是它们经常会错过标准库或其他链接代码的分配。这是预期的,因为它们是Visual Studio堆调试基础结构。
gflags 是一套非常全面和详细的调试功能,已经包含在Windows中多年。具有源和二进制仅用例的高级功能(因为它是操作系统堆调试基础结构)。
它可以记录所有堆用户的所有堆用户的完整堆栈跟踪(在后处理操作中重新分隔符号信息),如果需要,可以连续地记录所有堆修改入口点。此外,它可以使用pathalogical案例修改堆,这可能会使数据分配保持一致,从而最佳地分配VM系统提供的页面保护(即在页面末尾分配您请求的堆块,因此即使是单个字节溢出在溢出时检测到。
umdh是一个可以帮助评估各个检查点状态的工具,但是在执行目标期间不断累积数据o在传统上下文中它不是简单的检查点调试停止。另外, WARNING ,最后我至少检查了存储堆栈信息的循环缓冲区的总大小,对于每个请求都有点小(64k条目(条目+堆栈)),所以你可能需要为重堆用户快速转储。还有其他方法可以访问这些数据,但是umdh非常简单。
注意有2种模式;
模式2,umdh [-d] {File1} [File2] [-f:Filename]
我不知道是什么疯狂抓住了选择在-p:foo参数说明符和参数的裸排序之间交替的开发人员,但它可能会有点混乱。
调试sdk与许多其他工具一起工作,memsnap是一个显然专注于内存leask等的工具,但我没有使用它,你的milage可能会有所不同。
执行没有参数的gflags用于UI模式,+ arg和/ args也是不同的“模式”。
答案 10 :(得分:0)
在Linux上,我已成功使用mtrace(3)
记录分配和释放。它的用法很简单
main()
的顶部)调用MALLOC_TRACE
,@ /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
设置为应保存跟踪并运行程序的文件路径。之后,输出文件将包含以下内容(从中间摘录以显示分配失败):
{{1}}