windows平台使用UMDH工具检测内存泄漏

原创,欢迎转载,请注明出处

前言

最近在Windows平台上扩展ovs-vswitchd的功能时,遇到了内存泄漏的问题。查阅windbg的文档,使用UMDH工具解决。特此记录一下。

本文使用的工具随EWDK_rs3_release_16299_170928-1534开发套件发行,VS是否带有该工具请自行查阅。本文提及的工具在EWDK的安装目录的“Program Files\Windows Kits\10\Debuggers\x64\”文件夹中。本文的操作都是在控制台下执行的。默认系统的PATH变量中不包含这些命令所在的目录,请自行添加或执行命令时输入全路径。

准备

创建用户空间程序栈跟踪数据库(Create user mode stack trace database)

使用gflags命令设置,命令格式如下:

1
"{EWDK安装目录}\Program Files\Windows Kits\10\Debuggers\x64\gflags" /i ImageName +ust

注意:ImageName是指被检测的程序的名称,包含扩展名。例如:glags /i main.exe +ust.

设置环境变量_NT_SYMBOL_PATH

Windows调试工具(WinDbg, KD, CDB, NTST)通过_NT_SYMBOL_PATH查找程序的符号表。在控制台下设置该变量的语法如下:

1
set _NT_SYMBOL_PATH=c:\myapp\symbols;srv*c:\mycache*https://msdl.microsoft.com/download/symbols

  • 命令中的“c:\myapp\symbols”的路径中包含被检测的文件的pdb文件。
  • set命令设置环境变量的作用范围是当前的console会话。如需,该环境变量永久有效,请使用setx命令。

UMDH使用

使用UMDH收集程序的运行时信息

命令语法如下:

1
"{EWDK安装目录}\Program Files\Windows Kits\10\Debuggers\x64\umdh.exe" -p:{程序的PID} -f:log0

通常UMDH命令会被执行至少两次。注意日志请使用不同的名字。

使用UMDH分析程序运行时行为

命令语法:

1
"{EWDK安装目录}\Program Files\Windows Kits\10\Debuggers\x64\umdh.exe" log0 log1

接下来通过一个例子看一下UMDH能给我们什么样的信息。

示例

创建一个内存泄漏程序

  • 程序代码如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#include <windows.h>
#include <stdio.h>

#define ARRAY_SIZE 128

int main(void)
{
int * pointers[ARRAY_SIZE];

for(unsigned int i = 0; i < -1; ++i){
pointers[i % ARRAY_SIZE] = malloc(4);
if(NULL == pointers[i % ARRAY_SIZE]){
printf("no memory available");
}

Sleep(1);
}
}
  • 编译程序的命令如下,生成的程序名为main.exe
1
cl /EHsc /Zi main.c

注意编译时私用/Zi开关,生成pdb文件。该文件在分析程序行为时,被用于定位具体的代码行。

  • /Z7
    编译时,生成.obj文件,其包含可被调试工具使用的符号信息。

  • /Zi
    生成程序数据文件(PDB),该文件包含可被调试工具使用的类型信息和符号信息。

  • /ZI
    与/Zi开关的作用相似,生成PDB文件。不同的是,PDB文件的格式不同。此PDB文件支持“Edit”和“Continue”。

准备工作

  • 创建main.exe的栈跟踪数据库
1
glags /i main.exe +ust
  • 设置_NT_SYMBOL_PATH环境变量
1
set _NT_SYMBOL_PATH=e:\workspace\test;srv*c:\mycache*https://msdl.microsoft.com/download/symbols

“e:\workspace\test”目录下包含main.pdb文件

收集数据

  • 运行main.exe
1
main.exe
  • 使用UMDH收集运行时行为
    在控制台下运行下面的命令,收集main.exe的运行时行为,记录存储到“log0”文件中
1
umdh.exe -p:2230 -f:log0
几秒钟后,再次运行umdh收集程序行为信息,记录存储到“log1”文件中
1
umdh.exe -p:2230 -f:log1

分析数据

  • 命令如下
1
umdh.exe -v log1 log0

命令的输出结果如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
-     988 (  1600 -  1f88)    580 allocs        BackTrace94D0588E
- 262 ( 580 - 7e2) BackTrace94D0588E allocations

ntdll!RtlpCallInterceptRoutine+40
ntdll!RtlAllocateHeap+7892C
main!_malloc_base+44 (minkernel\crts\ucrt\src\appcrt\heap\malloc_base.cpp, 34)
main!main+3E (c:\workspace\test\main.c, 11)
main!__scrt_common_main_seh+110 (f:\dd\vctools\crt\vcstartup\src\startup\exe_common.inl, 283)
KERNEL32!BaseThreadInitThunk+22
ntdll!RtlUserThreadStart+34


Total decrease == 988 requested + 68d8 overhead = 7260

输出结果中的第七行指出了内存泄漏发生的位置。如果程序中不知一处有内存泄漏,则umdh会输出每一个发生内存泄漏的代码行。

后记 - “一个空格引发的血案”

写作过程中,由于块代码(block code)使用“```”标识的。由于块代码的结束标记“```”多了一个空格导致格式始终显示不对。预期的结果是这样:

1
aaa
1
bbb

多余的空格导致显示如下:

1
2
3
4
aaa
```
```text
bbb

经验不足,这个问题花了较长的时间才解决。