写程序的人都有这个苦恼,用户说你的程序总有时会莫名奇妙的崩溃,可是你怎么也无法重现崩溃的场景,所以也无法找出程序中的Bug,难道就束手无策吗?
前几天在一本杂志(《程序员-游戏创造》)上看到一篇关于这个主题的文章,非常详细的论述了怎样通过代码和工具实现Bug Report,使得你可以快速定位崩溃的源代码行数。
要catch未处理的异常,该文中论述了使用全局的try-catch的优缺点,全局的try-catch导致了调试工作的麻烦,不可取。Win32异常的可以通过设置过滤函数来处理,而C++也有set_terminate函数可以设置一个函数来处理任何未catch的异常。
文章还告诉我们,main/WinMain是程序的入口处,但静态初始化在这之前进行,操作系统装载我们的程序后,先调用的是CRT提供函数WinMainCRTStartup/mainCRTStartup,可以通过VC开发工具设置程序的入口函数来获取异常的优先控制权(/ENTRY:函数)(Linker->Advanced->Entry Point)。
1.用minidump进行调试
这个技术是改进Windows操作系统的错误报告机制的关键。产生的文件非常小,适合于网络传输。 有三种方法创建一个minidump文件: a. 在你的应用程序中加入未处理的exception处理函数,函数中创建minidump文件 b. Visual Studio .NET集成开发环境中调试时,Debug菜单中按Save Dump c. Windows XP 在一个程序遇到未处理的异常时会自动产生minidump文件,但直接提交给微软,你没机会处理。
要使用该技术,build的时候要生成完全的调试信息文件(PDB),发布每个版本时要保存每个可执行文件(exe/dll)以及相对应的pdb文件,以备将来debug用。为了更好的辅助文件匹配,注意要正确设置每个可执行文件的内部版本号,每次发布要使用不同的版本号,然而debugger使用的是PE文件头中内部时间蹉来匹配的。在发布版本中生成调试信息有副作用,占用更多空间并且容易被逆向工程。
写minidump的API是MiniDumpWriteDump,在PlatformSDK中的DBGHelp.dll,只有XP版本的没有问题。为了调用这个API,你必须使用SetUnhandledExceptionFilter API来设置一个未处理异常处理器,用来catch崩溃,但在VS2005之前,仍然会无法catch纯C++异常。
在你的函数中,注意要导入正确的DbgHelp.dll,使用LoadLibrary会默认载入System32目录下的dll,Win2000将会导致错误。
分析用户传回的minidump文件,使用Visual Studio .Net打开(*.dmp,*.mdmp)并创建缺省的项目。按F5在Output窗口中显示载入的模块信息。重建所有进程状态,但可能缺少symbols以及调试信息。所以你需要所有匹配的exe和dll文件以及pdb文件,要查看所有的module打开Modules窗口,找出所有(通常没必要所有)匹配的dll(DLL Help database: http://support.microsoft.com/servicedesks/fileversion/dllinfo.asp.),或者你需要操作系统的安装盘或者从报告者的系统中得到所有匹配的dll,拷贝到本地的一个目录下,例如D:/Mudules。然后设置项目的Debugging页中的Command参数为MODPATH=D:/Mudules,按F5重新载入minidump。注意你也需要每个dll匹配的pdb文件,平台的可以在操作系统安装盘或者
Operating system Files required Windows NT 4 DBGs Windows 2000 DBGs, PDBs Windows XP PDBs
如果你要处理很多minidumps,可能需要所有的dll和pdb/dbg,这时你可以使用Symbol Server来管理所有资源,首先到
http://www.microsoft.com/ddk/debugging/symbols.asp
下载调试工具,拷贝Symsrv.dll到VS.Net可以访问的地方,建一个本地目录,例如:C:/localstore,在项目属性对话框中设置Debugging页中Symbol路径为SRV*c:/localstore*http://msdl.microsoft.com/download/symbols。此后,每次F5后就会自动下载必要的文件。
原文:
http://www.codeproject.com/debug/postmortemdebug_standalone1.asp 例子:
http://www.codeproject.com/debug/crash_report.asp
2. map文件 生成map文件来定位崩溃的代码行 原文:
http://www.codeproject.com/debug/mapfile.asp
3. CrashFinder
John Robbins 写好的CrashFinder能够发现VC++/VB的应用程序的崩溃处(代码行)。Release版本也要生成Debug信息,在VC中使用/Zi 编译选项(C/C++ tab中Program Database )和/DEBUG and /PDB:<pdb filename>链接选项(Link tab中打勾Debug info 并选择Microsoft format),VB中在项目属性打勾Compile to Native Code 和Create Symbolic Debug Info 选项。
如果你在Debugger的OUtput窗口中看到LDR: Dll xxx base 10000000 relocated due to collision with yyy信息,可以通过REBASE.EXE(Platform SDK)程序使得你可以重新安排载入dll/ocx地址,因为不同的系统载入dll/ocx的地址不同,如果冲突会导致你找不出到底是哪个dll崩溃。
如果是beta版,你可以让应用程序生成详细Dr. Watson 信息(要很大空间不适合传输),你可以在安装应用程序时检测系统是否已经安装Dr. Watson(在注册表HKEY_LOCAL_MACHINE/SOFTWARE/ Microsoft/Windows NT/CurrentVersion/AeDebug 处),有了这种信息你可以得到详细的堆栈空间并游刃有余地找出崩溃的地方。
CrashFinder使用IMAGEHLP.DLL符号引擎(Windows NT® 4.0首次引入),Windows NT 5.0 SDK已经可以处理源码行号。(CrashFinder2.1已经升级,使用最新的DBGHelp.dll)
CrashFinder只保存应用程序和调试信息文件的目录信息,所以无需每次编译都需要CrashFinder连编。你可以创建多个CrashFinder项目,每个项目针对一种操作系统。
原文:
http://www.microsoft.com/msj/0498/bugslayer0498.aspx 下载:
http://www.wintellect.com/about/instructors/robbins/code.aspx
解决方案:
(To be continue)
Trackback: http://tb.blog.csdn.net/TrackBack.aspx?PostId=658660