windows下使用umdh定位C++内存泄漏

windows下使用umdh定位C++内存泄漏

如需转载请标明出处:https://blog.csdn.net/itas109
技术交流:129518033

环境:

OS: windows 10(1909 内部版本18363)
windbg: 6.12 x64
UMDH: 6.1.7650

前言

用户模式转储堆 (UMDH) 实用工具与操作系统一起用于分析特定进程 Windows 堆分配。 UMDH 查找特定进程中的哪个例程正在泄漏内存。

1. 安装umdh

windbg x64 v6.12

windbg x86 v6.12

2. 使用umdh截取内存快照

2.1 命令行方式

  • 开启堆栈追踪功能

注意:该功能开始后会影响程序性能,生产环境慎用。

"C:\Program Files\Debugging Tools for Windows (x64)\gflags" -i main.exe +ust
  • 设置应用程序pdb路径(系统pdb路径可选填)
SET _NT_SYMBOL_PATH=D:\MemoryLeakTest\build

SET _NT_SYMBOL_PATH=D:\MemoryLeakTest\build;srv*c:\symbols*http://msdl.microsoft.com/download/symbols
  • 截取当前内存快照
"C:\Program Files\Debugging Tools for Windows (x64)\umdh" -pn:main.exe -f:Snap1.log
  • 程序运行一段时间或执行某些怀疑有泄漏的操作

此处,测试程序main.exe每次按回车,都会申请新内存

  • 截取运行后的快照
"C:\Program Files\Debugging Tools for Windows (x64)\umdh" -pn:main.exe -f:Snap2.log
  • 分析结果

注意:此时可能会下载系统所需pdb,需要等待一会

"C:\Program Files\Debugging Tools for Windows (x64)\umdh" -d Snap1.log Snap2.log -f:result.txt

2.2 批处理方式

env.bat

@echo off

SET APP_NAME=main.exe
SET APP_PDB_PATH=D:\MemoryLeakTest\build
@REM SET SYSTEM_PDB_PATH=srv*c:\symbols*http://msdl.microsoft.com/download/symbols
SET WINDBG_PATH=C:\Program Files\Debugging Tools for Windows (x64)

SET PATH=%WINDBG_PATH%;%PATH%
SET _NT_SYMBOL_PATH=%APP_PDB_PATH%;%SYSTEM_PDB_PATH%

SET CURRENT_PWD=%~dp0

gflags -i %APP_NAME% +ust

start.bat

@echo off

call env.bat

umdh -pn:%APP_NAME% -f:%CURRENT_PWD%\Snap1.log

pause

end.bat

@echo off

call env.bat

umdh -pn:%APP_NAME% -f:%CURRENT_PWD%\Snap2.log
umdh -d %CURRENT_PWD%\Snap1.log %CURRENT_PWD%\Snap2.log -f:%CURRENT_PWD%\result.txt

pause

3. 结果分析

main.cpp第8行有内存泄漏

// Debug library initialized ...
DBGHELP: main - private symbols & lines 
         .\main.pdb
DBGHELP: ntdll - export symbols
DBGHELP: KERNEL32 - export symbols
DBGHELP: KERNELBASE - export symbols
DBGHELP: VCRUNTIME140D - export symbols
DBGHELP: ucrtbased - 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).                                                      
//                                                                          


+    1076 (   1076 -      0)      1 allocs	BackTraceE2CEAC2E
+       1 (      1 -      0)	BackTraceE2CEAC2E	allocations

	ntdll!RtlWalkHeap+00000213
	ntdll!RtlAllocateHeap+00000AEB
	ucrtbased!calloc_base+00001226
	ucrtbased!calloc_base+00000FCD
	ucrtbased!malloc_dbg+0000002F
	ucrtbased!malloc+0000001E
	main!operator new+00000013 (d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\heap\new_scalar.cpp, 35)
	main!operator new[]+00000013 (d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\heap\new_array.cpp, 29)
	main!main+00000038 (d:\MemoryLeakTest\main.cpp, 8)
	main!invoke_main+00000034 (d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, 79)
	main!__scrt_common_main_seh+0000012E (d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, 288)
	main!__scrt_common_main+0000000E (d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_common.inl, 331)
	main!mainCRTStartup+00000009 (d:\agent\_work\2\s\src\vctools\crt\vcstartup\src\startup\exe_main.cpp, 17)
	KERNEL32!BaseThreadInitThunk+00000014
	ntdll!RtlUserThreadStart+00000021


Total increase ==   1076 requested +     44 overhead =   1120

4. 测试程序

main.exe

注意:因为windbg为64位,所以需要将程序编译为64位程序

  • main.cpp
#include <iostream>
int main()
{
while (true)
{
printf("Press Enter to Continue\n");
getchar();
char *s = new char[1024];
}
return 0;
}
  • CMakeLists.txt
cmake_minimum_required(VERSION 2.8.12)

project(main)

add_executable( ${PROJECT_NAME} main.cpp)

License

License under CC BY-NC-ND 4.0: 署名-非商业使用-禁止演绎

如需转载请标明出处:https://blog.csdn.net/itas109
技术交流:129518033


Reference:

  1. 使用 UMDH 查找用户模式内存泄漏

猜你喜欢

转载自blog.csdn.net/itas109/article/details/130303643