使用Windbg定位Windows C++程序中的内存泄露
Posted link-初扬
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用Windbg定位Windows C++程序中的内存泄露相关的知识,希望对你有一定的参考价值。
目录
最近有个客户在使用我们的Windows软件时又遇到了内存泄露问题,软件在客户的机器环境上运行半个多小时后就会出现闪退崩溃。去年我们也遇到过类似的问题,很大概率是第三方安全软件导致的,第三方安全库注入到我们的进程中,应该是注入的库有内存泄露,导致我们的进程出现问题。本文简单讲述一下如何使用Windbg来定位Windows程序中的内存泄露问题。
1、概述
Windows平台上分析内存泄露的工具有很多,比如Rational Purify、BoundsChecker等。但这些工具已经好几年不再维护了,老的版本已不再兼容较新的Visual Studio了。即便能兼容,也是比较麻烦的,比如BoundsChecker就需要将头文件和库添加到工程中重新编译程序,大型软件中包含了多个模块,在不确定发生内存泄露的模块的情况下,在所有模块中都添加BoundsChecker的文件重新编译一下是不现实的。
我们需要一个工具,在不需要重新编译程序的情况下,就能直接去分析程序是否存在内存泄露。之前一直在寻找这样的工具,一直没有找到合适的。使用过腾讯的tMemoryMonitor内存监测工具,但该工具并不好用,很多实际场景下的内存泄露都监测不到。
最后只能使用Windbg去检测内存泄露。Windbg是微软提供的Windows下强大的调试工具,可以分析多种软件异常问题,在我们日常开发过程中会频繁地使用到该工具,但之前使用Windbg监测内存泄露问题还是第一次。目前我们已经使用Windbg定位了好几起内存泄露的问题,Windbg还是比较给力的。
2、使用Windbg监测内存泄露的一般步骤
在监测之前,需要安装新版本的Windbg,因为我们实际上是使用Windbg安装目录下的gflags.exe和umdh.exe两个程序去完成内存监测的。
新版本的Windbg已经合并到Windows SDK包中,要安装Windbg,则需要下载Windows SDK开发包,在安装时可以选择只安装Windbg,操作起来有点小麻烦。我已经将10.0版本的Windbg安装包上传至CSDN上,直接去下载安装即可,其链接为:https://download.csdn.net/download/chenlycly/34256522https://download.csdn.net/download/chenlycly/34256522https://download.csdn.net/download/chenlycly/34256522
使用gflags.exe和umdh.exe监测内存泄露的一般步骤如下:
1)右键以管理员权限启动cmd命令行窗口,用cd命令,切换到windbg的安装目录中,比如我的安装路径是:C:\\Program Files\\Windows Kits\\10\\Debuggers\\x86。
2)在cmd中输入:gflags /i XXX.exe +ust。
3)在cmd中输入:umdh.exe -pn:XXX.exe -f:E:\\log1.txt,等待命令执行完成。
4)让软件运行一会,操作软件使之产生内存泄露。
5)在cmd中输入:umdh.exe -pn:XXX.exe -f:E:\\log2.txt,等待命令执行完成。
6)在cmd中输入:umdh.exe E:\\log1.txt E:\\log2.txt -f:E:\\result.txt,等待命令执行完成。
7)查看result.txt文件中的堆内存使用变化的统计,定位问题。
3、详解整个操作过程
3.1、gflags.exe和umdh.exe介绍
gflags.exe和umdh.exe是新版本Windbg安装路径下的程序。
gflags.exe (Global Flags) ,全局标志编辑器 ,用来启用和禁用高级调试、诊断和故障排除功能。 它通常用于打开其他工具跟踪、计数和日志的指示器。可以通过命令行执行其相关的操作,也可以在gflags的UI界面上设置:
umdh.exe(User-Mode Dump Heap),用户转储堆程序,用来追踪并分析进程的堆内存分配。 对于每个堆内存的分配,umdh都可以追踪其分配的大小、开销的大小、指向分配的指针和分配堆栈。 如果进程具有多个活动内存堆,umdh将追踪所有堆。 此类分析可以实时展示,也可以保存在日志文件中。该工具程序是没有UI界面,是通过cmd命令行执行相关操作的。
3.2、启动cmd命令行
一般我们需要右键以管理员权限运行cmd命令行窗口,特别是在Win10系统中。然后用cd命令,切换到windbg的安装目录中,比如我的安装路径是:C:\\Program Files\\Windows Kits\\10\\Debuggers\\x86,以方便下面在命令行中直接启动该路径下的gflags.exe和umdh.exe两个程序。
3.3、设置pdb符号库路径
umdh最终会分析出使用堆内存的函数调用堆栈,为了方便查看到函数调用堆栈中的具体函数,可以设置pdb符号库文件路径。我们可以在命令行中设置如下的环境变量:
set _NT_SYMBOL_PATH="D:\\MySymbols;srv*C:\\WINDOWS\\Symbols*http://msdl.microsoft.com/download/symbols"
环境变量的名称为_NT_SYMBOL_PATH,其值是包含pdb的路径。路径包含两部分,前半部分的D:\\MySymbols是我们软件模块的符号库文件,后半部分则是微软系统库在线下载的符号库路径。
如果不想设置微软系统库在线pdb路径,可以只设置软件自己模块的pdb文件。除了在命令行中添加环境变量,也可以在系统中添加环境变量。以win10系统为例,在系统搜索栏中搜索环境变量,找到编辑环境变量的入口,依次操作即可:
3.4、调用gflags设置启用udmh.exe的堆栈跟踪
继续在cmd命令行中输入:
gflags /i XXX.exe +ust
其中/i用来指定目标进程的名称,参数ust的含义是:Create user mode stack trace database。此命令用来给目标进程创建用户模式堆栈跟踪数据库,用于追踪堆内存的使用情况。
3.5、第一次使用umdh抓取堆内存使用快照
继续在cmd命令行中输入:
umdh.exe -pn:XXX.exe -f:E:\\log1.txt
其中-pn参数用来指定目标进程的进程名,-f参数用来指定存放抓取来的堆内存使用快照数据信息。此命令运行后,将第一次抓取的堆内存使用快照数据保存到E:\\log1.txt中。
输入该命令后,让程序运行一会,尽可能运行的时间长一会,保证在这段时间内产生足够多的内存泄露,这样windbg的监测结果更准确一些。
3.6、第二次使用umdh抓取堆内存使用快照
继续在cmd中输入如下的命令:
umdh.exe -pn:XXX.exe -f:E:\\log2.txt
命令说明同上。此命令运行后,将第二次抓取的堆内存使用快照数据保存到E:\\log2.txt中。
3.7、比较两次堆内存使用快照,得出结论
最后在cmd中输入:
umdh.exe E:\\log1.txt E:\\log2.txt -f:E:\\result.txt
即比较log1.txt 和log2.txt两文件中的堆内存使用变化量,得出统计数据,将统计结果保存到E:\\result.txt文件中。打开E:\\result.txt,即可查看到堆内存的变化情况,按堆内存申请数量从高到底排列,每一项统计结果都有详细的函数调用堆栈,一般我们只需要分析使用量较高的几项即可。一般第一项就是发生内存泄露的堆栈,比如:
4、Windbg分析内存泄露的不足
Windbg只能监测两个时间点的申请堆内存的变化量,并没有去统计释放的堆内存,这一点和Linux上排查内存泄露的神器Valgrind要差一些。所以Windbg统计出来的结果中,排在第一位的项可能并不是内存泄露的项,需要我们去结合代码将其他几项过滤掉,最终确定发生内存泄露的那一项。
以上是关于使用Windbg定位Windows C++程序中的内存泄露的主要内容,如果未能解决你的问题,请参考以下文章