Details:
Error address: 0000000000c59e50Heap handle: 0000000000c50000Error type heap_failure_buffer_overrun (6)Parameter 1: 000000000000000aLast known valid blocks: before - 0000000000c59c80, after -0000000000c59fd0Stack trace: 00000000777b79e8: ntdll!RtlpAnalyzeHeapFailure+0x00000000000003a8 000000007774fad6: ntdll!RtlpAllocateHeap+0x0000000000001d2a 00000000777434d8: ntdll!RtlAllocateHeap+0x000000000000016c 00000000777247ea: ntdll!RtlpReAllocateHeap+0x0000000000000648 0000000077723ff2: ntdll!RtlReAllocateHeap+0x00000000000000a2 00000000750c712f: msvcr80!realloc+0x000000000000006f 0000000140010f6f: ProcessA!FunctionA_AnalyzeEventData+0x0000000000000fcf 000000014000f63c: ProcessA!FunctionA_SockWork+0x0000000000000e1c 00000000774e652d: kernel32!BaseThreadInitThunk+0x000000000000000d 000000007771c541: ntdll!RtlUserThreadStart+0x000000000000001dIndex Address Name Debugging options enabled 1: 001f0000 2: 00010000 3: 00020000 4: 00670000 5: 00950000 6: 00c50000 7: 00910000 8: 00bc0000 9: 010e0000 10: 01220000 11: 01420000 12: 00c30000 13: 03660000 14: 00ba0000 15: 037b0000 16: 01340000 17: 039a0000 第三步、使用「!for_each_frame dv /t」打印出错函数的局部变量,找出元凶。 从下面的变量里面找到距离0000000000c59c80地址最近的变量,对了就是它: char * pData_n = 0x00000000`00c59c90 "SE:Security: ???" ※注意如果变量值指针的指针需要先用dc看一下该指针指向的地址。 之后看代码知道,程序在读取pData_n的数据的时候如果遇到是0a(Windos换行符)就自动在后面加上 0d变成0a0d。导致pData_n内存越界了。0:009> !for_each_frame dv /t_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _12 00000000`0548edb0 00000001`40010f6f msvcr80!realloc+0x6f [f:/dd/vctools/crt_bld/self_64_amd64/crt/src/realloc.c @ 332]void * pBlock = 0x00000000`00000000unsigned int64 newsize = 0x548fbe8_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _13 00000000`0548ede0 00000001`4000f63c ProcessA!FunctionA_AnalyzeEventData+0xfcf [e:/ProcessA/FunctionA_sockserv.cpp @ 1666]void * cd = 0xffffffff`ffffffffstruct _MpEvsHead * Head = 0x00000000`0548ff10char * pEventData = 0x00000000`00c97fd0 "???"char ** pNewData = 0x00000000`0548fe48char * SiteName = 0x00000000`0548fe18 ""int oval_check = 0n0char * pszHostIp = 0x00000000`0548fbf0 "192.168.1.1"int j = 0n469int NodeName_check = 0n0char [2068] eventtext = char [2068] "SE:Security: ???"unsigned long err = 0int NL_henkan = 0n1int Evttxt_check = 0n1char [129] nameWork = char [129] "`_???"int ret = 0n0struct NameObject_t * pNameObj_n = 0x00000000`00c5eee8char * pData_n = 0x00000000`00c59c90 "SE:Security: ???"long lWork = 0n9char [257] szTrcBuff = char [257] "safely divided text.([453]bytes --> [469]bytes)"long nNameNum = 0n44long nNewLen = 0n1740struct NameObject_t * pNameObj_o = 0x00000000`00c98028char * pData_o = 0x00000000`00c984c6 "SE:Security: ???"char * pt = 0x00000000`00c59e55 "[???"long i = 0n20int IpAddr_check = 0n0int res = 0n1_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _14 00000000`0548f8a0 00000000`774e652d ProcessA!FunctionA_SockWork+0xe1c [e:/ProcessA/FunctionA_sockserv.cpp @ 1102]void * ns = 0x00000000`000002a0char * pRead_str = 0x00000000`00c562f0 ","int bTableRegisterd = 0n0unsigned long err = 0char [3] traceflg = char [3] ""int ret = 0n0short sWork = 0n2int oval_check = 0n0char * pNewData = 0x00000000`00c5ee90 "???"char * wk = 0x00000000`0548f930 "192.168.1.1"char [33] SiteName = char [33] ""long lWork = 0n2032char [257] szTrcBuff = char [257] "recv event OK"int iLastSerchedIndex = 0n0char [256] HostIp = char [256] "192.168.1.1"int ret2 = 0n0struct _MpEvsHead Head = struct _MpEvsHeadlong nDataLen = 0n3char [257] szTrcBuff2 = char [257] ""char [20] szSendData = char [20] "OK"struct addrinfo hinst = struct addrinfoint conv_disc_set = 0n1long lRc = 0n0void * conv_disc = 0xffffffff`ffffffffint res = 0n1char * pData = 0x00000000`00c97fd0 "???"long nRead = 0n3726char [16] evttype = char [16] "Alarm.sys"char * lpszEventid = 0x00000000`00c5f180 ""long nSend = 0n12char [256] ipTmp = char [256] "192.168.1.1"char [20] szToCode = char [20] "sjis"char [20] szFromCode = char [20] "sjis"int bWriteEvent = 0n1_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _实例2 无效参数(STATUS_INVALID_PARAMETER)。 错误代码:0xc000000d 错误含义:STATUS_INVALID_PARAMETER第一步、先用「!analyze -v」分析出错误的地方以及由于什么原因导致程序Dump掉的。 0:000> !analyze -v******************************************************************************** ** Exception Analysis ** *********************************************************************************** ERROR: Symbol file could not be found. Defaulted to export symbols for user32.dll - Unable to load image C:/Windows/Odsv.dll, Win32 error 0n2*** WARNING: Unable to verify timestamp for Odsv.dll*** ERROR: Module load completed but symbols could not be loaded for Odsv.dllGetPageUrlData failed, server returned HTTP status 404URL requested: http://watson.microsoft.com/StageOne/ProcessB_exe/1_0_0_1/4e362265/msvcr80_dll/8_0_50727_6195/4dcdd833/c000000d/0001d5fa.htm?Retriage=1FAULTING_IP: msvcr80!strncpy_s+10a [f:/dd/vctools/crt_bld/self_64_amd64/crt/src/tcsncpy_s.inl @ 62]00000000`74e6d5fa b822000000 mov eax,22hEXCEPTION_RECORD: ffffffffffffffff -- (.exr 0xffffffffffffffff)ExceptionAddress: 0000000074e6d5fa (msvcr80!strncpy_s+0x000000000000010a) ExceptionCode: c000000d ExceptionFlags: 00000000NumberParameters: 0PROCESS_NAME: ProcessB.exeERROR_CODE: (NTSTATUS) 0xc000000d - <Unable to get error code text>EXCEPTION_CODE: (NTSTATUS) 0xc000000d - <Unable to get error code text>MOD_LIST: <ANALYSIS/>NTGLOBALFLAG: 0
APPLICATION_VERIFIER_FLAGS: 0LAST_CONTROL_TRANSFER: from 0000000000124250 to 0000000074e5b0ecFAULTING_THREAD: ffffffffffffffffDEFAULT_BUCKET_ID: STATUS_INVALID_PARAMETERPRIMARY_PROBLEM_CLASS: STATUS_INVALID_PARAMETERBUGCHECK_STR: APPLICATION_FAULT_STATUS_INVALID_PARAMETERIP_ON_STACK: +2e32faf01dedf5800000000`00124250 60 ???FRAME_ONE_INVALID: 1STACK_TEXT: 00000000`00124220 00000000`00124250 : 00000000`00000006 00000000`00000000 00000000`00000001 00000000`00000000 : msvcr80!_invalid_parameter+0x6c [f:/dd/vctools/crt_bld/self_64_amd64/crt/src/invarg.c @ 88]00000000`00124228 00000000`00000006 : 00000000`00000000 00000000`00000001 00000000`00000000 00000000`00000000 : 0x12425000000000`00124230 00000000`00000000 : 00000000`00000001 00000000`00000000 00000000`00000000 00000000`00124260 : 0x6STACK_COMMAND: ~0s; .ecxr ; kbFOLLOWUP_IP:
msvcr80!strncpy_s+10a [f:/dd/vctools/crt_bld/self_64_amd64/crt/src/tcsncpy_s.inl @ 62]00000000`74e6d5fa b822000000 mov eax,22hFAULTING_SOURCE_CODE: No source found for 'f:/dd/vctools/crt_bld/self_64_amd64/crt/src/tcsncpy_s.inl'SYMBOL_STACK_INDEX: 0SYMBOL_NAME: msvcr80!strncpy_s+10aFOLLOWUP_NAME: MachineOwnerMODULE_NAME: msvcr80IMAGE_NAME: msvcr80.dllDEBUG_FLR_IMAGE_TIMESTAMP: 4dcdd833FAILURE_BUCKET_ID: STATUS_INVALID_PARAMETER_c000000d_msvcr80.dll!strncpy_sBUCKET_ID: X64_APPLICATION_FAULT_STATUS_INVALID_PARAMETER_msvcr80!strncpy_s+10aWATSON_STAGEONE_URL: http://watson.microsoft.com/StageOne/ProcessB_exe/1_0_0_1/4e362265/msvcr80_dll/8_0_50727_6195/4dcdd833/c000000d/0001d5fa.htm?Retriage=1Followup: MachineOwner---------这次运气很不好,从「!analyze -v」打出来的结果来看看不出啥东西来,只知道在调用strncpy_s的时候dmp掉了,无法定位具体是哪个函数出错的原因很多,有可能客户采集的不是全dmp文件或者dmp文件中的栈被破坏了。 这的确很伤脑筋,就针对这个我可是花了3个星期一行行的解析栈里面的内容 才解决的。第二步、先用「!teb」看一下这个程序的栈是从哪里到哪里的。0:000>!tebTEB at 000007ffffeee000 ExceptionList: 0000000000000000 StackBase: 0000000008d50000 StackLimit: 0000000008d4d000 SubSystemTib: 0000000000000000 FiberData: 0000000000001e00 ArbitraryUserPointer: 0000000000000000 Self: 000007ffffeee000 EnvironmentPointer: 0000000000000000 ClientId: 0000000000001bdc . 0000000000001868 RpcHandle: 0000000000000000 Tls Storage: 000007ffffeee058 PEB Address: 000007fffffd6000 LastErrorValue: 87 LastStatusValue: c000000d Count Owned Locks: 0 HardErrorMode: 0第三步、先用「dps」看一下这个程序的栈中的内存的内容。 下面截取其中比较重要的一段。-------------------------------------------------------------------------------------------------------------------------------00000000`001247d8 00000000`74e6d5fa msvcr80!strncpy_s+0x10a [f:/dd/vctools/crt_bld/self_64_amd64/crt/src/tcsncpy_s.inl @ 62]00000000`001247e0 00000000`009c01e000000000`001247e8 00000000`030f581000000000`001247f0 00000000`0057e310 ProcessB2!work ★「ProcessB2!work」的内容本应该是像这样的数据「DNxxxxxxxx_150_109」但是现在「ProcessB2!work」中的内容却是「VIP_rtcrx00184-004a/b-y3b-d」这个。00000000`001247f8 00000000`005782c0 ProcessB2!trcData ▲「ProcessB2!trcData」的内容是「Function:testB call」。 函数List::testB の trace("testB", __FILE__, __LINE__, TRCLV_3);00000000`00124800 00000000`0000000000000000`00124808 00000000`0000000000000000`00124810 00000000`004a3150 ProcessB2!`string' ▲「 ProcessB2!`string'」的内容是「e:/ProcessB/FunctionB.cpp __FILE__」。00000000`00124818 00000000`00455b65 ProcessB2!List::testB+0x55 [e:/ProcessB/Listset.cpp @ 719]00000000`00124820 00000000`009c01e000000000`00124828 00000000`030f581000000000`00124830 00000000`0057e310 ProcessB2!work00000000`00124838 00000000`001249e000000000`00124840 32322e35`322e300000000000`00124848 30614031`33312e3400000000`00124850 7097fb8e`bc92373000000000`00124858 5049565f`5753334c00000000`00124860 00000000`0000125f00000000`00124868 000082bd`b1200d5e00000000`00124870 00000000`009c01e000000000`00124878 00000000`00467bda ProcessB2!FunctionB+0x73a [e:/ProcessB/FunctionB.cpp @ 181] -------------------------------------------------------------------------------------------------------------------------------这里终于定位到是哪个函数出问题。搞清楚这些函数的功能,然后打印出所有可能打印的内容,发现函数传递了一个不合法的数据。在这里要说一下为啥传的数据不合法就会Dmp掉。首先strncpy 这个函数在使用的时候只要有个宏定义(默认是有的)在编译的时候就会使用strncpy_s这个安全函数。详情可以参考下面微软的说明文档。http://msdn.microsoft.com/zh-cn/LIBRARY/ms175759(v=vs.80)其次说明一下为什么会dmp掉。strncpy在使用的时候如果转化成strncpy_s的时候是这样一种形式。char dst[5];strncpy(dst, "a long string", 5); ----> strncpy_s(dst, 5, "a long string", 5);而这样就会到时报STATUS_INVALID_PARAMETER这个错误这是strncpy_s的特性。具体使用方法可以参考下面的文档。http://msdn.microsoft.com/zh-cn/library/5dae5d43(v=vs.90).aspx节选:char dst[5];strncpy_s(dst, 5, "a long string", 5);means that we are asking strncpy_s to copy five characters into a buffer five bytes long; this would leave no space for the null terminator, hence strncpy_s zeroes out the string and calls the invalid parameter handler.If truncation behavior is needed, use _TRUNCATE or (size – 1):strncpy_s(dst, 5, "a long string", _TRUNCATE);strncpy_s(dst, 5, "a long string", 4);详细的ACTIONABLE_HEAP_CORRUPTION_heap_failure_buffer_overrun方法还可以参考以下的例子:http://blogs.msdn.com/b/jiangyue/archive/2010/03/16/windows-heap-overrun-monitoring.aspx========void Crash(void){ int i = 1; int j = 0; i /= j;}void main(void){ Crash();}编译环境:vc++6.0编译器设置: 这一步设置,要求对release版本不使用优化,如果使用优化,上面源代码中Crash(void)函数将不被汇编。这一步设置,产生release版本的调试符号表,为后续定位错误准备。步骤:1、 安装drwtsn32用户可以通过drwtsn32命令,查看dmp文件会被保存在何处。2、 安装windbg,Windbg下载地址:http://www.microsoft.com/whdc/devtools/debugging/default.mspx3、 设置windbgA、符号表路径设置其中;srv*d:/symbolslocal*http://msdl.microsoft.com/download/symbols设置的目的是下载该程序用到的操作系统相关的库函数的符号表到本地。B、源代码路径设置C、dmp文件导入载入dump文件显示如图:clip_image002图1.2 WinDbg界面2、 分析dump文件
若生成的dump文件在本机,dump文件中将包含调试需要的PDB文件及源代码路径,若不在本机,可以通过WinDbg菜单[File] à [Symbol File path] 及 [Source File Path] 分别设置PDB文件路径和源代码路径。如果程序涉及到DLL,需要将EXE、DLL所有涉及的PDB、源代码路径都包括。使用命令:!analyze –v将分析dump文件,并显示程序崩溃处于的代码行:clip_image003图1.3 分析dump 文件========调试技巧 —— 如何利用windbg + dump + map分析程序异常
http://blog.csdn.net/xiaoshahai/article/details/7285103之前碰到论坛里有几个好友,说程序不时的崩溃,什么xxoo不能read的! 如果光要是这个内存地址,估计你会疯掉~~所以分享一下基本的调试技巧,需要准备的工具有WinDbg + VC6.0,下面是自己整理的一份自动生成DUMP文件的源代码,只需要添加到工程即可,源代码如下:MiniDump.h[cpp] view plaincopy #include <windows.h> #include <tlhelp32.h> //#include "dbghelp.h" //#define DEBUG_DPRINTF 1 //allow d() //#include "wfun.h" #pragma optimize("y", off) //generate stack frame pointers for all functions - same as /Oy- in the project #pragma warning(disable: 4200) //nonstandard extension used : zero-sized array in struct/union #pragma warning(disable: 4100) //unreferenced formal parameter /*BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr); int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str); int WINAPI Get_Version_Str(PCHAR Str); PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException); void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);*/ // In case you don't have dbghelp.h. #ifndef _DBGHELP_ typedef struct _MINIDUMP_EXCEPTION_INFORMATION { DWORD ThreadId; PEXCEPTION_POINTERS ExceptionPointers; BOOL ClientPointers; } MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION; typedef enum _MINIDUMP_TYPE { MiniDumpNormal = 0x00000000, MiniDumpWithDataSegs = 0x00000001, } MINIDUMP_TYPE; typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)( IN HANDLE hProcess, IN DWORD ProcessId, IN HANDLE hFile, IN MINIDUMP_TYPE DumpType, IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL IN PVOID UserStreamParam, OPTIONAL IN PVOID CallbackParam OPTIONAL ); #else typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)( IN HANDLE hProcess, IN DWORD ProcessId, IN HANDLE hFile, IN MINIDUMP_TYPE DumpType, IN CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, OPTIONAL IN PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam, OPTIONAL IN PMINIDUMP_CALLBACK_INFORMATION CallbackParam OPTIONAL ); #endif //#ifndef _DBGHELP_ // Tool Help functions. typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID); typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme); extern void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag); extern HMODULE hDbgHelp; extern MINIDUMP_WRITE_DUMP MiniDumpWriteDump_; extern CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_; extern MODULE32_FIRST Module32First_; extern MODULE32_NEST Module32Next_; MiniDump.cpp/* Author: Vladimir Sedach. Purpose: demo of Call Stack creation by our own means, and with MiniDumpWriteDump() function of DbgHelp.dll. */ #include "StdAfx.h" #include "MiniDump.h" #include <Shlwapi.h> #pragma comment(lib,"shlwapi.lib") HMODULE hDbgHelp; MINIDUMP_WRITE_DUMP MiniDumpWriteDump_; CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_; MODULE32_FIRST Module32First_; MODULE32_NEST Module32Next_; #define DUMP_SIZE_MAX 8000 //max size of our dump #define CALL_TRACE_MAX ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40)) //max number of traced calls #define NL "/r/n" //new line extern CString GetExePath(); //**************************************************************************************** BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr) //**************************************************************************************** // Find module by Ret_Addr (address in the module). // Return Module_Name (full path) and Module_Addr (start address). // Return TRUE if found. { MODULEENTRY32 M = {sizeof(M)}; HANDLE hSnapshot; Module_Name[0] = 0; if (CreateToolhelp32Snapshot_) { hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0); if ((hSnapshot != INVALID_HANDLE_VALUE) && Module32First_(hSnapshot, &M)) { do { if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize) { lstrcpyn(Module_Name, M.szExePath, MAX_PATH); Module_Addr = M.modBaseAddr; break; } } while (Module32Next_(hSnapshot, &M)); } CloseHandle(hSnapshot); } return !!Module_Name[0]; } //Get_Module_By_Ret_Addr //****************************************************************** int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str) //****************************************************************** // Fill Str with call stack info. // pException can be either GetExceptionInformation() or NULL. // If pException = NULL - get current call stack. { CHAR Module_Name[MAX_PATH]; PBYTE Module_Addr = 0; PBYTE Module_Addr_1; int Str_Len; typedef struct STACK { STACK * Ebp; PBYTE Ret_Addr; DWORD Param[0]; } STACK, * PSTACK; STACK Stack = {0, 0}; PSTACK Ebp; if (pException) //fake frame for exception address { Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp; Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress; Ebp = &Stack; } else { Ebp = (PSTACK)&pException - 1; //frame addr of Get_Call_Stack() // Skip frame of Get_Call_Stack(). if (!IsBadReadPtr(Ebp, sizeof(PSTACK))) Ebp = Ebp->Ebp; //caller ebp } Str[0] = 0; Str_Len = 0; // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX. // Break trace on wrong stack frame. for (int Ret_Addr_I = 0; (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr)); Ret_Addr_I++, Ebp = Ebp->Ebp) { // If module with Ebp->Ret_Addr found. if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr_1)) { if (Module_Addr_1 != Module_Addr) //new module { // Save module's address and full path. Module_Addr = Module_Addr_1; Str_Len += wsprintf(Str + Str_Len, NL "%08X %s", Module_Addr, Module_Name); } // Save call offset. Str_Len += wsprintf(Str + Str_Len, NL " +%08X", Ebp->Ret_Addr - Module_Addr); // Save 5 params of the call. We don't know the real number of params. if (pException && !Ret_Addr_I) //fake frame for exception address Str_Len += wsprintf(Str + Str_Len, " Exception Offset"); else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD))) { Str_Len += wsprintf(Str + Str_Len, " (%X, %X, %X, %X, %X)", Ebp->Param[0], Ebp->Param[1], Ebp->Param[2], Ebp->Param[3], Ebp->Param[4]); } } else Str_Len += wsprintf(Str + Str_Len, NL "%08X", Ebp->Ret_Addr); } return Str_Len; } //Get_Call_Stack //*********************************** int WINAPI Get_Version_Str(PCHAR Str) //*********************************** // Fill Str with Windows version. { OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)}; //EX for NT 5.0 and later if (!GetVersionEx((POSVERSIONINFO)&V)) { ZeroMemory(&V, sizeof(V)); V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); GetVersionEx((POSVERSIONINFO)&V); } if (V.dwPlatformId != VER_PLATFORM_WIN32_NT) V.dwBuildNumber = LOWORD(V.dwBuildNumber); //for 9x HIWORD(dwBuildNumber) = 0x04xx return wsprintf(Str, NL "Windows: %d.%d.%d, SP %d.%d, Product Type %d", //SP - service pack, Product Type - VER_NT_WORKSTATION,... V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor/*, V.wProductType*/); } //Get_Version_Str //************************************************************* PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException) //************************************************************* // Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str. { PCHAR Str; int Str_Len; int i; CHAR Module_Name[MAX_PATH]; PBYTE Module_Addr; HANDLE hFile; FILETIME Last_Write_Time; FILETIME Local_File_Time; SYSTEMTIME T; Str = new CHAR[DUMP_SIZE_MAX]; if (!Str) return NULL; Str_Len = 0; Str_Len += Get_Version_Str(Str + Str_Len); Str_Len += wsprintf(Str + Str_Len, NL "Process: "); GetModuleFileName(NULL, Str + Str_Len, MAX_PATH); Str_Len = lstrlen(Str); // If exception occurred. if (pException) { EXCEPTION_RECORD & E = *pException->ExceptionRecord; CONTEXT & C = *pException->ContextRecord; // If module with E.ExceptionAddress found - save its path and date. if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr)) { Str_Len += wsprintf(Str + Str_Len, NL "Module: %s", Module_Name); if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE) { if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time)) { FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time); FileTimeToSystemTime(&Local_File_Time, &T); Str_Len += wsprintf(Str + Str_Len, NL "Date Modified: %02d/%02d/%d", T.wMonth, T.wDay, T.wYear); } CloseHandle(hFile); } } else { Str_Len += wsprintf(Str + Str_Len, NL "Exception Addr: %08X", E.ExceptionAddress); } Str_Len += wsprintf(Str + Str_Len, NL "Exception Code: %08X", E.ExceptionCode); if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION) { // Access violation type - Write/Read. Str_Len += wsprintf(Str + Str_Len, NL "%s Address: %08X", (E.ExceptionInformation[0]) ? "Write" : "Read", E.ExceptionInformation[1]); } // Save instruction that caused exception. Str_Len += wsprintf(Str + Str_Len, NL "Instruction: "); for (i = 0; i < 16; i++) Str_Len += wsprintf(Str + Str_Len, " %02X", PBYTE(E.ExceptionAddress)[i]); // Save registers at exception. Str_Len += wsprintf(Str + Str_Len, NL "Registers:"); Str_Len += wsprintf(Str + Str_Len, NL "EAX: %08X EBX: %08X ECX: %08X EDX: %08X", C.Eax, C.Ebx, C.Ecx, C.Edx); Str_Len += wsprintf(Str + Str_Len, NL "ESI: %08X EDI: %08X ESP: %08X EBP: %08X", C.Esi, C.Edi, C.Esp, C.Ebp); Str_Len += wsprintf(Str + Str_Len, NL "EIP: %08X EFlags: %08X", C.Eip, C.EFlags); } //if (pException) // Save call stack info. Str_Len += wsprintf(Str + Str_Len, NL "Call Stack:"); Get_Call_Stack(pException, Str + Str_Len); if (Str[0] == NL[0]) lstrcpy(Str, Str + sizeof(NL) - 1); return Str; } //Get_Exception_Info //************************************************************************************* void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag) //************************************************************************************* // Create dump. // pException can be either GetExceptionInformation() or NULL. // If File_Flag = TRUE - write dump files (.dmz and .dmp) with the name of the current process. // If Show_Flag = TRUE - show message with Get_Exception_Info() dump. { HANDLE hDump_File; PCHAR Str; DWORD Bytes; DWORD nLen = 0; CString strDir,strTXTFile,strDMPFile; CString strDate,strTotal; CTime tm = CTime::GetCurrentTime(); strDir.Format(_T("%s//Log"),GetExePath()); strTXTFile.Format(_T("%s//Log//%04d-%02d-%02d %02d%02d%02d.txt"),GetExePath(),tm.GetYear(),tm.GetMonth(), tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond()); strDMPFile.Format(_T("%s//Log//%04d-%02d-%02d %02d%02d%02d.dmp"),GetExePath(),tm.GetYear(),tm.GetMonth(), tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond()); if(!PathFileExists(strDir)) CreateDirectory(strDir,NULL); Str = Get_Exception_Info(pException); //if (Show_Flag && Str) // MessageBox(NULL, Str, "MiniDump", MB_ICONHAND | MB_OK); if (File_Flag) { if (Str) { hDump_File = CreateFile(strTXTFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); nLen = lstrlen(Str); Str[nLen] = '/0'; WriteFile(hDump_File, Str, lstrlen(Str) + 1, &Bytes, NULL); CloseHandle(hDump_File); } // If MiniDumpWriteDump() of DbgHelp.dll available. if (MiniDumpWriteDump_) { MINIDUMP_EXCEPTION_INFORMATION M; M.ThreadId = GetCurrentThreadId(); M.ExceptionPointers = pException; M.ClientPointers = 0; hDump_File = CreateFile(strDMPFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL); MiniDumpWriteDump_(GetCurrentProcess(), GetCurrentProcessId(), hDump_File, MiniDumpNormal, (pException) ? &M : NULL, NULL, NULL); CloseHandle(hDump_File); } } //if (File_Flag) delete Str; } //Create_Dump 具体参考方法如下:1、在CXXDlg::OnInitDialog()中添加这样一段:SetUnhandledExceptionFilter(CrashReportEx); HMODULE hKernel32; // Try to get MiniDumpWriteDump() address. hDbgHelp = LoadLibrary("DBGHELP.DLL"); MiniDumpWriteDump_ = (MINIDUMP_WRITE_DUMP)GetProcAddress(hDbgHelp, "MiniDumpWriteDump"); // d("hDbgHelp=%X, MiniDumpWriteDump_=%X", hDbgHelp, MiniDumpWriteDump_); // Try to get Tool Help library functions. hKernel32 = GetModuleHandle("KERNEL32"); CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot"); Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32First"); Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32Next"); 测试代码如下:class CTestDlg : public CDialog { // Construction public: CTestDlg(CWnd* pParent = NULL); // standard constructor void Fun1(char *pszBuffer); void Fun2(char *pszBuffer); void Fun3(char *pszBuffer); }; void CTestDlg::Fun1(char *pszBuffer) { Fun2(pszBuffer); } void CTestDlg::Fun2(char *pszBuffer) { Fun3(pszBuffer); } void CTestDlg::Fun3(char *pszBuffer) { pszBuffer[1] = 0x00; } 我们在双击确定按钮时的响应代码如下:void CTestDlg::OnOK() { // TODO: Add extra validation here Fun1(NULL); } 2、设置VC编译选项,勾选生成MAP和Debug Info:3、将编译生成的Release目录中的pdb、map文件保存起来,以后调试会用到:
4、运行程序,单击确定按钮出现异常后自动重启,并创建一个Log文件夹,里面生成dump文件:5、我们打开WinDbg,设置一下pdb路径(File / Symbol File Path):6、用WiinDbg打开dump文件(File / Open Crash Dump)7、输入命令!analyze -v,等待几秒后会打印出错误信息,函数调用栈如下图:OK ,这样我们就能在发布版本的程序中,准确的定位到哪个函数出了问题,所以发布程序时,一定要记得生成pdb、map文件,不然客户运行出错的话,你不死也残!测试工程下载地址:http://download.csdn.NET/source/3575167========
新闻热点
疑难解答