为啥 _CrtSetDumpClient 不起作用?

Posted

技术标签:

【中文标题】为啥 _CrtSetDumpClient 不起作用?【英文标题】:Why is _CrtSetDumpClient not working?为什么 _CrtSetDumpClient 不起作用? 【发布时间】:2014-08-04 23:56:46 【问题描述】:

我正在使用 Visual Studio Express 2013 for Windows Desktop 编写一个用 C 语言编写的 Windows 命令行程序。在调试模式下编译时,我真的希望我的程序能够检测内存泄漏并将它们打印在标准错误或标准输出上,以便它们出现在我的面前。

通过调用_CrtDumpMemoryLeaks,我可以将内存泄漏信息打印到 Visual Studio 中的调试输出(您可以在输出窗格下找到)。根据 MSDN 文档,我认为我可以添加对 _CrtSetDumpClient 的调用,以便访问被转储的数据,然后将其打印到 stderr。

这是我用来测试这个问题的代码:

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <stdio.h>
#include <crtdbg.h>

void dumpClient(void * userPortion, size_t blockSize)

    printf("memory leak: %d\n", blockSize);


int main(int argc, char ** argv)

    printf("hello\n");
    _CrtSetDumpClient(&dumpClient);
    malloc(44);
    _CrtDumpMemoryLeaks();
    return 0;

我在 Visual Studio 中创建了一个新的 Visual C++ Win32 控制台应用程序项目,将此代码粘贴到项目中,禁用预编译头文件,确保 IDE 处于调试模式,然后构建。如果我按 F5 (开始调试命令)运行它,那么我可以在 Visual Studio 的调试窗口中看到以下输出,这很好:

Detected memory leaks!
Dumping objects ->
c:\users\david\documents\scraps\test_vc++\testvc\testvc.cpp(15) : 81 normal block at 0x0120A500, 44 bytes long.
 Data: <                > CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD CD 
Object dump complete.
The program '[3868] TestVC.exe' has exited with code 0 (0x0).

但是,如果我在 dumpClient 中设置断点,我可以看到它永远不会被调用。此外,如果我从命令提示符运行程序,它只会打印hello。我希望看到的预期输出是:

hello
memory leak: 44

有谁知道为什么 dumpClient 函数根本没有被调用?

【问题讨论】:

您创建了一个“正常”泄漏,而不是您的代码标记为“客户端”分配的特殊泄漏。通过 _CrtSetReportHook() 挂钩报告正常泄漏。 【参考方案1】:

TL;DR 你可以打电话给_malloc_dbg(44, _CLIENT_BLOCK, filename, line)而不是malloc

通过查看 dbgheap.c,您可以看到调用函数的唯一方法是:

if (_BLOCK_TYPE(pHead->nBlockUse) == _CLIENT_BLOCK)

    _RPT3(_CRT_WARN, "client block at 0x%p, subtype %x, %Iu bytes long.\n",
        (BYTE *)pbData(pHead), _BLOCK_SUBTYPE(pHead->nBlockUse), pHead->nDataSize);

    if (_pfnDumpClient && !__crtIsBadReadPtr(pbData(pHead), pHead->nDataSize))
    
        (*_pfnDumpClient)((void *)pbData(pHead), pHead->nDataSize);
    
    else
    
        _printMemBlockData(plocinfo, pHead);
    

所以你必须有 _BLOCK_TYPE(pHead->nBlockUse) == _CLIENT_BLOCK。

调用malloc时,只分配_NORMAL_BLOCKs。

您可以改为致电_malloc_dbg(44, _CLIENT_BLOCK, filename, line)。 http://msdn.microsoft.com/en-us/library/faz3a37z.aspx

然后你的函数将被调用。

当然,Microsoft 可以在 _CrtSetDumpClient 文档中提到这一点,但这太容易了 ;)

【讨论】:

谢谢!如果我查看 crtdbg.h(Visual Studio 附带),我可以看到 malloc(s) 是如何定义为 _malloc_dbg(s, _NORMAL_BLOCK, __FILE__, __LINE__) 的。我想知道他们为什么那样做。我还看到他们对free(s) 做了类似的事情,所以我可能必须为 malloc 制作我自己的预处理器宏,并免费让它正常工作。 仅供参考,dbgheap.c 可以在C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\crt\src找到【参考方案2】:

根据 MSDN documentation

_CrtSetDumpClient: Installs an application-defined function to 
 dump _CLIENT_BLOCK type memory blocks 

这里的关键字是 _CLIENT_BLOCK。此处记录了各种类型的 blocks on heap。一个简单的 malloc 调用会创建一个 _NORMAL_BLOCK,因此不会调用您的函数。

【讨论】:

以上是关于为啥 _CrtSetDumpClient 不起作用?的主要内容,如果未能解决你的问题,请参考以下文章

为啥这个 Python“循环代码”不起作用?

如果 Layout 连接到父级,为啥动态调整大小不起作用?

为啥 PHP $_SESSION 变量在 header() 方法中使用时不起作用?

为啥没有 _Remove_reference 时 std::move() 不起作用?

为啥我的 Mongoose 填充请求不起作用?

为啥 setter 的 'this._obj['a']=2' 在 Dart 类中不起作用