您的位置:首页 > 编程语言 > C语言/C++

Debug系列:VC++程序Release版崩溃的解决办法

2015-09-22 14:02 405 查看
from: http://rendao.org/blog/1999/

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的使用
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: