461 lines
15 KiB
C++
461 lines
15 KiB
C++
#include "stdafx.h"
|
|
#include <malloc.h>
|
|
#include "File.h"
|
|
#include "../Minidump/MiniDump.h"
|
|
|
|
|
|
TCHAR MiniDump::m_filename[MAX_PATH];
|
|
TCHAR MiniDump::m_folername[MAX_PATH];
|
|
|
|
CONTEXT MiniDump::m_stackContext;
|
|
IMAGEHLP_LINE64 MiniDump::m_stackLine;
|
|
STACKFRAME64 MiniDump::m_stackFrame;
|
|
BYTE MiniDump::m_stackSymbol[SYM_BUFF_SIZE];
|
|
TCHAR MiniDump::m_dumpText[BUFF_SIZE];
|
|
BOOL MiniDump::m_symEngInit = FALSE;
|
|
|
|
|
|
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(
|
|
HANDLE hProcess,
|
|
DWORD dwPid,
|
|
HANDLE hFile,
|
|
MINIDUMP_TYPE DumpType,
|
|
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
|
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
|
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
|
|
|
|
|
LPTOP_LEVEL_EXCEPTION_FILTER PreviousExceptionFilter = NULL;
|
|
|
|
|
|
LONG WINAPI UnHandledExceptionFilter(struct _EXCEPTION_POINTERS *exceptionInfo)
|
|
{
|
|
// 콜스택저장
|
|
MiniDump::PrintCallStack(exceptionInfo);
|
|
|
|
// Windows 2000 이전에는 따로 DBGHELP를 배포해서 설정해 주어야 한다.
|
|
HMODULE DllHandle = LoadLibrary(_T("DBGHELP.DLL"));
|
|
if (DllHandle)
|
|
{
|
|
MINIDUMPWRITEDUMP MiniDumpWriteDump = reinterpret_cast<MINIDUMPWRITEDUMP>(GetProcAddress(DllHandle, "MiniDumpWriteDump"));
|
|
if (MiniDumpWriteDump)
|
|
{
|
|
TCHAR dumpPath[MAX_PATH] = {0};
|
|
|
|
SYSTEMTIME SystemTime;
|
|
GetLocalTime(&SystemTime);
|
|
|
|
_sntprintf_s(
|
|
dumpPath,
|
|
_countof(dumpPath),
|
|
MAX_PATH,
|
|
_T("%s\\%s(%04d%02d%02d-%02d%02d%02d).dmp"),
|
|
MiniDump::m_folername,
|
|
MiniDump::m_filename,
|
|
SystemTime.wYear,
|
|
SystemTime.wMonth,
|
|
SystemTime.wDay,
|
|
SystemTime.wHour,
|
|
SystemTime.wMinute,
|
|
SystemTime.wSecond);
|
|
|
|
HANDLE hDumpFile = CreateFile(
|
|
dumpPath,
|
|
GENERIC_WRITE,
|
|
FILE_SHARE_WRITE,
|
|
NULL,
|
|
CREATE_ALWAYS,
|
|
FILE_ATTRIBUTE_NORMAL,
|
|
NULL);
|
|
|
|
if (hDumpFile != INVALID_HANDLE_VALUE)
|
|
{
|
|
_MINIDUMP_EXCEPTION_INFORMATION ExceptionInfo;
|
|
|
|
ExceptionInfo.ThreadId = GetCurrentThreadId();
|
|
ExceptionInfo.ExceptionPointers = exceptionInfo;
|
|
ExceptionInfo.ClientPointers = NULL;
|
|
|
|
BOOL bSuccess = MiniDumpWriteDump(
|
|
GetCurrentProcess(),
|
|
GetCurrentProcessId(),
|
|
hDumpFile,
|
|
MiniDumpNormal,
|
|
&ExceptionInfo,
|
|
NULL,
|
|
NULL);
|
|
|
|
if (bSuccess)
|
|
{
|
|
CloseHandle(hDumpFile);
|
|
return EXCEPTION_EXECUTE_HANDLER;
|
|
}
|
|
|
|
CloseHandle(hDumpFile);
|
|
}
|
|
}
|
|
}
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
BOOL MiniDump::Begin(const TCHAR* filename)
|
|
{
|
|
SetErrorMode(SEM_FAILCRITICALERRORS);
|
|
|
|
TCHAR temp[512];
|
|
GetCurrentDirectory(_countof(temp), temp);
|
|
_sntprintf_s(m_folername, _countof(m_folername),_T("%s\\dump"), temp);
|
|
CreateDirectory(m_folername, NULL);
|
|
|
|
_tcscpy_s(m_filename, _countof(m_filename), filename); // 디버깅 해볼것
|
|
|
|
PreviousExceptionFilter = SetUnhandledExceptionFilter(UnHandledExceptionFilter);
|
|
return TRUE;
|
|
}
|
|
|
|
BOOL MiniDump::End(VOID)
|
|
{
|
|
SetUnhandledExceptionFilter(PreviousExceptionFilter);
|
|
return TRUE;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Operations
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
// Initializes the symbol engine if needed
|
|
void MiniDump::InitSymEng(void)
|
|
{
|
|
if(FALSE == m_symEngInit)
|
|
{
|
|
// Set up the symbol engine.
|
|
DWORD options = SymGetOptions();
|
|
|
|
// Turn on line loading.
|
|
SymSetOptions(options | SYMOPT_LOAD_LINES);
|
|
|
|
// Force the invade process flag on.
|
|
BOOL bRet = SymInitialize(GetCurrentProcess(), NULL , TRUE);
|
|
m_symEngInit = bRet;
|
|
}
|
|
}
|
|
|
|
// Helper function to isolate filling out the stack frame, which is CPU specific.
|
|
void MiniDump::FillInStackFrame(PCONTEXT contextRecord)
|
|
{
|
|
// Initialize the STACKFRAME structure.
|
|
ZeroMemory(&m_stackFrame, sizeof (STACKFRAME64));
|
|
|
|
m_stackFrame.AddrPC.Offset = contextRecord->Eip;
|
|
m_stackFrame.AddrPC.Mode = AddrModeFlat;
|
|
m_stackFrame.AddrStack.Offset = contextRecord->Esp;
|
|
m_stackFrame.AddrStack.Mode = AddrModeFlat;
|
|
m_stackFrame.AddrFrame.Offset = contextRecord->Ebp;
|
|
m_stackFrame.AddrFrame.Mode = AddrModeFlat;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// Operations
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
BOOL __stdcall CH_ReadProcessMemory( HANDLE hHandle,
|
|
DWORD64 qwBaseAddress,
|
|
PVOID lpBuffer,
|
|
DWORD nSize,
|
|
LPDWORD lpNumberOfBytesRead)
|
|
{
|
|
return (ReadProcessMemory( GetCurrentProcess(),
|
|
(LPCVOID)qwBaseAddress,
|
|
lpBuffer,
|
|
nSize,
|
|
lpNumberOfBytesRead));
|
|
}
|
|
|
|
DWORD Ansi2Wide(const char * szANSI, wchar_t* szWide, int iWideLen)
|
|
{
|
|
int iRet = MultiByteToWideChar( GetACP(),
|
|
0,
|
|
szANSI,
|
|
-1,
|
|
szWide,
|
|
iWideLen);
|
|
return (iRet);
|
|
}
|
|
|
|
|
|
DWORD Wide2Ansi(const wchar_t* szWide, char * szANSI, int iAnsiLen)
|
|
{
|
|
ZeroMemory(szANSI, iAnsiLen);
|
|
int iRet = WideCharToMultiByte(CP_ACP, 0, szWide, -1, szANSI, iAnsiLen, NULL, NULL);
|
|
return iRet;
|
|
}
|
|
|
|
// The internal function that does all the stack walking
|
|
LPCTSTR MiniDump::InternalGetStackTraceString(DWORD options)
|
|
{
|
|
// The value that is returned
|
|
LPCTSTR szRet;
|
|
// The module base address. I look this up right after the stack
|
|
// walk to ensure that the module is valid.
|
|
DWORD64 modBase;
|
|
|
|
__try
|
|
{
|
|
// Initialize the symbol engine in case it isn't initialized.
|
|
MiniDump::InitSymEng();
|
|
|
|
// Note: If the source file and line number functions are used,
|
|
// StackWalk can cause an access violation.
|
|
BOOL ret = StackWalk64 ( IMAGE_FILE_MACHINE_I386,
|
|
GetCurrentProcess(),
|
|
GetCurrentThread(),
|
|
&m_stackFrame,
|
|
&m_stackContext,
|
|
CH_ReadProcessMemory,
|
|
SymFunctionTableAccess64,
|
|
SymGetModuleBase64,
|
|
NULL);
|
|
if((FALSE == ret) || (0 == m_stackFrame.AddrFrame.Offset))
|
|
{
|
|
szRet = NULL;
|
|
__leave;
|
|
}
|
|
|
|
// Before I get too carried away and start calculating
|
|
// everything, I need to double-check that the address returned
|
|
// by StackWalk really exists. I've seen cases in which
|
|
// StackWalk returns TRUE but the address doesn't belong to
|
|
// a module in the process.
|
|
modBase = SymGetModuleBase64(GetCurrentProcess(), m_stackFrame.AddrPC.Offset);
|
|
if(0 == modBase)
|
|
{
|
|
szRet = NULL;
|
|
__leave;
|
|
}
|
|
|
|
int iCurr = 0;
|
|
|
|
// At a minimum, put in the address.
|
|
#ifdef _WIN64
|
|
iCurr += wsprintf( m_dumpText + iCurr,
|
|
_T( "0x%016X" ),
|
|
m_stackFrame.AddrPC.Offset);
|
|
#else
|
|
iCurr += wsprintf ( m_dumpText + iCurr,
|
|
_T("%04X:%08X"),
|
|
m_stackContext.SegCs,
|
|
m_stackFrame.AddrPC.Offset);
|
|
#endif
|
|
|
|
// Output the parameters?
|
|
if(DUMP_PARAMS == (options & DUMP_PARAMS))
|
|
{
|
|
iCurr += wsprintf( m_dumpText + iCurr,
|
|
_T("0x%08X 0x%08X 0x%08X 0x%08X"),
|
|
m_stackFrame.Params[ 0 ],
|
|
m_stackFrame.Params[ 1 ],
|
|
m_stackFrame.Params[ 2 ],
|
|
m_stackFrame.Params[ 3 ]);
|
|
}
|
|
|
|
// Output the module name.
|
|
if(DUMP_MODULE == (options & DUMP_MODULE))
|
|
{
|
|
iCurr += wsprintf(m_dumpText + iCurr, _T (" "));
|
|
|
|
iCurr += GetModuleBaseName( GetCurrentProcess(),
|
|
(HINSTANCE)modBase,
|
|
m_dumpText + iCurr,
|
|
BUFF_SIZE - iCurr);
|
|
}
|
|
|
|
// Output the symbol name?
|
|
if(DUMP_SYMBOL == (options & DUMP_SYMBOL))
|
|
{
|
|
DWORD64 displacement;
|
|
|
|
// Start looking up the exception address.
|
|
PIMAGEHLP_SYMBOL64 symbol = (PIMAGEHLP_SYMBOL64)&m_stackSymbol;
|
|
ZeroMemory(symbol , SYM_BUFF_SIZE);
|
|
symbol->SizeOfStruct = sizeof(IMAGEHLP_SYMBOL64);
|
|
symbol->MaxNameLength = SYM_BUFF_SIZE - sizeof(IMAGEHLP_SYMBOL64);
|
|
symbol->Address = m_stackFrame.AddrPC.Offset;
|
|
|
|
if(TRUE == SymGetSymFromAddr64( GetCurrentProcess(),
|
|
m_stackFrame.AddrPC.Offset,
|
|
&displacement,
|
|
symbol))
|
|
{
|
|
if(options & ~DUMP_SYMBOL)
|
|
{
|
|
iCurr += wsprintf(m_dumpText + iCurr , _T ( "," ));
|
|
}
|
|
|
|
// Copy no more symbol information than there's room
|
|
// for. Symbols are ANSI
|
|
int iLen = lstrlenA( symbol->Name );
|
|
if(iLen > (BUFF_SIZE - iCurr - (MAX_SYM_SIZE + 50)))
|
|
{
|
|
// Get some room on the stack to convert the string.
|
|
lstrcpyn( m_dumpText + iCurr,
|
|
reinterpret_cast<LPCWSTR>(symbol->Name),
|
|
BUFF_SIZE - iCurr - 1);
|
|
|
|
// Gotta leave now
|
|
szRet = m_dumpText;
|
|
__leave;
|
|
}
|
|
else
|
|
{
|
|
if(displacement > 0)
|
|
{
|
|
iCurr += wsprintf ( m_dumpText + iCurr,
|
|
//_T("%S()+%04d byte(s)"), // for unicode
|
|
_T("%s()+%04d byte(s)"),
|
|
symbol->Name,
|
|
displacement);
|
|
}
|
|
else
|
|
{
|
|
iCurr += wsprintf ( m_dumpText + iCurr,
|
|
//_T("%S"), // for unicode
|
|
_T("%s"),
|
|
symbol->Name);
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// If the symbol wasn't found, the source file and line
|
|
// number won't be found either, so leave now.
|
|
szRet = m_dumpText;
|
|
__leave;
|
|
}
|
|
}
|
|
|
|
// Output the source file and line number information?
|
|
if(DUMP_SRCLINE == (options & DUMP_SRCLINE))
|
|
{
|
|
ZeroMemory(&m_stackLine , sizeof(IMAGEHLP_LINE64));
|
|
m_stackLine.SizeOfStruct = sizeof(IMAGEHLP_LINE64);
|
|
|
|
DWORD dwLineDisp;
|
|
if(TRUE == SymGetLineFromAddr64(GetCurrentProcess(),
|
|
m_stackFrame.AddrPC.Offset,
|
|
&dwLineDisp,
|
|
&m_stackLine))
|
|
{
|
|
if(options & ~DUMP_SRCLINE)
|
|
{
|
|
iCurr += wsprintf(m_dumpText + iCurr , _T ( "," ));
|
|
}
|
|
|
|
// Copy no more of the source file and line number
|
|
// information than there's room for.
|
|
int iLen = lstrlenA(m_stackLine.FileName);
|
|
if(iLen > (BUFF_SIZE - iCurr - (MAX_PATH + 50)))
|
|
{
|
|
// Get some room on the stack to convert the string.
|
|
lstrcpyn(m_dumpText + iCurr,reinterpret_cast<LPCWSTR>(m_stackLine.FileName), BUFF_SIZE - iCurr - 1);
|
|
|
|
// Gotta leave now
|
|
szRet = m_dumpText;
|
|
__leave;
|
|
}
|
|
else
|
|
{
|
|
if(dwLineDisp > 0)
|
|
{
|
|
iCurr += wsprintf( m_dumpText + iCurr,
|
|
//_T(" %S, line %04d+%04d byte(s)"),
|
|
_T(" %s, line %04d+%04d byte(s)"),
|
|
m_stackLine.FileName,
|
|
m_stackLine.LineNumber,
|
|
dwLineDisp);
|
|
}
|
|
else
|
|
{
|
|
iCurr += wsprintf( m_dumpText + iCurr,
|
|
//_T(" %S, line %04d"),
|
|
_T(" %s, line %04d"),
|
|
m_stackLine.FileName,
|
|
m_stackLine.LineNumber);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
szRet = m_dumpText;
|
|
}
|
|
__except(EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
szRet = NULL;
|
|
}
|
|
return (szRet);
|
|
}
|
|
|
|
LPCTSTR MiniDump::GetFirstStackTraceString(DWORD options, EXCEPTION_POINTERS * exceptionInfo)
|
|
{
|
|
if(TRUE == IsBadReadPtr(exceptionInfo, sizeof(EXCEPTION_POINTERS*)))
|
|
{
|
|
// Debug(_T("GetFirstStackTraceString - invalid exceptionInfo\r\n"));
|
|
return (NULL);
|
|
}
|
|
|
|
// Get the stack frame filled in.
|
|
MiniDump:: FillInStackFrame(exceptionInfo->ContextRecord);
|
|
|
|
// Copy over the exception pointers fields so I don't corrupt the
|
|
// real one.
|
|
m_stackContext = *(exceptionInfo->ContextRecord);
|
|
|
|
return (MiniDump::InternalGetStackTraceString(options));
|
|
}
|
|
|
|
LPCTSTR MiniDump::GetNextStackTraceString(DWORD options, EXCEPTION_POINTERS* exceptionInfo)
|
|
{
|
|
// All error checking is in InternalGetStackTraceString.
|
|
// Assume that GetFirstStackTraceString has already initialized the
|
|
// stack frame information.
|
|
return (InternalGetStackTraceString(options));
|
|
}
|
|
|
|
void MiniDump::PrintCallStack(struct _EXCEPTION_POINTERS *exceptionInfo)
|
|
{
|
|
TCHAR dumpPath[MAX_PATH] = {0};
|
|
|
|
SYSTEMTIME SystemTime;
|
|
GetLocalTime(&SystemTime);
|
|
|
|
_sntprintf_s(
|
|
dumpPath,
|
|
_countof(dumpPath),
|
|
MAX_PATH,
|
|
_T("%s\\%s(%04d%02d%02d-%02d%02d%02d).txt"),
|
|
MiniDump::m_folername,
|
|
MiniDump::m_filename,
|
|
SystemTime.wYear,
|
|
SystemTime.wMonth,
|
|
SystemTime.wDay,
|
|
SystemTime.wHour,
|
|
SystemTime.wMinute,
|
|
SystemTime.wSecond);
|
|
|
|
FileWriter file;
|
|
if(file.Open(dumpPath))
|
|
{
|
|
// 콜스택 정보
|
|
DWORD options = DUMP_MODULE | DUMP_SYMBOL | DUMP_SRCLINE; //DUMP_PARAMS
|
|
const TCHAR* dumpText = GetFirstStackTraceString(options, exceptionInfo);
|
|
do
|
|
{
|
|
file.WriteFormat(_T("%s\r\n"), dumpText);
|
|
dumpText = GetNextStackTraceString(options, exceptionInfo);
|
|
}
|
|
while(NULL != dumpText);
|
|
|
|
// 파일저장
|
|
file.Close();
|
|
}
|
|
}
|