李国帅 2018/2/6
整理以往的调试日志
随着Vc程序规模的不断扩大,内存泄漏问题便成为不得不面对的重要问题,下面总结了几种进行检测的简便方法。像是BoundsChecker这些工具自从v6版本后都变成收费了,虽说挺好用,但是小公司出身的苦哈哈一族就算了。废话不说转入正题。
---------------------
1、使用vld.lib进行检测
对于普通的vc应用程序基本也就够用了,能够检测出基本的内存泄漏,只要不使用特别复杂的内存操作语法。这是一个开源的检测库,可方便的从网上下载。
#ifdef _DEBUG #include "MemLeakDetect.h" #endif /************************************************************************/ /* 调试 */ #ifdef _DEBUG CMemLeakDetect gMemLeakDetect; #endif /************************************************************************/
2、使用vc调试语句
使用vld.lib好像不能对com进行很好的支持,可以简单的使用vc调试语句进行调试,效果也不见得差。使用
#ifdef _DEBUG //只能监视new出来的内存泄漏
#ifndef _CRTDBG_MAP_ALLOC
#define _CRTDBG_MAP_ALLOC //使生成的内存dump包含内存块分配的具体代码为止
#endif
#include <stdlib.h>
#include <crtdbg.h> //for memory leak check
#endif
_CrtSetDbgFlag(_CRTDBG_REPORT_FLAG |_CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
_CrtSetBreakAlloc(187);
...
_CrtDumpMemoryLeaks();
出现一些泄露,原因并不是真正的泄露,比如CString变量判断为泄露,实际上不是因为_CrtDumpMemoryLeaks();调用的时候,程序的堆栈还没有真的的退出。
{317} normal block at 0x01C415F8, 496 bytes long.
Data: <) `> 29 00 00 00 04 D9 1B 00 0E 00 00 00 00 00 00 60
{289} normal block at 0x0002EBC8, 2 bytes long.
Data: < > 88 C3
{270} normal block at 0x0002E828, 40 bytes long.
Data: < D > B8 44 8A 01 01 00 00 00 E8 CD 19 00 FF FF FF FF
f:\rtm\vctools\vc7libs\ship\atlmfc\src\mfc\dllmodul.cpp(133) : {102} client block at 0x00027960, subtype c0, 64 bytes long.
a CDynLinkLibrary object at $00027960, 64 bytes long
{97} client block at 0x00027790, subtype c0, 64 bytes long.
a CDynLinkLibrary object at $00027790, 64 bytes long
f:\rtm\vctools\vc7libs\ship\atlmfc\src\mfc\oleinit.cpp(84) : {91} client block at 0x00026540, subtype c0, 68 bytes long.
a CCmdTarget object at $00026540, 68 bytes long
f:\rtm\vctools\vc7libs\ship\atlmfc\src\mfc\plex.cpp(29) : {89} normal block at 0x00026360, 124 bytes long.
Data: < > 00 00 00 00 00 00 00 00 00 00 00 00 D8 1B 02 00
f:\rtm\vctools\vc7libs\ship\atlmfc\src\mfc\occmgr.cpp(788) : {88} normal block at 0x00021BD8, 4 bytes long.
Data: < : x> DC 3A 1F 78
{65} client block at 0x00021320, subtype c0, 64 bytes long.
a CDynLinkLibrary object at $00021320, 64 bytes long
3、使用umdh查看内存变化
免费的调试工具不多,可以使用vc debug工具集(Debugging Tools for Windows (x86)工具)中使用umdh对比内存是否增长。这个工具相当的方便直接,直接映射运行程序的内存表,然后进行比较。如果进行定位,网上大把的相关教程,不过不用查教程你依然知道内存是否增加了。(windbg可以打印一些信息,不用安装环境。)
输出内存占用,分析vc程序
set _NT_SYMBOL_PATH=d:\mysymbols
gflags -i testplayer.exe +ust
umdh -pn:evclient.exe -f:dump1
umdh -p:2712 -f:dump2
umdh dump1 dump2 -f:Result1.txt
--------------------
4、使用CrashFinder+pdb文件分析崩溃地址
获取PDB文件
要想获取崩溃所在的位置,一定要有调试信息,那么就需要编译程序时生成PDB,map,cod之类的文件,这些文件里包含了进程信息、线程信息、调用堆栈、变量等信息。
如何生成pdb文件( 程序数据库文件)?
配置属性--c/c++ 输出文件-- 汇编输出 = /FAc
--连接器--调试--生成调试信息,生成映射文件,映射导出。
程序发布的时候保存好pdb文件是个好习惯,避免出错也找不到原因。
记录崩溃信息
应用程序在崩溃的时候,会弹出一个框,弹出如下内容:
问题事件名称: APPCRASH
应用程序名: ***.exe
应用程序版本: 1.0.0.2
应用程序时间戳: 51492e42
故障模块名称: ***.ax
故障模块版本: 1.0.0.6
故障模块时间戳: 51492e09
异常代码: c0000005
异常偏移: 0008feb9
使用CrashFinder+pdb文件进行定位
找到偏移位置,故障模块名称: ***.ax和异常偏移: 0008feb9
运行CrashFinder,新建文档输入文件***.ax,ImageBase=0x10000000,所以文件偏移为1008feb9.
输入1008feb9查询Crash,得到
1008FEB9, xxx.AX, CxxxMng::CreateSession, g:\xxx\xxx.cpp line 305
找到新加入的语句:
int *p = NULL;
*p = 100;
只要有pdb文件,定位非常容易。
5、使用崩溃dump文件查找错误的原因。
使用dbghelp.lib或者手动从任务管理器生成dump文件,然后使用vc或者WinDBG打开,也可定位到出错位置,可能效果更好。
如果能把错误信息或者文件一起提交到服务端,那是最好了。
关于如何分析dump文件,就不班门弄斧了,最多也就抄一段。
说在最后,查找问题和定位错误有时候并不是那么容易,可能要想很多办法,并不是一个工具一定能够找到问题,也不是使用工具一下就能找到问题,这是一个细致活。