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

来源:互联网 发布:nodejs连接数据库 编辑:程序博客网 时间:2024/06/10 01:35
之前碰到论坛里有几个好友,说程序不时的崩溃,什么xxoo不能read的! 如果光要是这个内存地址,估计你会疯掉~~

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

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

MiniDump.h

[cpp] view plaincopyprint?
  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_; 


MiniDump.cpp

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

 

具体参考方法如下:

1、在CXXDlg::OnInitDialog()中添加这样一段:

[cpp] view plaincopyprint?
  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"); 

测试工程下载地址:

http://download.csdn.net/detail/bb8cbb8c/4370715

原创粉丝点击