gflags 监控内存分配、检查内存泄露

转:https://blog.csdn.net/Scarlett_OHara/article/details/89399078

       https://blog.csdn.net/ybdesire/article/details/71304364

       https://blog.csdn.net/listener51/article/details/78106185

想要知道程序从开始到运行一段时间内存的使用情况,可以使用gflags工具。

1. windbg同级目录下有gflags.exe,点开后进行如下设置。

2. 在gflags.exe的目录下直接打开命令窗口

输入glags.exe /i myTest.exe +ust

       set _NT_SYMBOL_PATH=srv*D:\winSymbol*http://msdl.microsoft.com/download/symbols;D:\pdb

       (D:\winSymbol存储是下载的windows的调试库,联网状态下自动下载   D:\pdb是myTest.exe的pdb所在目录)

       在程序入口处输入umdh -p:A -f:d:\1.log  或者umdh -pn:A1 -f:d:\1.log         (A代表程序的PID,A1代表程序名)

       程序运行一段时间后输入umdh -p:B -f:d:\2.log

       然后再输入umdh -d -v d:\1.log d:\2.log > d:\12.log

      就可以在12.log里面看到哪里在分配内存了。如果pdb加载成功是可以看到代码具体行号的。
 

 

简单来说,就是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;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
很明显,这里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
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
发现+ 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;
}
1
2
3
4
5
6
7
8
9
继续执行上面使用UMDH的10个步骤,发现diff.dmp中,出去注释,则只有如下一行有价值的信息:

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

 

1、下载地址:http://www.technlg.net/windows/windgb-download/

注意:最好下载x64位的安装包,因为32位的安装包可能会安装不成功。

 

2、环境配置

计算机-》右击-》属性-》高级系统设置-》环境-》系统变量-》Path-》编辑-》变量值    处后加入:

c:\Program Files\Debugging Tools for Windows(x64)

注意:此路劲请参考个人的安装路径。

 

3、利用工具umdh(user-mode dump heap)分析

(1)由于在安装路径下有 gflags.exe、umdh.exe 这两个可执行文件,且在步骤2中已加入环境变量,下一步可直接在dos界面输入命令;

(2)桌面左下角-》开始 处输入cmd命令,打开dos界面窗口后,输入命令:gflags.exe,会出现下图,并按照下图进行相关设置:

(3)gflags标志设置好后,开启cmd

键入要定位内存泄露的程序gflags.exe /I memroyleak.exe(程序名称)+ust

如图成功后,开启memoryleak.exe程序,即运行需要测试的程序。

 

4、利用umdh创建heap快照

命令格式:umdh–pn:memoryleak.exe(程序名称) –f:snap1.log(日志名称)

程序运行一段时间后或者程序占用内存增加时,将memoryleak.exe退出。

然后再次创建heap快照,命令行无差别,snap1.log改为snap2.log或者其他。

设置好程序的符号路径,如下图:

设置好后可以开始分析heap前后两个快照的差异

分析差异命令:umdh –d snap1.log snap2.log –f:result.txt

分析完成后查看结果result.txt,可以指定具体的输出文件夹,不指定的话,输出在桌面的个人文件夹《以自己名字命名的文件夹》

红色为umdh定位出来的泄露点,我们在查看源代码

这样我们就可以修改代码中内存泄露的地方了。

 

注意:利用Umdh创建Heap快照 步骤设置程序的符号路径,此符号路径是一个在线网址,因此需要电脑联网;对于我们来说只能通过下载符号离线包的方式,离线包的路径为:

https://developer.microsoft.com/en-us/windows/hardware/download-symbols

关键词:Download Windows Symbol Packages

但是不下载此包,符号路径会自动设置为 %windir%\symbols 即windows自带的符号路径,目前未发现问题

 

参考网址:http://blog.csdn.net/chenyujing1234/article/details/11918987

 

windows下内存泄漏--新大陆

1、检测是否存在内存泄漏

   Windows平台下面Visual Studio 调试器和C运行时(CRT)库为我们提供了检测和识别内存泄漏的有效方法,原理大致如下:内存分配要通过CRT在运行时实现,只要在分配内存和释放内存时分别做好记录,程序结束时对比分配内存和释放内存的记录就可以确定是不是有内存泄漏。在vs中启用内存检测的方法如下:

(1)、程序文件开头处包含以下语句,不要更改顺序

 

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

              通过包括crtdbg.h,将malloc和free函数映射到它们的调试版本,即_malloc_dbg和 _free_dbg,这两个函数将跟踪内存分配和释放。 此映射在调试版本(在其中定义了_DEBUG)中发生。 发布版本使用普通的 malloc 和 free 函数。

                 #define 语句将 CRT 堆函数的基版本映射到对应的“Debug”版本。 并非绝对需要该语句;但如果没有该语句,内存泄漏转储包含的有用信息将较少。
(2)、在添加了上述语句之后,可以通过在程序中包括以下语句(通常应恰好放在程序退出位置之前)来转储内存泄漏信息:

 

_CrtDumpMemoryLeaks();
 

示例代码:

 

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
 
int main(int argc,char** argv)
{
    char *str = (char*)malloc(20 * sizeof(char));
    _CrtDumpMemoryLeaks();
    return 0;
}

         当在调试器下运行程序时(按F5),_CrtDumpMemoryLeaks将在VS最下栏 “输出”窗口 -》显示输出来源-》调试 中显示内存泄漏信息。如果没有使用#Define _CRTDBG_MAP_ALLOC语句,输出窗口则不显示内存泄漏的具体位置。
注意:
        如果程序总是在同一位置退出,调用 _CrtDumpMemoryLeaks 将非常容易。如果程序从多个位置退出,则无需在每个可能退出的位置放置对 _CrtDumpMemoryLeaks 的调用,而可以在程序开始处包含以下调用:
_CrtSetDbgFlag ( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
      该语句在程序退出时自动调用 _CrtDumpMemoryLeaks。必须同时设置 _CRTDBG_ALLOC_MEM_DF 和 _CRTDBG_LEAK_CHECK_DF两个位域。
示例代码:

 

 

 

 

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
 
int main(int argc,char** argv)
{
    char *str = (char*)malloc(20 * sizeof(char));
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
    return 0;
}

 

 

#define _CRTDBG_MAP_ALLOC
#include <stdlib.h>
#include <crtdbg.h>
 
 
void check_mem()
{
    _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF);
}
 
int main(int argc,char** argv)
{
    char *str = (char*)malloc(20 * sizeof(char));
    atexit(check_mem);
    return 0;
}
 

注:上述方法在测试调用的第三方库是否存在内存泄漏时,并不能显示泄露函数在哪个文件、哪一行,只会简单的显示,如:

 

 

Detected memory leaks!  
Dumping objects ->  
{223} normal block at 0x003CF650, 4 bytes long.  
 Data: <  < > E8 F6 3C 00  
    此时,需要在程序的入口处加入:

 

 

_CrtSetBreakAlloc(223);
   在vs中按F5调试,在堆栈信息中就可以看到泄露的具体位置。其中223表示{223} normal block at 0x003CF650, 4 bytes long.
 

参考网址:https://msdn.microsoft.com/en-us/library/x98tx3cf.aspx
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值