_CrtMemDumpAllObjectsSince不返回预期结果

_CrtMemDumpAllObjectsSince not returning expected results

本文关键字:结果 返回 CrtMemDumpAllObjectsSince      更新时间:2023-10-16

我正在使用_CrtMemCheckpoint和_CrtMemDumpAllObjectsSince来跟踪dll中可能的内存泄漏。

在DllMain中,当检测到DLL_PROCESS_ATTACH时,将调用一个init函数,该函数在全局_CrtMemState变量startState上调用_CrtMemCheckpoint(&startState)。 当检测到DLL_PROCESS_DETACH时,将调用一个调用_CrtMemDumpAllObjectsSince(&startState)的退出函数。 这返回

ExitInstance()Dumping objects ->
{8706} normal block at 0x07088200, 8 bytes long.
Data: <p v     > 70 FF 76 07 01 01 CD CD 
{8705} normal block at 0x07084D28, 40 bytes long.
Data: <                > 00 00 00 10 FF FF FF FF FF FF FF FF 00 00 00 00 
{4577} normal block at 0x070845F0, 40 bytes long.
Data: <dbV             > 64 62 56 0F 01 00 00 00 FF FF FF FF FF FF FF FF 
{166} normal block at 0x028DD4B8, 40 bytes long.
Data: <dbV             > 64 62 56 0F 01 00 00 00 FF FF FF FF FF FF FF FF 
{87} normal block at 0x02889BA8, 12 bytes long.
Data: < P          > DC 50 90 02 00 00 00 00 01 00 00 00 

到目前为止一切顺利,除了最后三个条目(4577、166 和 87)也在 startState 中。 即 如果我在我的 Init 函数和 Exit 函数中运行 _CrtDumpMemoryLeaks(),这些条目都在两个列表中。

文档是这样说的:

_CrtMemDumpAllObjectsSince 使用 state 参数的值来确定在何处启动转储操作。开始倾销从 指定的堆状态,状态参数必须是指向 _CrtMemState在调用_CrtMemDumpAllObjectsSince之前由_CrtMemCheckpoint填充的结构。

这让我相信在 startState 中跟踪的项目将被排除在输出之外。 在调用 _CrtMemCheckpoint 的 Init 函数结束时,大约有 4700 次分配调用。 _CrtMemDumpAllObjectsSince不应该只转储在该检查点调用之后分配的对象吗?

我错过了什么?


这显然很奇怪,但它(部分)完成了工作,但是以一种过分热心的模式
。这些功能已有几十年的历史,所以不是错误,但设计得并不完全好。

事实是,在"旧"状态中,有一些东西会在你的"自"状态之后发生变化。
所以问题是"是的,它确实反映了此后的变化,但它是致命的泄漏吗?">
这种频繁和放大了 DLL 的延迟初始化。
此外,还有许多复杂的对象,如map/string/array/list,它确实延迟了内部缓冲区的分配。
坏消息是,几乎所有声明为"静态"的复杂对象实际上都是在第一次使用时启动的。

因此,这些变化应该以_CrtMemDumpAllObjectsSince显示,因为它们改变了它们的记忆分配。
不幸的是,显示是如此粗糙和未经过滤,以至于它也显示了太多不相关的块(未修改)。

典型的最大罪魁祸首是使用"realloc"来改变旧alloc
的状态

这甚至可能看起来更奇怪,因为它们可能会消失,
例如,在统计快照后进行真正的malloc时,因为这会对用于转储的低水位标记进行一种"重置",将其设置为更高的水平。这神奇地让你的一堆"额外显示器"消失了。
如果您正在执行多线程,则行为会更加不稳定,因为它很容易变得不重复。

注意:
它不显示文件名和行号的事实表明它在这里处理的是预初始化。
所以罪魁祸首很可能是在main()(或InitInstance();)之前启动的静态复杂对象

龙:
_CrtMemDumpAllObjectsSince痛苦!
事实上,可能如此混乱的无用信息,以至于它违背了每天/每天简单使用_CrtMemDumpAllObjectsSince的目的。 (在它激发的良好本质中)
解决方法
不简单!
您可以尝试做一个malloc,然后在你做你的"自"状态快照后释放,以取笑这个标记。
但为了更安全、更易于控制,不幸的是,我确实看到了从原始 MS 结构中转储的自己的_MyCrtMemDumpAllObjectsSince的方法。 这是受到static void __cdecl dump_all_object_since_nolock(_CrtMemState const* const state) throw()
(见"debug_heap.cpp")版权Microsoft的启发!

代码"按原样"可用,更多是为了获得灵感。

但在对_CrtMemState工作方式进行一些解释之前:
_CrtMemState状态确实有一个指针"pBlockHeader",它通常是"_CrtMemBlockHeader*"的双链表的链接 ">
这个列表实际上不仅仅是一个快照,而是对所有正在使用的内存块的选择(不清楚如何), 以这样的方式排列,即"当前状态"直接指向"pBlockHeader">
,因此去
->_block_header_prev允许探索旧块
->_block_header_next允许探索新块(您在概念"自"中寻找的果汁,但非常危险,因为没有结束标记)

棘手的部分:
MS维护一个重要的内部静态_CrtMemBlockHeader*,称为__acrt_first_block__acrt_first_block在分配和重新分配期间不断变化

但是,_MyCrtMemDumpAllObjectsSince转储确实从此__acrt_first_block开始并向前(使用_block_header_next),直到找到 NULL ptr
处理的第一个块由这个__acrt_first_block决定,并且您发送的"状态"不超过转储的 STOP。

否则说_CrtMemDumpAllObjectsSince并没有真正转储"自"状态,
而是从__acrt_first_block转储到您的"自"状态。

">

for"循环通过显示从"开始"(自)到"结束"(自以来最早修改)的块而矫枉过正。
这是有道理的,但这也包括尚未修改的转储块。展示我们不关心的事情。

MS结构很聪明,可以直接使用,但不能保证Microsoft将来_CrtMemBlockHeader保持相同的重要结构
但在过去的15年里,我没有看到它有任何变化(我也没有预见到他们会改变战略和关键结构的任何原因)。

我不喜欢复制/粘贴 MS 代码并使用我的背负代码
解析链接器 所以我使用的解决方法是基于拦截发送到"输出"窗口的文本消息的能力,解码并将所有信息存储在我自己的银行

下面的结构给出了使用锁定下的静态结构来存储所有信息的
截距的想法

_CrtSetReportHook2(_CRT_RPTHOOK_INSTALL,MyReportHookDumpFilter);
_CrtMemDumpAllObjectsSince(state);  // CONTRARY to what it says, this thing seems to dump everything until old_state
_CrtSetReportHook2(_CRT_RPTHOOK_REMOVE,MyReportHookDumpFilter);
_MyReportHookDumpFilterCommand(_CUST_SORT,NULL);
_MyReportHookDumpFilterCommand(_CUST_DUMP,NULL);

"_MyReportHookDumpFilterCommand"确实会检查根本不被修改的块的预先存在,并避免在转储阶段显示这些块 将其

作为代码的灵感以简化显示。如果有人有更简单的使用方法,请分享!

相关文章: