Debug系列:VC++程序Release版崩溃的解决办法
2015-09-22 14:02
405 查看
from: http://rendao.org/blog/1999/
Setp1:在需要生成dump文件的工程中引用包含MiniDump.h,并且在程序初始化时调用minidump_startup, 程序退出时调用minidump_cleanup
Step2: 在工程release设置中,VC6下务必保证勾选了“产生调试信息”和“产生MAP文件”,VC2012下进入“配置属性|链接器|全部选项”,“生成调试信息”选择“是”,“生成映射文件”选择“是”,“引用”选择“是(/OPT:REF) ”。
Step3: 编译后生成的pdb、源码文件都需要保存,以便以后可辅助分析dump文件。本文的崩溃分析方法中未用到map文件,据说pdb就够了,但最好还是保存下来备用。
miniDump.h
#ifndef MINIDUMP_DEF
#define MINIDUMP_DEF
#include <windows.h>
#include <tlhelp32.h>
#include "StdAfx.h"
//#include "dbghelp.h"
//#define DEBUG_DPRINTF 1
#define CRASHREPORT_DIR "CrashReport"
#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)
4000
(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_;
//崩溃后的回调函数,在这里创建dump文件
inline LONG WINAPI CrashReportEx(LPEXCEPTION_POINTERS ExceptionInfo)
{
char szFileName[MAX_PATH] = {0x00};
UINT nRet = 0;
// 重启程序,也可以不重启,因为如果是启动时程序就崩溃的话,将产生多个dump文件
//::GetModuleFileName(NULL, szFileName, MAX_PATH);
//nRet = WinExec(szFileName, SW_SHOW);
// 创建DUMP文件
Create_Dump(ExceptionInfo,1,1);
return EXCEPTION_EXECUTE_HANDLER;
}
//对外接口,一般在程序初始化时调用此函数,然后此程序即拥有了崩溃后生成dump文件的功能
inline void* minidump_startup()
{
SetUnhandledExceptionFilter(CrashReportEx); //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");
return hDbgHelp;
}
inline void minidump_cleanup(void* handle)
{
if( handle )
{
FreeLibrary( (HMODULE)handle );
}
}
#endif
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 <afx.h> //CString
#include <AfxWin.h> //AfxGetInstanceHandle
#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
CString GetExePath()
{
char sFileName[256] = {0};
CString sPath = _T("");
GetModuleFileName(AfxGetInstanceHandle(), sFileName, 255);
sPath.Format("%s", sFileName);
int pos = sPath.ReverseFind('\\');
if(pos != -1) sPath = sPath.Left(pos);
else sPath = _T("");
return sPath;
}
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 stanbsp;ck 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\\%s"),GetExePath(),CRASHREPORT_DIR);
strTXTFile.Format(_T("%s\\%s\\%04d-%02d-%02d
%02d-%02d-%02d.txt"),GetExePath(),CRASHREPORT_DIR,tm.GetYear(),tm.GetMonth(),
tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());
strDMPFile.Format(_T("%s\\%s\\%04d-%02d-%02d
%02d-%02d-%02d.dmp"),GetExePath(),CRASHREPORT_DIR,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
补充说明
据说此法无法检测内存溢出、内存覆盖的错误,我简单验证过,基本是这样,比如:
遇到这样的错误崩溃,会生成dump文件
char* s=NULL;
s[1] = ‘a’;
遇到这样的错误崩溃,可能不会生成dump文件,
char s[10];
sprintf_s(s,10,”hello%d”,”abcdefghijklmnopqrstuvwxyz”);
遇到这样的错误崩溃,可能不会生成dump文件
char s[10];
sprintf_s(s,10,”hello%s”,”abcdefghijklmnopqrstuvwxyz”);
打开WinDbg -> 设置pdb路径(File|Symbol File Path) -> 设置源代码路径(File|Source File Path) -> 设置Exe路径(File|Image File Path) -> 打开dump文件(File|Open Crash Dump)。
如果只设置pdb文件路径,也是可以显示出错信息的,只是不够详细。
Step2: 分析定位
7、输入命令!analyze -v,等待几秒后会打印出错误信息,函数调用栈,并定位到出错代码
OK ,这样我们就能在发布版本的程序中,准确的定位到哪个函数出了问题
Debug系列:VC++程序Release版崩溃的解决办法
VC++程序发布后,如果在客户那运行崩溃,且研发环境下无法本地复现,工程师可能就要去现场,如果在现场也只是偶发的bug,去了现场也是抓狂。那,有什么办法能便于我们解决VC++程序生产环境下崩溃的问题呢?常用方法,为你的程序加入Dump机制。
网上流传已久的miniDump方法是可行的,某国外程序员写的miniDump.h和miniDump.cpp,把这个模块加入你的VC++工程就可以了。miniDump模块添加办法如下:Setp1:在需要生成dump文件的工程中引用包含MiniDump.h,并且在程序初始化时调用minidump_startup, 程序退出时调用minidump_cleanup
Step2: 在工程release设置中,VC6下务必保证勾选了“产生调试信息”和“产生MAP文件”,VC2012下进入“配置属性|链接器|全部选项”,“生成调试信息”选择“是”,“生成映射文件”选择“是”,“引用”选择“是(/OPT:REF) ”。
Step3: 编译后生成的pdb、源码文件都需要保存,以便以后可辅助分析dump文件。本文的崩溃分析方法中未用到map文件,据说pdb就够了,但最好还是保存下来备用。
miniDump.h
#ifndef MINIDUMP_DEF
#define MINIDUMP_DEF
#include <windows.h>
#include <tlhelp32.h>
#include "StdAfx.h"
//#include "dbghelp.h"
//#define DEBUG_DPRINTF 1
#define CRASHREPORT_DIR "CrashReport"
#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)
4000
(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_;
//崩溃后的回调函数,在这里创建dump文件
inline LONG WINAPI CrashReportEx(LPEXCEPTION_POINTERS ExceptionInfo)
{
char szFileName[MAX_PATH] = {0x00};
UINT nRet = 0;
// 重启程序,也可以不重启,因为如果是启动时程序就崩溃的话,将产生多个dump文件
//::GetModuleFileName(NULL, szFileName, MAX_PATH);
//nRet = WinExec(szFileName, SW_SHOW);
// 创建DUMP文件
Create_Dump(ExceptionInfo,1,1);
return EXCEPTION_EXECUTE_HANDLER;
}
//对外接口,一般在程序初始化时调用此函数,然后此程序即拥有了崩溃后生成dump文件的功能
inline void* minidump_startup()
{
SetUnhandledExceptionFilter(CrashReportEx); //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");
return hDbgHelp;
}
inline void minidump_cleanup(void* handle)
{
if( handle )
{
FreeLibrary( (HMODULE)handle );
}
}
#endif
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 | #ifndef MINIDUMP_DEF #define MINIDUMP_DEF #include <windows.h> #include <tlhelp32.h> #include "StdAfx.h" //#include "dbghelp.h" //#define DEBUG_DPRINTF 1 #define CRASHREPORT_DIR "CrashReport" #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_ typedefstruct _MINIDUMP_EXCEPTION_INFORMATION{ DWORD ThreadId; PEXCEPTION_POINTERSExceptionPointers; BOOLClientPointers; }MINIDUMP_EXCEPTION_INFORMATION,*PMINIDUMP_EXCEPTION_INFORMATION; typedefenum _MINIDUMP_TYPE{ MiniDumpNormal= 0x00000000, MiniDumpWithDataSegs= 0x00000001, } MINIDUMP_TYPE; typedef BOOL(WINAPI* MINIDUMP_WRITE_DUMP)( INHANDLE hProcess, INDWORD ProcessId, INHANDLE hFile, INMINIDUMP_TYPE DumpType, INCONST PMINIDUMP_EXCEPTION_INFORMATIONExceptionParam,OPTIONAL INPVOID UserStreamParam,OPTIONAL INPVOID CallbackParam OPTIONAL ); #else typedef BOOL(WINAPI* MINIDUMP_WRITE_DUMP)( INHANDLE hProcess, INDWORD ProcessId, INHANDLE hFile, INMINIDUMP_TYPE DumpType, INCONST PMINIDUMP_EXCEPTION_INFORMATIONExceptionParam,OPTIONAL INPMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,OPTIONAL INPMINIDUMP_CALLBACK_INFORMATION CallbackParamOPTIONAL ); #endif //#ifndef _DBGHELP_ // Tool Help functions. typedef HANDLE(WINAPI* CREATE_TOOL_HELP32_SNAPSHOT)(DWORDdwFlags,DWORD th32ProcessID); typedefBOOL (WINAPI* MODULE32_FIRST)(HANDLEhSnapshot,LPMODULEENTRY32 lpme); typedef BOOL(WINAPI* MODULE32_NEST)(HANDLEhSnapshot,LPMODULEENTRY32 lpme); extern voidWINAPI Create_Dump(PEXCEPTION_POINTERSpException,BOOL File_Flag,BOOL Show_Flag); extern HMODULEhDbgHelp; externMINIDUMP_WRITE_DUMP MiniDumpWriteDump_; externCREATE_TOOL_HELP32_SNAPSHOT CreateToolhelp32Snapshot_; extern MODULE32_FIRSTModule32First_; externMODULE32_NEST Module32Next_; //崩溃后的回调函数,在这里创建dump文件 inline LONGWINAPI CrashReportEx(LPEXCEPTION_POINTERSExceptionInfo) { charszFileName[MAX_PATH]= {0x00}; UINTnRet =0; // 重启程序,也可以不重启,因为如果是启动时程序就崩溃的话,将产生多个dump文件 //::GetModuleFileName(NULL, szFileName, MAX_PATH); //nRet = WinExec(szFileName, SW_SHOW); // 创建DUMP文件 Create_Dump(ExceptionInfo,1,1); returnEXCEPTION_EXECUTE_HANDLER; } //对外接口,一般在程序初始化时调用此函数,然后此程序即拥有了崩溃后生成dump文件的功能 inlinevoid*minidump_startup() { SetUnhandledExceptionFilter(CrashReportEx);//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"); returnhDbgHelp; } inlinevoid minidump_cleanup(void*handle) { if(handle ) { FreeLibrary((HMODULE)handle); } } #endif |
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 <afx.h> //CString
#include <AfxWin.h> //AfxGetInstanceHandle
#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
CString GetExePath()
{
char sFileName[256] = {0};
CString sPath = _T("");
GetModuleFileName(AfxGetInstanceHandle(), sFileName, 255);
sPath.Format("%s", sFileName);
int pos = sPath.ReverseFind('\\');
if(pos != -1) sPath = sPath.Left(pos);
else sPath = _T("");
return sPath;
}
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 stanbsp;ck 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\\%s"),GetExePath(),CRASHREPORT_DIR);
strTXTFile.Format(_T("%s\\%s\\%04d-%02d-%02d
%02d-%02d-%02d.txt"),GetExePath(),CRASHREPORT_DIR,tm.GetYear(),tm.GetMonth(),
tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond());
strDMPFile.Format(_T("%s\\%s\\%04d-%02d-%02d
%02d-%02d-%02d.dmp"),GetExePath(),CRASHREPORT_DIR,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 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 | /* Author: Vladimir Sedach. Purpose: demo of Call Stack creation by our own means, and with MiniDumpWriteDump() function of DbgHelp.dll. */ #include "StdAfx.h" #include <afx.h> //CString #include <AfxWin.h> //AfxGetInstanceHandle #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 CStringGetExePath() { charsFileName[256]= {0}; CString sPath= _T(""); GetModuleFileName(AfxGetInstanceHandle(),sFileName,255); sPath.Format("%s",sFileName); intpos =sPath.ReverseFind('\\'); if(pos!= -1)sPath =sPath.Left(pos); elsesPath =_T(""); returnsPath; } BOOLWINAPI Get_Module_By_Ret_Addr(PBYTERet_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. { MODULEENTRY32M ={sizeof(M)}; HANDLEhSnapshot; 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 WINAPIGet_Call_Stack(PEXCEPTION_POINTERSpException,PCHAR Str) // Fill Str with call stanbsp;ck info. // pException can be either GetExceptionInformation() or NULL. // If pException = NULL - get current call stack. { CHARModule_Name[MAX_PATH]; PBYTE Module_Addr= 0; PBYTEModule_Addr_1; intStr_Len; typedefstruct STACK { STACK* Ebp; PBYTERet_Addr; DWORD Param[0]; }STACK,* PSTACK; STACKStack ={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(intRet_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"); elseif (!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); } returnStr_Len; } //Get_Call_Stack int WINAPIGet_Version_Str(PCHARStr) // Fill Str with Windows version. { OSVERSIONINFOEXV ={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 returnwsprintf(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_POINTERSpException) // Allocate Str[DUMP_SIZE_MAX] and return Str with dump, if !pException - just return call stack in Str. { PCHARStr; intStr_Len; inti; CHARModule_Name[MAX_PATH]; PBYTEModule_Addr; HANDLE hFile; FILETIMELast_Write_Time; FILETIME Local_File_Time; SYSTEMTIMET; Str= newCHAR[DUMP_SIZE_MAX]; if(!Str) returnNULL; 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); returnStr; }//Get_Exception_Info voidWINAPI Create_Dump(PEXCEPTION_POINTERSpException,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. { HANDLEhDump_File; PCHAR Str; DWORDBytes; DWORD nLen= 0; CString strDir,strTXTFile,strDMPFile; CStringstrDate,strTotal; CTime tm= CTime::GetCurrentTime(); strDir.Format(_T("%s\\%s"),GetExePath(),CRASHREPORT_DIR); strTXTFile.Format(_T("%s\\%s\\%04d-%02d-%02d %02d-%02d-%02d.txt"),GetExePath(),CRASHREPORT_DIR,tm.GetYear(),tm.GetMonth(), tm.GetDay(),tm.GetHour(),tm.GetMinute(),tm.GetSecond()); strDMPFile.Format(_T("%s\\%s\\%04d-%02d-%02d %02d-%02d-%02d.dmp"),GetExePath(),CRASHREPORT_DIR,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_INFORMATIONM; 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 |
补充说明
据说此法无法检测内存溢出、内存覆盖的错误,我简单验证过,基本是这样,比如:
遇到这样的错误崩溃,会生成dump文件
char* s=NULL;
s[1] = ‘a’;
遇到这样的错误崩溃,可能不会生成dump文件,
char s[10];
sprintf_s(s,10,”hello%d”,”abcdefghijklmnopqrstuvwxyz”);
遇到这样的错误崩溃,可能不会生成dump文件
char s[10];
sprintf_s(s,10,”hello%s”,”abcdefghijklmnopqrstuvwxyz”);
Dump文件有了,那如何使用Dump文件来排错呢?
Step1:四个准备文件打开WinDbg -> 设置pdb路径(File|Symbol File Path) -> 设置源代码路径(File|Source File Path) -> 设置Exe路径(File|Image File Path) -> 打开dump文件(File|Open Crash Dump)。
如果只设置pdb文件路径,也是可以显示出错信息的,只是不够详细。
Step2: 分析定位
7、输入命令!analyze -v,等待几秒后会打印出错误信息,函数调用栈,并定位到出错代码
OK ,这样我们就能在发布版本的程序中,准确的定位到哪个函数出了问题
Related posts:
VC++下tinyXML的使用相关文章推荐
- 用C输出乘法口诀表
- C语言与C++中点运算符与箭头运算符的区别
- Effective C++——条款51(第8章)
- c++设置输出精度
- C语言封装disruptor
- C++高并发
- .stl文件(CAD三维模型)格式 及 基于C/C++的数据读取
- c语言不定参数探究
- c语言学习笔记(3)const和volatile以及const volatile int i=0分析
- C++中的**this
- [C/C++]do/while的巧妙用法
- C++ 日期 & 时间
- 一份c/c++写日志到处都能用的代码
- C++ 引用
- 【C语言】摄氏温度转化为相应的华氏温度
- C++中memset函数
- 黑马程序员---C语言学习笔记之分支结构if语句与switc语句的详解
- 用C语言实现Prim算法及测试用例
- C++ 读写MySQL经典 (转载)
- c++ 用宏定义一个函数