调试技巧 —— 如何利用windbg + dump + map分析程序异常 .

 之前碰到论坛里有几个好友,说程序不时的崩溃,什么xxoo不能read的! 如果光要是这个内存地址,估计你会疯掉~~

所以分享一下基本的调试技巧,需要准备的工具有WinDbg + VC6.0,

下面是自己整理的一份自动生成DUMP文件的源代码,只需要添加到工程即可,源代码如下:

MiniDump.h

view plain copy to clipboard print ?
  1. #include <windows.h>   
  2. #include <tlhelp32.h>   
  3.   
  4. //#include "dbghelp.h"   
  5. //#define DEBUG_DPRINTF     1   //allow d()   
  6. //#include "wfun.h"   
  7.   
  8. #pragma optimize("y", off)      //generate stack frame pointers for all functions - same as /Oy- in the project   
  9. #pragma warning(disable: 4200)  //nonstandard extension used : zero-sized array in struct/union   
  10. #pragma warning(disable: 4100)  //unreferenced formal parameter   
  11.   
  12. /*BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr); 
  13. int  WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str); 
  14. int  WINAPI Get_Version_Str(PCHAR Str); 
  15. PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException); 
  16. void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);*/  
  17.   
  18. // In case you don't have dbghelp.h.   
  19. #ifndef _DBGHELP_   
  20.   
  21. typedef struct _MINIDUMP_EXCEPTION_INFORMATION {  
  22.     DWORD   ThreadId;  
  23.     PEXCEPTION_POINTERS ExceptionPointers;  
  24.     BOOL    ClientPointers;  
  25. } MINIDUMP_EXCEPTION_INFORMATION, *PMINIDUMP_EXCEPTION_INFORMATION;  
  26.   
  27. typedef enum _MINIDUMP_TYPE {  
  28.     MiniDumpNormal =            0x00000000,  
  29.         MiniDumpWithDataSegs =      0x00000001,  
  30. } MINIDUMP_TYPE;  
  31.   
  32. typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(  
  33.                                             IN HANDLE           hProcess,  
  34.                                             IN DWORD            ProcessId,  
  35.                                             IN HANDLE           hFile,  
  36.                                             IN MINIDUMP_TYPE    DumpType,  
  37.                                             IN CONST PMINIDUMP_EXCEPTION_INFORMATION    ExceptionParam, OPTIONAL  
  38.                                             IN PVOID                                    UserStreamParam, OPTIONAL  
  39.                                             IN PVOID                                    CallbackParam OPTIONAL  
  40.                                             );  
  41.   
  42. #else   
  43.   
  44. typedef BOOL (WINAPI * MINIDUMP_WRITE_DUMP)(  
  45.                                             IN HANDLE           hProcess,  
  46.                                             IN DWORD            ProcessId,  
  47.                                             IN HANDLE           hFile,  
  48.                                             IN MINIDUMP_TYPE    DumpType,  
  49.                                             IN CONST PMINIDUMP_EXCEPTION_INFORMATION    ExceptionParam, OPTIONAL  
  50.                                             IN PMINIDUMP_USER_STREAM_INFORMATION        UserStreamParam, OPTIONAL  
  51.                                             IN PMINIDUMP_CALLBACK_INFORMATION           CallbackParam OPTIONAL  
  52.                                             );  
  53. #endif //#ifndef _DBGHELP_   
  54.   
  55. // Tool Help functions.   
  56. typedef HANDLE (WINAPI * CREATE_TOOL_HELP32_SNAPSHOT)(DWORD dwFlags, DWORD th32ProcessID);  
  57. typedef BOOL (WINAPI * MODULE32_FIRST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);  
  58. typedef BOOL (WINAPI * MODULE32_NEST)(HANDLE hSnapshot, LPMODULEENTRY32 lpme);  
  59.   
  60.   
  61. extern void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag);  
  62.   
  63. extern HMODULE  hDbgHelp;  
  64. extern MINIDUMP_WRITE_DUMP  MiniDumpWriteDump_;  
  65.   
  66. extern CREATE_TOOL_HELP32_SNAPSHOT  CreateToolhelp32Snapshot_;  
  67. extern MODULE32_FIRST   Module32First_;  
  68. extern MODULE32_NEST    Module32Next_;  
