WinDbg调试Dump:
用WinDbg调试Dump文件需要三样东西:
- Dump文件
- 源代码,必须与编译可执行文件时的代码一致
- PDB文件,编译时生成的符号文件,一定是要与可执行文件同时生成的!(即便源代码一致,重新生成的PDB文件也不行。)
1)打开WinDbg工具,通过菜单“File”->“Open Crash Dump”打开dmp文件。
2)置符号路径(生成二进制文件时对应的PDB文件所在目录,版本要一致!多个二进制文件的pdb最好生在同一目录):
常用命令:
//加载pdb:
.reload /i /f
//查看pdb是否加载成功:
lm
//自动分析指令(通常分析出的是主线程的堆栈)
!analyze -v
此时通过分析出的堆栈去找有无kernel32!UnhandledExceptionFilter信息
如果有,则此堆栈(主线程堆栈)为异常堆栈;
如果没有,则需查看所有线程堆栈:
//查看所有线程堆栈:
~*kb
程序示例
.h
//reference: http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus
#pragma once
//need function "MiniDumpWriteDump" in Dbghelp.dll which is always existing on Windows XP and later version
//if you want to use it on Windows 2000, you can put Windows XP's Dbghelp.dll in your program dir
//dmp file name will end with ".dmp" in exe dir, see source file for details
namespace exception_catcher
{
//some exception handlers work on per process, others work on per thread
//per process: SEH exception, pure virtual function call, C++ new exception, runtime invalid parameter error, signal: SIGABRT, SIGINT, SIGTERM
//per thread: terminate() call, unexpected() call, signal: SIGFPE, SIGILL, SIGSEGV
void set_process_exception_handlers();
void set_thread_exception_handlers();
}
/*
//
//
//代码修改自http://www.codeproject.com/Articles/207464/Exception-Handling-in-Visual-Cplusplus
//有两处大修改:
// 1. 原代码中GetExceptionPointers的实现是复制自vc8.0,我这里是复制自vc9.0
// 2. 原代码中是动态获取MiniDumpWriteDump函数的地址,我这里是静态链接了Dbghelp.dll
// 因为经我测试,虽然Windows2000的Dbghelp.dll里没有MiniDumpWriteDump函数,但是是可以用WindowsXPSP3的Dbghelp.dll而正常生成dump文件的
// 所以,你最好将WindowsXPSP3的Dbghelp.dll和你的应用程序一同发布。我没有测试其它版本的Dbghelp.dll是否能在Windows2000上用
//
//
//对于示例中的SEH异常,一次会有两个dmp文件,第一个是SIGSEGV的,第二个是SEH的。两个都指示到了正确的异常语句
//对于示例中的terminate钩子,一次会产生两个dmp文件,第一个是terminate的,第二个是SIGABRT的。但是两个都指示到了unexpected()的调用位置???
//对于示例中的unexpected钩子,一次会产生三个dmp文件,第一个是unexpected的,第二个是terminate的,第三个是SIGABRT的。都指示到了unexpected()的调用位置
//对于示例中的纯虚函数调用异常,一次会产生两个dmp文件,第一个是纯虚函数调用的,第二个是SIGABRT的。都指示到了纯虚函数调用的位置
//对于示例中的非法参数调用,一次会产生一个dmp文件,指示位置正确
//对于示例中的new异常,一次会产生两个dmp文件,第一个是new的,指示位置正确,第二个是SEH的,调用栈:AVbadcast->_onexit->_unlockexit->_unlock->LeaveCriticalSection
//对于示例中的SIGABRT信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的SIGFPE信号钩子,我测试的时候除零语句并没有发生除零异常(Release版),可能还要设置什么东西,所以无法验证是否能够捕获到除零异常
//对于示例中的SIGILL信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的SIGINT信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的SIGSEGV信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的SIGTERM信号钩子,一次会产生一个dmp文件,指示位置正确
//对于示例中的RaiseException异常,在不同的code下可能产生不同个数的dmp文件,不过调用栈都是:_onexit->printf
//对于示例中的C++类型异常,一次会产生一个dmp文件,是SEH的,根据类型的不同调用栈不同,但是都没指向正确的代码上???
//
//下面是原代码中的示例程序(稍加修改)
//
#include <float.h>
#include <stdio.h>
#include <signal.h>
#include <conio.h>
#include <stdlib.h>
#include <exception>
#include <Windows.h>
#include "logger.h"
#include "exception_catcher.h"
void sigfpe_test()
{
// Code taken from http://www.devx.com/cplus/Article/34993/1954
//Set the x86 floating-point control word according to what
//exceptions you want to trap.
_clearfp(); //Always call _clearfp before setting the control
//word
//Because the second parameter in the following call is 0, it
//only returns the floating-point control word
unsigned int cw;
_controlfp_s(&cw, 0, 0); //Get the default control
//word
//Set the exception masks off for exceptions that you want to
//trap. When a mask bit is set, the corresponding floating-point
//exception is //blocked from being generating.
cw &=~(EM_OVERFLOW|EM_UNDERFLOW|EM_ZERODIVIDE|
EM_DENORMAL|EM_INVALID);
//For any bit in the second parameter (mask) that is 1, the
//corresponding bit in the first parameter is used to update
//the control word.
unsigned int cwOriginal;
_controlfp_s(&cwOriginal, cw, MCW_EM); //Set it.
//MCW_EM is defined in float.h.
//Restore the original value when done:
//_controlfp(cwOriginal, MCW_EM);
// Divide by zero
float a = 1;
float b = 0;
float c = a/b;
c;
}
#define BIG_NUMBER 0x1fffffff
#pragma warning(disable: 4717) // avoid C4717 warning
int RecurseAlloc()
{
int *pi = new int[BIG_NUMBER];
pi;
RecurseAlloc();
return 0;
}
class CDerived;
class CBase
{
public:
CBase(CDerived *derived): m_pDerived(derived) {};
~CBase();
virtual void function(void) = 0;
CDerived * m_pDerived;
};
#pragma warning(disable:4355)
class CDerived : public CBase
{
public:
CDerived() : CBase(this) {}; // C4355
virtual void function(void) {};
};
CBase::~CBase()
{
m_pDerived -> function();
}
int main()
{
InitLog("", 0, LOG_DEBUG);
exception_catcher::SetProcessExceptionHandlers();
exception_catcher::SetThreadExceptionHandlers();
printf("Choose an exception type:\n");
printf("0 - SEH exception\n");
printf("1 - terminate\n");
printf("2 - unexpected\n");
printf("3 - pure virtual method call\n");
printf("4 - invalid parameter\n");
printf("5 - new operator fault\n");
printf("6 - SIGABRT\n");
printf("7 - SIGFPE\n");
printf("8 - SIGILL\n");
printf("9 - SIGINT\n");
printf("10 - SIGSEGV\n");
printf("11 - SIGTERM\n");
printf("12 - RaiseException\n");
printf("13 - throw C++ typed exception\n");
printf("Your choice > ");
int ExceptionType = 0;
scanf_s("%d", &ExceptionType);
switch(ExceptionType)
{
case 0: // SEH
{
// Access violation
int *p = 0;
#pragma warning(disable : 6011) // warning C6011: Dereferencing NULL pointer 'p'
*p = 0;
#pragma warning(default : 6011)
}
break;
case 1: // terminate
{
// Call terminate
terminate();
}
break;
case 2: // unexpected
{
// Call unexpected
unexpected();
}
break;
case 3: // pure virtual method call
{
// pure virtual method call
CDerived derived;
}
break;
case 4: // invalid parameter
{
char* formatString;
// Call printf_s with invalid parameters.
formatString = NULL;
#pragma warning(disable : 6387) // warning C6387: 'argument 1' might be '0': this does not adhere to the specification for the function 'printf'
printf(formatString);
#pragma warning(default : 6387)
}
break;
case 5: // new operator fault
{
// Cause memory allocation error
RecurseAlloc();
}
break;
case 6: // SIGABRT
{
// Call abort
abort();
}
break;
case 7: // SIGFPE
{
// floating point exception ( /fp:except compiler option)
sigfpe_test();
}
break;
case 8: // SIGILL
{
raise(SIGILL);
}
break;
case 9: // SIGINT
{
raise(SIGINT);
}
break;
case 10: // SIGSEGV
{
raise(SIGSEGV);
}
break;
case 11: // SIGTERM
{
raise(SIGTERM);
}
break;
case 12: // RaiseException
{
// Raise noncontinuable software exception
RaiseException(EXCEPTION_ACCESS_VIOLATION, EXCEPTION_NONCONTINUABLE, 0, NULL);
}
break;
case 13: // throw
{
// Throw typed C++ exception.
throw std::runtime_error("123");
}
break;
default:
{
printf("Unknown exception type specified.");
_getch();
}
break;
}
system("pause");
return 0;
}
*/
.cpp
//if you want to use this class separately,
//you should comment log sentences in "CreateMiniDump" and all handlers,
//and replace CSelfPath with your exe path getting function in "PrepareDumpFilePath"
#include "stdafx.h"
#include <string>
#include <new.h>
#include <signal.h>
#include <Windows.h>
#include <ShellApi.h>
#include <Dbghelp.h>
#include <intrin.h>
#include "exception_catcher.h"
#pragma comment(lib, "Dbghelp.lib")
//copy from invarg.c of VC9.0
#if defined (_AMD64_)
PRUNTIME_FUNCTION
RtlLookupFunctionEntry (
IN ULONG64 ControlPc,
OUT PULONG64 ImageBase,
IN OUT PVOID HistoryTable OPTIONAL
);
PVOID
RtlVirtualUnwind (
IN ULONG HandlerType,
IN ULONG64 ImageBase,
IN ULONG64 ControlPc,
IN PRUNTIME_FUNCTION FunctionEntry,
IN OUT PCONTEXT ContextRecord,
OUT PVOID *HandlerData,
OUT PULONG64 EstablisherFrame,
IN OUT PVOID ContextPointers OPTIONAL
);
#endif
namespace
{
std::string prepare_dump_file_path()
{
char path[MAX_PATH] = { 0 };
::GetModuleFileNameA(NULL, path, MAX_PATH);
for (size_t i = strlen(path) - 1; i >= 0; i--)
{
if (path[i] == '\\' || path[i] == ':')
{
path[i + 1] = 0;
break;
}
}
std::string file_path = path;
file_path += "Log\\";
SYSTEMTIME systime = {0};
GetLocalTime(&systime);
const size_t name_buf_size = 100;
char name_buf[name_buf_size] = {0};
//PS: pid 5 numbers
sprintf_s(name_buf, "SyncDbHelper_%04d%02d%02d%02d%02d%02d%03d_%05d.dmp",
systime.wYear, systime.wMonth, systime.wDay,
systime.wHour, systime.wMinute, systime.wSecond,
systime.wMilliseconds, GetCurrentProcessId());
file_path += name_buf;//buf is large enough to hold string and a null-terminated ch
return file_path;
}
bool create_minidump(EXCEPTION_POINTERS* pExceptionPtrs)
{
bool ret = false;
MINIDUMP_EXCEPTION_INFORMATION dumpinfo;
dumpinfo.ThreadId = GetCurrentThreadId();
dumpinfo.ExceptionPointers = pExceptionPtrs;
dumpinfo.ClientPointers = 0;
std::string file_path = prepare_dump_file_path();
HANDLE hFile = CreateFileA(file_path.c_str(),
GENERIC_WRITE,
0,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL,
NULL);
if (INVALID_HANDLE_VALUE == hFile)
{
//E_LOG(LOGLEVEL_VERBOSE,"CreateFile(for create dump) fail, file path: %s", file_path.c_str());
}
else
{
if (!MiniDumpWriteDump(GetCurrentProcess(),
GetCurrentProcessId(),
hFile,
MiniDumpNormal,
(NULL == pExceptionPtrs) ? NULL : &dumpinfo,
NULL,
NULL))
{
// E_LOG(LOGLEVEL_VERBOSE,"MiniDumpWriteDump fail, file path: %s", file_path.c_str());
}
else
{
ret = true;
}
CloseHandle(hFile);
hFile = INVALID_HANDLE_VALUE;
//string p = kutil::pwd();
//p += "elin_crash_report.exe --type=crash_report";
//ShellExecuteA(NULL, "open", p.c_str(), NULL, NULL, SW_SHOW);
}
return ret;
}
EXCEPTION_POINTERS* get_exception_pointers(const DWORD dwExceptionCode)
{
//copy from function _invoke_watson in invarg.c of VC9.0
EXCEPTION_RECORD ExceptionRecord = {0};
CONTEXT ContextRecord;
#ifdef _X86_
__asm {
mov dword ptr [ContextRecord.Eax], eax
mov dword ptr [ContextRecord.Ecx], ecx
mov dword ptr [ContextRecord.Edx], edx
mov dword ptr [ContextRecord.Ebx], ebx
mov dword ptr [ContextRecord.Esi], esi
mov dword ptr [ContextRecord.Edi], edi
mov word ptr [ContextRecord.SegSs], ss
mov word ptr [ContextRecord.SegCs], cs
mov word ptr [ContextRecord.SegDs], ds
mov word ptr [ContextRecord.SegEs], es
mov word ptr [ContextRecord.SegFs], fs
mov word ptr [ContextRecord.SegGs], gs
pushfd
pop [ContextRecord.EFlags]
}
ContextRecord.ContextFlags = CONTEXT_CONTROL;
#pragma warning(push)
#pragma warning(disable:4311)
ContextRecord.Eip = (ULONG)_ReturnAddress();
ContextRecord.Esp = (ULONG)_AddressOfReturnAddress();
#pragma warning(pop)
ContextRecord.Ebp = *((ULONG *)_AddressOfReturnAddress()-1);
#elif defined (_AMD64_)
ULONG64 ControlPc;
ULONG64 EstablisherFrame;
PRUNTIME_FUNCTION FunctionEntry;
PVOID HandlerData;
ULONG64 ImageBase;
RtlCaptureContext(&ContextRecord);
ControlPc = ContextRecord.Rip;
FunctionEntry = RtlLookupFunctionEntry(ControlPc, &ImageBase, NULL);
if (FunctionEntry != NULL) {
RtlVirtualUnwind(/*UNW_FLAG_NHANDLER*/0x00,
ImageBase,
ControlPc,
FunctionEntry,
&ContextRecord,
&HandlerData,
&EstablisherFrame,
NULL);
} else {
ContextRecord.Rip = (ULONGLONG) _ReturnAddress();
ContextRecord.Rsp = (ULONGLONG) _AddressOfReturnAddress();
}
#elif defined (_IA64_)
/* Need to fill up the Context in IA64. */
RtlCaptureContext(&ContextRecord);
#else /* defined (_IA64_) */
ZeroMemory(&ContextRecord, sizeof(ContextRecord));
#endif /* defined (_IA64_) */
ExceptionRecord.ExceptionCode = dwExceptionCode;
ExceptionRecord.ExceptionFlags = EXCEPTION_NONCONTINUABLE;
ExceptionRecord.ExceptionAddress = _ReturnAddress();
//end copy
EXCEPTION_RECORD* pExceptionRecord = new EXCEPTION_RECORD;
memcpy(pExceptionRecord, &ExceptionRecord, sizeof(EXCEPTION_RECORD));
CONTEXT* pContextRecord = new CONTEXT;
memcpy(pContextRecord, &ContextRecord, sizeof(CONTEXT));
EXCEPTION_POINTERS* pExceptionPointers = new EXCEPTION_POINTERS;
pExceptionPointers->ContextRecord = pContextRecord;
pExceptionPointers->ExceptionRecord = pExceptionRecord;
return pExceptionPointers;
}
//handlers
LONG WINAPI catched_seh_handler(EXCEPTION_POINTERS* pExceptionPtrs)
{
//E_LOG(LOGLEVEL_VERBOSE,"SEH exception occurs");
create_minidump(pExceptionPtrs);
return EXCEPTION_CONTINUE_SEARCH;
}
void __cdecl catched_terminate_handler()
{
//E_LOG(LOGLEVEL_VERBOSE,"terminate call occurs");
create_minidump(get_exception_pointers(0));
}
void __cdecl catched_unexpected_handler()
{
//E_LOG(LOGLEVEL_VERBOSE,"unexpected call occurs");
create_minidump(get_exception_pointers(0));
}
void __cdecl catched_pure_call_handler()
{
//E_LOG(LOGLEVEL_VERBOSE,"pure virtual function call occurs");
create_minidump(get_exception_pointers(0));
}
void __cdecl catched_invalid_parameter_handler(const wchar_t* expression,
const wchar_t* function,
const wchar_t* file,
unsigned int line,
uintptr_t pReserved)
{
//E_LOG(LOGLEVEL_VERBOSE,"invalid parameter call occurs, expression[%ls], function[%ls], file[%s], line[%d]",
// expression, function, file, line);
create_minidump(get_exception_pointers(0));
}
int __cdecl catched_new_handler(size_t)
{
//E_LOG(LOGLEVEL_VERBOSE,"new operator memory allocation exception occurs");
create_minidump(get_exception_pointers(0));
return 0;
}
void catched_sigabrt_handler(int)
{
//E_LOG(LOGLEVEL_VERBOSE,"signal SIGABRT occurs");
create_minidump(get_exception_pointers(0));
}
void catched_sigfpe_handler(int code, int subcode)
{
//E_LOG(LOGLEVEL_VERBOSE,"signal SIGFPE occurs, code[%d], subcode[%d]", code, subcode);
create_minidump(reinterpret_cast<EXCEPTION_POINTERS *>(_pxcptinfoptrs));
}
void catched_sigint_handler(int)
{
//E_LOG(LOGLEVEL_VERBOSE,"signal SIGINT occurs");
create_minidump(get_exception_pointers(0));
}
void catched_sigill_handler(int)
{
// E_LOG(LOGLEVEL_VERBOSE,"signal SIGILL occurs");
create_minidump(get_exception_pointers(0));
}
void catched_sigsegv_handler(int)
{
//E_LOG(LOGLEVEL_VERBOSE,"signal SIGSEGV occurs");
create_minidump(reinterpret_cast<EXCEPTION_POINTERS *>(_pxcptinfoptrs));
}
void catched_sigterm_handler(int)
{
//E_LOG(LOGLEVEL_VERBOSE,"signal SIGTERM occurs");
create_minidump(get_exception_pointers(0));
}
//end handlers
}
void exception_catcher::set_process_exception_handlers()
{
// Install top-level SEH handler
SetUnhandledExceptionFilter(catched_seh_handler);
// Catch pure virtual function calls.
// Because there is one _purecall_handler for the whole process,
// calling this function immediately impacts all threads. The last
// caller on any thread sets the handler.
// http://msdn.microsoft.com/en-us/library/t296ys27.aspx
_set_purecall_handler(catched_pure_call_handler);
// Catch new operator memory allocation exceptions
_set_new_handler(catched_new_handler);
// Catch invalid parameter exceptions.
_set_invalid_parameter_handler(catched_invalid_parameter_handler);
// Set up C++ signal handlers
_set_abort_behavior(_CALL_REPORTFAULT, _CALL_REPORTFAULT);
// Catch an abnormal program termination
signal(SIGABRT, catched_sigabrt_handler);
// Catch illegal instruction handler
signal(SIGINT, catched_sigint_handler);
// Catch a termination request
signal(SIGTERM, catched_sigterm_handler);
}
void exception_catcher::set_thread_exception_handlers()
{
// Catch terminate() calls.
// In a multithreaded environment, terminate functions are maintained
// separately for each thread. Each new thread needs to install its own
// terminate function. Thus, each thread is in charge of its own termination handling
// http://msdn.microsoft.com/en-us/library/t6fk7h29.aspx
set_terminate(catched_terminate_handler);
// Catch unexpected() calls.
// In a multithreaded environment, unexpected functions are maintained
// separately for each thread. Each new thread needs to install its own
// unexpected function. Thus, each thread is in charge of its own unexpected handling.
// http://msdn.microsoft.com/en-us/library/h46t5b69.aspx
set_unexpected(catched_unexpected_handler);
// Catch a floating point error
typedef void (*sigh)(int);
signal(SIGFPE, (sigh)catched_sigfpe_handler);
// Catch an illegal instruction
signal(SIGILL, catched_sigill_handler);
// Catch illegal storage access errors
signal(SIGSEGV, catched_sigsegv_handler);
}
.main
#include "exception_catcher.h"
void main()
{
exception_catcher::set_process_exception_handlers();
exception_catcher::set_thread_exception_handlers();
}
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。