用GFlags和UMDH发现堆内存泄漏

xiaoxiao2021-02-28  87

C++内存泄漏问题

简单来说,就是new/malloc后的内存,没有做delete/free。 这样的问题,难以发现,难以调试。在Windows上,有两个工具,GFlags和UMDH,可以用来发现内存泄漏问题。 我们先写一个C++内存泄漏的程序,然后详细讲解如何用GFlags和UMDH发现内存泄漏问题。

int *getmem(void) { int *p = (int *)malloc(32); return p; } int _tmain(int argc, _TCHAR* argv[]) { int i; scanf("%i", &i);//wait for mem leak int *x = getmem();//mem leak here scanf("%i", &i);//wait to exit return 0; }

很明显,这里malloc的内存,没有释放。用VS2010将程序编译为Win32控制台程序:mem_leak.exe。

程序中之所以设置两个scanf,是为了让程序停下来:

启动程序时,不存在内存泄漏,此时可以dump堆信息1输入任意数字,触发内存泄漏,程序还能停在这里,便于dump堆信息2

UMDH的原理,就是对堆信息1和堆信息2做diff,通过diff中的allocate与deallocate次数匹配,就能发现内存泄漏问题

GFlags和UMDH具体使用步骤

参考这个例子进行试验:

https://msdn.microsoft.com/en-us/library/windows/hardware/ff543088(v=vs.85).aspx

实验环境:Windows10 64bit, VS2010

我的具体步骤:

(1)下载WindowsSDK,从这里:https://developer.microsoft.com/zh-cn/windows/downloads/windows-8-1-sdk

(2)运行WindowsSDK安装包,选择“Debugging Tools for Windows”。因为UMDH和GFlags都是Debugging Tools for Windows中的工具。里面含有的工具在这里能看到:https://msdn.microsoft.com/en-us/library/windows/hardware/ff543998(v=vs.85).aspx#installation_directories

(3)UMDH 和 GFlags被安装在C:\Program Files (x86)\Windows Kits\8.1\Debuggers\x64(设置环境变量)

(4)在CMD中,输入gflags /i mem_leak.exe +ust(这是使用UMDH之前的准备工作,具体参考https://msdn.microsoft.com/en-us/library/windows/hardware/ff553431(v=vs.85).aspx)

(5)在CMD中,输入set _NT_SYMBOL_PATH=C:\Windows\symbols(这是使用UMDH之前的准备工作,具体参考https://msdn.microsoft.com/en-us/library/windows/hardware/ff553431(v=vs.85).aspx)

(6)运行mem_leak.exe,在任务管理器找到进程号processed。启动程序时,不存在内存泄漏

(7)在CMD中,执行umdh –p:processed –f:mem_leak.before.dmp(启动程序时,不存在内存泄漏,此时可以dump堆信息1)

(8)在运行的mem_leak.exe中,输入任意数字,触发memory leak行为。触发内存泄漏,程序还能停在这里,便于dump堆信息2。

(9)在CMD中,执行umdh –p:processed –f:mem_leak.after.dmp(dump堆信息2。)

(10)在CMD中,执行umdh -d mem_leak.before.dmp mem_leak.after.dmp > diff.dmp

查看diff.dmp,具体信息如下

// Debug library initialized ... E60000-E65FFF DBGHELP: mem_leak - private symbols & lines .\mem_leak.pdb 77410000-77588FFF DBGHELP: ntdll - export symbols 75A40000-75B2FFFF DBGHELP: KERNEL32 - export symbols 76F60000-770D5FFF DBGHELP: KERNELBASE - export symbols 72E50000-72F0EFFF DBGHELP: MSVCR100 - export symbols 73480000-73502FFF DBGHELP: TmUmEvt - export symbols 76580000-76585FFF DBGHELP: PSAPI - export symbols 76C80000-76CC3FFF DBGHELP: SHLWAPI - export symbols 764C0000-7657DFFF DBGHELP: msvcrt - export symbols 76010000-761C9FFF DBGHELP: combase - export symbols 75E90000-75F3BFFF DBGHELP: RPCRT4 - export symbols 758E0000-758FDFFF DBGHELP: SspiCli - export symbols 758D0000-758D9FFF DBGHELP: CRYPTBASE - export symbols 75870000-758C8FFF DBGHELP: bcryptPrimitives - export symbols 75FC0000-76002FFF DBGHELP: sechost - export symbols 75BF0000-75D3CFFF DBGHELP: GDI32 - export symbols 75D50000-75E8FFFF DBGHELP: USER32 - export symbols 75F40000-75FBAFFF DBGHELP: ADVAPI32 - export symbols 76CD0000-76CFAFFF DBGHELP: IMM32 - export symbols 772F0000-7740FFFF DBGHELP: MSCTF - export symbols 731D0000-732BAFFF DBGHELP: tmmon - export symbols 74300000-74307FFF DBGHELP: VERSION - export symbols // // Each log entry has the following syntax: // // + BYTES_DELTA (NEW_BYTES - OLD_BYTES) NEW_COUNT allocs BackTrace TRACEID // + COUNT_DELTA (NEW_COUNT - OLD_COUNT) BackTrace TRACEID allocations // ... stack trace ... // // where: // // BYTES_DELTA - increase in bytes between before and after log // NEW_BYTES - bytes in after log // OLD_BYTES - bytes in before log // COUNT_DELTA - increase in allocations between before and after log // NEW_COUNT - number of allocations in after log // OLD_COUNT - number of allocations in before log // TRACEID - decimal index of the stack trace in the trace database // (can be used to search for allocation instances in the original // UMDH logs). // + 32 ( 32 - 0) 1 allocs BackTrace9F3A48 + 1 ( 1 - 0) BackTrace9F3A48 allocations ntdll!RtlWalkHeap+188 ntdll!WinSqmEventWrite+D7AE ntdll!RtlAllocateHeap+28 tmmon!SleepMon+273AB MSVCR100!malloc+36 mem_leak!wmain+1E (c:\users\xxx\documents\visual studio 2010\projects\mem_leak\mem_leak\mem_leak.cpp, 17) mem_leak!__tmainCRTStartup+122 (f:\dd\vctools\crt_bld\self_x86\crt\src\crtexe.c, 552) KERNEL32!BaseThreadInitThunk+24 ntdll!RtlSetCurrentTransaction+D4 ntdll!RtlSetCurrentTransaction+9F Total increase == 32 requested + 24 overhead = 56

发现+ 1 ( 1 - 0) BackTrace9F3A48 allocations。这说明有1次allocate内存未释放,还能从diff.dmp中,发现内存泄漏发生在mem_leak.cpp, 17行。

调试无内存泄漏问题的程序

经测试,如果申请的内存都被释放,diff.dmp中是不会出现这样的trace的。将这个例子main中代码改为如下:

int _tmain(int argc, _TCHAR* argv[]) { int i; scanf("%i", &i);//wait for mem leak int *x = getmem();//mem leak here free(x); scanf("%i", &i);//wait to exit return 0; }

继续执行上面使用UMDH的10个步骤,发现diff.dmp中,出去注释,则只有如下一行有价值的信息:

Total decrease == 0 requested + 0 overhead = 0

总结

使用GFlags和UMDH,可以用来发现Windows下C++程序的内存泄漏问题。 但UMDH在Win7中使用,自己一直报异常,我没能成功使用。反而在Win10下,UMDH使用正常。

转载请注明原文地址: https://www.6miu.com/read-64758.html

最新回复(0)