#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

view plain copy to clipboard print ?
  1. /* 
  2.     Author: Vladimir Sedach. 
  3.  
  4.     Purpose: demo of Call Stack creation by our own means, 
  5.     and with MiniDumpWriteDump() function of DbgHelp.dll. 
  6. */  
  7.   
  8. #include "StdAfx.h"   
  9. #include "MiniDump.h"   
  10. #include <Shlwapi.h>   
  11.   
  12. #pragma comment(lib,"shlwapi.lib")   
  13.   
  14. HMODULE hDbgHelp;  
  15. MINIDUMP_WRITE_DUMP MiniDumpWriteDump_;  
  16.   
  17. CREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_;  
  18. MODULE32_FIRST  Module32First_;  
  19. MODULE32_NEST   Module32Next_;  
  20.   
  21. #define DUMP_SIZE_MAX   8000    //max size of our dump   
  22. #define CALL_TRACE_MAX  ((DUMP_SIZE_MAX - 2000) / (MAX_PATH + 40))  //max number of traced calls   
  23. #define NL              "\r\n"  //new line   
  24.   
  25. extern CString GetExePath();  
  26.   
  27. //****************************************************************************************   
  28. BOOL WINAPI Get_Module_By_Ret_Addr(PBYTE Ret_Addr, PCHAR Module_Name, PBYTE & Module_Addr)  
  29. //****************************************************************************************   
  30. // Find module by Ret_Addr (address in the module).   
  31. // Return Module_Name (full path) and Module_Addr (start address).   
  32. // Return TRUE if found.   
  33. {  
  34.     MODULEENTRY32   M = {sizeof(M)};  
  35.     HANDLE  hSnapshot;  
  36.   
  37.     Module_Name[0] = 0;  
  38.       
  39.     if (CreateToolhelp32Snapshot_)  
  40.     {  
  41.         hSnapshot = CreateToolhelp32Snapshot_(TH32CS_SNAPMODULE, 0);  
  42.           
  43.         if ((hSnapshot != INVALID_HANDLE_VALUE) &&  
  44.             Module32First_(hSnapshot, &M))  
  45.         {  
  46.             do  
  47.             {  
  48.                 if (DWORD(Ret_Addr - M.modBaseAddr) < M.modBaseSize)  
  49.                 {  
  50.                     lstrcpyn(Module_Name, M.szExePath, MAX_PATH);  
  51.                     Module_Addr = M.modBaseAddr;  
  52.                     break;  
  53.                 }  
  54.             } while (Module32Next_(hSnapshot, &M));  
  55.         }  
  56.   
  57.         CloseHandle(hSnapshot);  
  58.     }  
  59.   
  60.     return !!Module_Name[0];  
  61. //Get_Module_By_Ret_Addr   
  62.   
  63. //******************************************************************   
  64. int WINAPI Get_Call_Stack(PEXCEPTION_POINTERS pException, PCHAR Str)  
  65. //******************************************************************   
  66. // Fill Str with call stack info.   
  67. // pException can be either GetExceptionInformation() or NULL.   
  68. // If pException = NULL - get current call stack.   
  69. {  
  70.     CHAR    Module_Name[MAX_PATH];  
  71.     PBYTE   Module_Addr = 0;  
  72.     PBYTE   Module_Addr_1;  
  73.     int     Str_Len;  
  74.       
  75.     typedef struct STACK  
  76.     {  
  77.         STACK * Ebp;  
  78.         PBYTE   Ret_Addr;  
  79.         DWORD   Param[0];  
  80.     } STACK, * PSTACK;  
  81.   
  82.     STACK   Stack = {0, 0};  
  83.     PSTACK  Ebp;  
  84.   
  85.     if (pException)     //fake frame for exception address   
  86.     {  
  87.         Stack.Ebp = (PSTACK)pException->ContextRecord->Ebp;  
  88.         Stack.Ret_Addr = (PBYTE)pException->ExceptionRecord->ExceptionAddress;  
  89.         Ebp = &Stack;  
  90.     }  
  91.     else  
  92.     {  
  93.         Ebp = (PSTACK)&pException - 1;  //frame addr of Get_Call_Stack()   
  94.   
  95.         // Skip frame of Get_Call_Stack().   
  96.         if (!IsBadReadPtr(Ebp, sizeof(PSTACK)))  
  97.             Ebp = Ebp->Ebp;      //caller ebp   
  98.     }  
  99.   
  100.     Str[0] = 0;  
  101.     Str_Len = 0;  
  102.   
  103.     // Trace CALL_TRACE_MAX calls maximum - not to exceed DUMP_SIZE_MAX.   
  104.     // Break trace on wrong stack frame.   
  105.     for (int Ret_Addr_I = 0;  
  106.         (Ret_Addr_I < CALL_TRACE_MAX) && !IsBadReadPtr(Ebp, sizeof(PSTACK)) && !IsBadCodePtr(FARPROC(Ebp->Ret_Addr));  
  107.         Ret_Addr_I++, Ebp = Ebp->Ebp)  
  108.     {  
  109.         // If module with Ebp->Ret_Addr found.   
  110.         if (Get_Module_By_Ret_Addr(Ebp->Ret_Addr, Module_Name, Module_Addr_1))  
  111.         {  
  112.             if (Module_Addr_1 != Module_Addr)   //new module   
  113.             {  
  114.                 // Save module's address and full path.   
  115.                 Module_Addr = Module_Addr_1;  
  116.                 Str_Len += wsprintf(Str + Str_Len, NL "%08X  %s", Module_Addr, Module_Name);  
  117.             }  
  118.   
  119.             // Save call offset.   
  120.             Str_Len += wsprintf(Str + Str_Len,  
  121.                 NL "  +%08X", Ebp->Ret_Addr - Module_Addr);  
  122.   
  123.             // Save 5 params of the call. We don't know the real number of params.   
  124.             if (pException && !Ret_Addr_I)  //fake frame for exception address   
  125.                 Str_Len += wsprintf(Str + Str_Len, "  Exception Offset");  
  126.             else if (!IsBadReadPtr(Ebp, sizeof(PSTACK) + 5 * sizeof(DWORD)))  
  127.             {  
  128.                 Str_Len += wsprintf(Str + Str_Len, "  (%X, %X, %X, %X, %X)",  
  129.                     Ebp->Param[0], Ebp->Param[1], Ebp->Param[2], Ebp->Param[3], Ebp->Param[4]);  
  130.             }  
  131.         }  
  132.         else  
  133.             Str_Len += wsprintf(Str + Str_Len, NL "%08X", Ebp->Ret_Addr);  
  134.     }  
  135.   
  136.     return Str_Len;  
  137. //Get_Call_Stack   
  138.   
  139. //***********************************   
  140. int WINAPI Get_Version_Str(PCHAR Str)  
  141. //***********************************   
  142. // Fill Str with Windows version.   
  143. {  
  144.     OSVERSIONINFOEX V = {sizeof(OSVERSIONINFOEX)};  //EX for NT 5.0 and later   
  145.   
  146.     if (!GetVersionEx((POSVERSIONINFO)&V))  
  147.     {  
  148.         ZeroMemory(&V, sizeof(V));  
  149.         V.dwOSVersionInfoSize = sizeof(OSVERSIONINFO);  
  150.         GetVersionEx((POSVERSIONINFO)&V);  
  151.     }  
  152.   
  153.     if (V.dwPlatformId != VER_PLATFORM_WIN32_NT)  
  154.         V.dwBuildNumber = LOWORD(V.dwBuildNumber);  //for 9x HIWORD(dwBuildNumber) = 0x04xx   
  155.   
  156.     return wsprintf(Str,  
  157.         NL "Windows:  %d.%d.%d, SP %d.%d, Product Type %d"//SP - service pack, Product Type - VER_NT_WORKSTATION,...   
  158.         V.dwMajorVersion, V.dwMinorVersion, V.dwBuildNumber, V.wServicePackMajor, V.wServicePackMinor/*, V.wProductType*/);  
  159. //Get_Version_Str   
  160.   
  161. //*************************************************************   
  162. PCHAR WINAPI Get_Exception_Info(PEXCEPTION_POINTERS pException)  
  163. //*************************************************************   
  164. // Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str.   
  165. {  
  166.     PCHAR       Str;  
  167.     int         Str_Len;  
  168.     int         i;  
  169.     CHAR        Module_Name[MAX_PATH];  
  170.     PBYTE       Module_Addr;  
  171.     HANDLE      hFile;  
  172.     FILETIME    Last_Write_Time;  
  173.     FILETIME    Local_File_Time;  
  174.     SYSTEMTIME  T;  
  175.       
  176.     Str = new CHAR[DUMP_SIZE_MAX];  
  177.   
  178.     if (!Str)  
  179.         return NULL;  
  180.   
  181.     Str_Len = 0;  
  182.     Str_Len += Get_Version_Str(Str + Str_Len);  
  183.   
  184.     Str_Len += wsprintf(Str + Str_Len, NL "Process:  ");  
  185.     GetModuleFileName(NULL, Str + Str_Len, MAX_PATH);  
  186.     Str_Len = lstrlen(Str);  
  187.   
  188.     // If exception occurred.   
  189.     if (pException)  
  190.     {  
  191.         EXCEPTION_RECORD &  E = *pException->ExceptionRecord;  
  192.         CONTEXT &           C = *pException->ContextRecord;  
  193.   
  194.         // If module with E.ExceptionAddress found - save its path and date.   
  195.         if (Get_Module_By_Ret_Addr((PBYTE)E.ExceptionAddress, Module_Name, Module_Addr))  
  196.         {  
  197.             Str_Len += wsprintf(Str + Str_Len,  
  198.                 NL "Module:  %s", Module_Name);  
  199.   
  200.             if ((hFile = CreateFile(Module_Name, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING,  
  201.                 FILE_ATTRIBUTE_NORMAL, NULL)) != INVALID_HANDLE_VALUE)  
  202.             {  
  203.                 if (GetFileTime(hFile, NULL, NULL, &Last_Write_Time))  
  204.                 {  
  205.                     FileTimeToLocalFileTime(&Last_Write_Time, &Local_File_Time);  
  206.                     FileTimeToSystemTime(&Local_File_Time, &T);  
  207.   
  208.                     Str_Len += wsprintf(Str + Str_Len,  
  209.                         NL "Date Modified:  %02d/%02d/%d",  
  210.                         T.wMonth, T.wDay, T.wYear);  
  211.                 }  
  212.                 CloseHandle(hFile);  
  213.             }  
  214.         }  
  215.         else  
  216.         {  
  217.             Str_Len += wsprintf(Str + Str_Len,  
  218.                 NL "Exception Addr:  %08X", E.ExceptionAddress);  
  219.         }  
  220.           
  221.         Str_Len += wsprintf(Str + Str_Len,  
  222.             NL "Exception Code:  %08X", E.ExceptionCode);  
  223.           
  224.         if (E.ExceptionCode == EXCEPTION_ACCESS_VIOLATION)  
  225.         {  
  226.             // Access violation type - Write/Read.   
  227.             Str_Len += wsprintf(Str + Str_Len,  
  228.                 NL "%s Address:  %08X",  
  229.                 (E.ExceptionInformation[0]) ? "Write" : "Read", E.ExceptionInformation[1]);  
  230.         }  
  231.   
  232.         // Save instruction that caused exception.   
  233.         Str_Len += wsprintf(Str + Str_Len, NL "Instruction: ");  
  234.         for (i = 0; i < 16; i++)  
  235.             Str_Len += wsprintf(Str + Str_Len, " %02X"PBYTE(E.ExceptionAddress)[i]);  
  236.   
  237.         // Save registers at exception.   
  238.         Str_Len += wsprintf(Str + Str_Len, NL "Registers:");  
  239.         Str_Len += wsprintf(Str + Str_Len, NL "EAX: %08X  EBX: %08X  ECX: %08X  EDX: %08X", C.Eax, C.Ebx, C.Ecx, C.Edx);  
  240.         Str_Len += wsprintf(Str + Str_Len, NL "ESI: %08X  EDI: %08X  ESP: %08X  EBP: %08X", C.Esi, C.Edi, C.Esp, C.Ebp);  
  241.         Str_Len += wsprintf(Str + Str_Len, NL "EIP: %08X  EFlags: %08X", C.Eip, C.EFlags);  
  242.     } //if (pException)   
  243.       
  244.     // Save call stack info.   
  245.     Str_Len += wsprintf(Str + Str_Len, NL "Call Stack:");  
  246.     Get_Call_Stack(pException, Str + Str_Len);  
  247.   
  248.     if (Str[0] == NL[0])  
  249.         lstrcpy(Str, Str + sizeof(NL) - 1);  
  250.   
  251.     return Str;  
  252. //Get_Exception_Info   
  253.   
  254. //*************************************************************************************   
  255. void WINAPI Create_Dump(PEXCEPTION_POINTERS pException, BOOL File_Flag, BOOL Show_Flag)  
  256. //*************************************************************************************   
  257. // Create dump.    
  258. // pException can be either GetExceptionInformation() or NULL.   
  259. // If File_Flag = TRUE - write dump files (.dmz and .dmp) with the name of the current process.   
  260. // If Show_Flag = TRUE - show message with Get_Exception_Info() dump.   
  261. {  
  262.     HANDLE  hDump_File;  
  263.     PCHAR   Str;  
  264.     DWORD   Bytes;  
  265.     DWORD   nLen = 0;  
  266.   
  267.     CString strDir,strTXTFile,strDMPFile;  
  268.     CString strDate,strTotal;  
  269.     CTime   tm = CTime::GetCurrentTime();  
  270.       
  271.     strDir.Format(_T("%s\\Log"),GetExePath());  
  272.     strTXTFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.txt"),GetExePath(),tm.GetYear(),tm.GetMonth(),  
  273.         tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());  
  274.     strDMPFile.Format(_T("%s\\Log\\%04d-%02d-%02d %02d%02d%02d.dmp"),GetExePath(),tm.GetYear(),tm.GetMonth(),  
  275.         tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());  
  276.   
  277.     if(!PathFileExists(strDir))  
  278.         CreateDirectory(strDir,NULL);  
  279.   
  280.     Str = Get_Exception_Info(pException);  
  281.   
  282.     //if (Show_Flag && Str)   
  283.     //  MessageBox(NULL, Str, "MiniDump", MB_ICONHAND | MB_OK);   
  284.   
  285.     if (File_Flag)  
  286.     {  
  287.         if (Str)  
  288.         {  
  289.             hDump_File = CreateFile(strTXTFile,  
  290.                 GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  
  291.               
  292.             nLen = lstrlen(Str);  
  293.             Str[nLen] = '\0';  
  294.   
  295.             WriteFile(hDump_File, Str, lstrlen(Str) + 1, &Bytes, NULL);  
  296.   
  297.             CloseHandle(hDump_File);  
  298.         }  
  299.   
  300.         // If MiniDumpWriteDump() of DbgHelp.dll available.   
  301.         if (MiniDumpWriteDump_)  
  302.         {  
  303.             MINIDUMP_EXCEPTION_INFORMATION  M;  
  304.   
  305.             M.ThreadId = GetCurrentThreadId();  
  306.             M.ExceptionPointers = pException;  
  307.             M.ClientPointers = 0;  
  308.   
  309.             hDump_File = CreateFile(strDMPFile,  
  310.                 GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);  
  311.   
  312.             MiniDumpWriteDump_(GetCurrentProcess(), GetCurrentProcessId(), hDump_File,  
  313.                 MiniDumpNormal, (pException) ? &M : NULL, NULL, NULL);  
  314.   
  315.             CloseHandle(hDump_File);  
  316.         }  
  317.     } //if (File_Flag)   
  318.   
  319.     delete Str;  
  320. //Create_Dump  
/* 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()中添加这样一段:

view plain copy to clipboard print ?
  1. SetUnhandledExceptionFilter(CrashReportEx);  
  2. HMODULE hKernel32;  
  3.   
  4. // Try to get MiniDumpWriteDump() address.   
  5. hDbgHelp = LoadLibrary("DBGHELP.DLL");  
  6. MiniDumpWriteDump_ = (MINIDUMP_WRITE_DUMP)GetProcAddress(hDbgHelp, "MiniDumpWriteDump");  
  7. //  d("hDbgHelp=%X, MiniDumpWriteDump_=%X", hDbgHelp, MiniDumpWriteDump_);   
  8.   
  9. // Try to get Tool Help library functions.   
  10. hKernel32 = GetModuleHandle("KERNEL32");  
  11. CreateToolhelp32Snapshot_ = (CREATE_TOOL_HELP32_SNAPSHOT)GetProcAddress(hKernel32, "CreateToolhelp32Snapshot");  
  12. Module32First_ = (MODULE32_FIRST)GetProcAddress(hKernel32, "Module32First");  
  13. Module32Next_ = (MODULE32_NEST)GetProcAddress(hKernel32, "Module32Next");  
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");

测试代码如下:

view plain copy to clipboard print ?
  1. class CTestDlg : public CDialog  
  2. {  
  3. // Construction   
  4. public:  
  5.     CTestDlg(CWnd* pParent = NULL); // standard constructor   
  6.   
  7.     void Fun1(char *pszBuffer);  
  8.     void Fun2(char *pszBuffer);  
  9.     void Fun3(char *pszBuffer);  
  10. };  
class CTestDlg : public CDialog { // Construction public: CTestDlg(CWnd* pParent = NULL); // standard constructor void Fun1(char *pszBuffer); void Fun2(char *pszBuffer); void Fun3(char *pszBuffer); };
view plain copy to clipboard print ?
  1. void CTestDlg::Fun1(char *pszBuffer)  
  2. {  
  3.     Fun2(pszBuffer);  
  4. }  
  5.   
  6. void CTestDlg::Fun2(char *pszBuffer)  
  7. {  
  8.     Fun3(pszBuffer);  
  9. }  
  10.   
  11. void CTestDlg::Fun3(char *pszBuffer)  
  12. {  
  13.     pszBuffer[1] = 0x00;  
  14. }  
void CTestDlg::Fun1(char *pszBuffer) { Fun2(pszBuffer); } void CTestDlg::Fun2(char *pszBuffer) { Fun3(pszBuffer); } void CTestDlg::Fun3(char *pszBuffer) { pszBuffer[1] = 0x00; }
我们在双击确定按钮时的响应代码如下:

view plain copy to clipboard print ?
  1. void CTestDlg::OnOK()   
  2. {  
  3.     // TODO: Add extra validation here   
  4.     Fun1(NULL);  
  5. }  
void CTestDlg::OnOK() { // TODO: Add extra validation here Fun1(NULL); }

2、设置VC编译选项,勾选生成MAP和Debug Info:

调试技巧 —— 如何利用windbg + dump + map分析程序异常 ._第1张图片


3、将编译生成的Release目录中的pdb、map文件保存起来,以后调试会用到:

4、运行程序,单击确定按钮出现异常后自动重启,并创建一个Log文件夹,里面生成dump文件:

5、我们打开WinDbg,设置一下pdb路径(File \ Symbol File Path):

调试技巧 —— 如何利用windbg + dump + map分析程序异常 ._第2张图片

6、用WiinDbg打开dump文件(File \ Open Crash Dump)

调试技巧 —— 如何利用windbg + dump + map分析程序异常 ._第3张图片

7、输入命令!analyze -v,等待几秒后会打印出错误信息,函数调用栈如下图:

调试技巧 —— 如何利用windbg + dump + map分析程序异常 ._第4张图片


OK ,这样我们就能在发布版本的程序中,准确的定位到哪个函数出了问题,所以发布程序时,一定要记得生成pdb、map文件,不然客户运行出错的话,你不死也残!


测试工程下载地址:

http://download.csdn.net/source/3575167

 

 

http://blog.csdn.net/wangningyu/article/details/6748138

你可能感兴趣的:(调试技巧 —— 如何利用windbg + dump + map分析程序异常 .)