mirror of
https://github.com/TencentCloud/TIMSDK.git
synced 2026-10-18 22:03:18 +08:00
227 lines
7.5 KiB
C++
227 lines
7.5 KiB
C++
/**************************************************************************
|
|
Copyright: Copyright ? 1998 - 2017 Tencent. All Rights Reserved
|
|
Created: 2017-2-17 11:28:54
|
|
Filename: CrashDump.cpp
|
|
|
|
Description:
|
|
***************************************************************************/
|
|
|
|
#include "CrashDump.h"
|
|
|
|
#include <ctime>
|
|
#include <string>
|
|
#include <sstream>
|
|
#include <assert.h>
|
|
|
|
#include <DbgHelp.h>
|
|
#include <shellapi.h>
|
|
|
|
typedef BOOL(WINAPI *getUserModeExceptionProc)(LPDWORD);
|
|
typedef BOOL(WINAPI *setUserModeExceptionProc)(DWORD);
|
|
|
|
typedef BOOL(WINAPI *MINIDUMPWRITEDUMP) (
|
|
HANDLE hProcess,
|
|
DWORD ProcessId,
|
|
HANDLE hFile,
|
|
MINIDUMP_TYPE DumpType,
|
|
PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
|
PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
|
PMINIDUMP_CALLBACK_INFORMATION CallbackParam
|
|
);
|
|
|
|
static std::wstring format(const wchar_t* pszFormat, ...)
|
|
{
|
|
wchar_t buffer[MAX_PATH] = { 0 };
|
|
|
|
va_list ap;
|
|
va_start(ap, pszFormat);
|
|
int nCount = ::vswprintf_s(buffer, _countof(buffer), pszFormat, ap);
|
|
va_end(ap);
|
|
|
|
if (nCount < 0)
|
|
{
|
|
assert(false);
|
|
return pszFormat;
|
|
}
|
|
|
|
return buffer;
|
|
}
|
|
|
|
/**************************************************************************/
|
|
|
|
CrashDump::CrashDump()
|
|
: m_oldExceptionFilter(NULL)
|
|
{
|
|
// 堆异常
|
|
BOOL bRet = ::HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
|
|
::OutputDebugStringW(format(L"HeapSetInformation, bRet: <%lu, %lu>.\n", bRet, ::GetLastError()).c_str());
|
|
|
|
// DEP策略
|
|
bRet = ::SetProcessDEPPolicy(PROCESS_DEP_ENABLE | PROCESS_DEP_DISABLE_ATL_THUNK_EMULATION);
|
|
::OutputDebugStringW(format(L"SetProcessDEPPolicy, bRet: <%lu, %lu>.\n", bRet, ::GetLastError()).c_str());
|
|
|
|
//standard app-wide unhandled exception filter
|
|
m_oldExceptionFilter = ::SetUnhandledExceptionFilter(UnhandledExceptionFilter);
|
|
|
|
//fix for exceptions being swallowed inside callbacks (see KB976038)
|
|
HMODULE hKernel32 = GetModuleHandle(TEXT("KERNEL32"));
|
|
if (NULL == hKernel32)
|
|
{
|
|
::OutputDebugStringW(L"GetModuleHandle faled.\n");
|
|
}
|
|
else
|
|
{
|
|
DWORD dwFlags = 0;
|
|
getUserModeExceptionProc procGetProcessUserModeExceptionPolicy;
|
|
setUserModeExceptionProc procSetProcessUserModeExceptionPolicy;
|
|
|
|
procGetProcessUserModeExceptionPolicy = (getUserModeExceptionProc)::GetProcAddress(hKernel32, "GetProcessUserModeExceptionPolicy");
|
|
procSetProcessUserModeExceptionPolicy = (setUserModeExceptionProc)::GetProcAddress(hKernel32, "SetProcessUserModeExceptionPolicy");
|
|
|
|
if (procGetProcessUserModeExceptionPolicy && procSetProcessUserModeExceptionPolicy)
|
|
{
|
|
if (procGetProcessUserModeExceptionPolicy(&dwFlags))
|
|
{
|
|
bRet = procSetProcessUserModeExceptionPolicy(dwFlags & ~1);
|
|
::OutputDebugStringW(format(L"GetProcessUserModeExceptionPolicy, bRet: %lu.\n", bRet).c_str());
|
|
}
|
|
|
|
::OutputDebugStringW(format(L"SetProcessUserModeExceptionPolicy, bRet: %lu, dwFlags: %lu.\n", bRet, dwFlags).c_str());
|
|
}
|
|
}
|
|
}
|
|
|
|
CrashDump::~CrashDump()
|
|
{
|
|
if (NULL != m_oldExceptionFilter)
|
|
{
|
|
::SetUnhandledExceptionFilter(m_oldExceptionFilter);
|
|
}
|
|
}
|
|
|
|
LONG WINAPI CrashDump::UnhandledExceptionFilter(struct _EXCEPTION_POINTERS* pExceptionInfo)
|
|
{
|
|
//always break into a debugger if one is present
|
|
if (::IsDebuggerPresent())
|
|
{
|
|
::OutputDebugStringW(L"IsDebuggerPresent return TRUE.\n");
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
//exception codes < 0x80000000 are typically informative only and not crash worthy
|
|
//0xe06d7363 indicates a c++ exception was thrown, let's just hope it was caught.
|
|
//this is no longer needed since we're an unhandled handler vs a vectored handler
|
|
|
|
/*if (exceptionInfo->ExceptionRecord->ExceptionCode < 0x80000000 || exceptionInfo->ExceptionRecord->ExceptionCode == 0xe06d7363 ||
|
|
exceptionInfo->ExceptionRecord->ExceptionCode == 0x800706b5)
|
|
return EXCEPTION_CONTINUE_SEARCH;*/
|
|
|
|
//uh oh, we're crashing inside ourselves... this is really bad!
|
|
static BOOL inExceptionHandler = FALSE;
|
|
if (inExceptionHandler)
|
|
{
|
|
::OutputDebugStringW(L"Current function has crashed.Shit.\n");
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
inExceptionHandler = TRUE;
|
|
|
|
WCHAR fullPath[MAX_PATH] = { 0 };
|
|
DWORD pathLength = ::GetModuleFileNameW(NULL, fullPath, MAX_PATH);
|
|
if (0 == pathLength)
|
|
{
|
|
::OutputDebugStringW(L"GetModuleFileNameW failed.\n");
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
LPCWSTR lastSlash = ::wcsrchr(fullPath, L'\\');
|
|
if (NULL == lastSlash)
|
|
{
|
|
::OutputDebugStringW(L"wcsrchr return wrong.\n");
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
std::wstring exeDirPath(fullPath, lastSlash - fullPath + 1);
|
|
|
|
WCHAR filePath[MAX_PATH] = { 0 };
|
|
for (int i = 0; ; ++i) // 避免同名
|
|
{
|
|
SYSTEMTIME sys_time = { 0 };
|
|
::GetLocalTime(&sys_time);
|
|
|
|
::swprintf_s(filePath, _countof(filePath) - 1, L"%s%04u_%02u_%02u_%02u_%02u_%02u_%d.dmp"
|
|
, exeDirPath.c_str()
|
|
, sys_time.wYear, sys_time.wMonth, sys_time.wDay
|
|
, sys_time.wHour, sys_time.wMinute, sys_time.wSecond, i);
|
|
|
|
if (::GetFileAttributes(filePath) == INVALID_FILE_ATTRIBUTES)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
HANDLE hFile = ::CreateFileW(filePath, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
|
|
if (INVALID_HANDLE_VALUE == hFile)
|
|
{
|
|
::OutputDebugStringW(L"CreateFileW failed.\n");
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
//load dbghelp dynamically
|
|
HMODULE hDbgHelp = LoadLibraryW(L"DBGHELP");
|
|
if (!hDbgHelp)
|
|
{
|
|
::OutputDebugStringW(L"LoadLibraryW DBGHELP failed.\n");
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
MINIDUMPWRITEDUMP fnMiniDumpWriteDump = (MINIDUMPWRITEDUMP)::GetProcAddress(hDbgHelp, "MiniDumpWriteDump");
|
|
if (!fnMiniDumpWriteDump)
|
|
{
|
|
::OutputDebugStringW(L"GetProcAddress MiniDumpWriteDump failed.\n");
|
|
|
|
::FreeLibrary(hDbgHelp);
|
|
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
MINIDUMP_TYPE dumpFlags = (MINIDUMP_TYPE)(MiniDumpWithIndirectlyReferencedMemory | MiniDumpWithUnloadedModules | MiniDumpWithProcessThreadData);
|
|
|
|
MINIDUMP_EXCEPTION_INFORMATION miniInfo = {0};
|
|
miniInfo.ClientPointers = TRUE;
|
|
miniInfo.ExceptionPointers = pExceptionInfo;
|
|
miniInfo.ThreadId = ::GetCurrentThreadId();
|
|
|
|
//generate a minidump if possible
|
|
if (fnMiniDumpWriteDump(::GetCurrentProcess(), ::GetCurrentProcessId(), hFile, dumpFlags, &miniInfo, NULL, NULL))
|
|
{
|
|
WCHAR buffer[MAX_PATH] = { 0 };
|
|
::swprintf_s(buffer, _countof(buffer) - 1, L"Process has crashed.\nMinidump was saved to: \n\\%s\n", filePath);
|
|
::OutputDebugStringW(buffer);
|
|
|
|
::MessageBoxW(NULL, buffer, NULL, MB_ICONERROR | MB_OK);
|
|
}
|
|
else
|
|
{
|
|
::OutputDebugStringW(format(L"Minidump was saved failed: %hu.\n", ::GetLastError()).c_str());
|
|
|
|
::MessageBoxW(NULL, format(L"Minidump was saved failed: %hu.\n", ::GetLastError()).c_str(), NULL, MB_ICONERROR | MB_OK);
|
|
}
|
|
|
|
::FreeLibrary(hDbgHelp);
|
|
::CloseHandle(hFile);
|
|
|
|
//we really shouldn't be returning here, if we're at the bottom of the VEH chain this is a pretty legitimate crash
|
|
//and if we return we could end up invoking a second crash handler or other weird / annoying things
|
|
//ExitProcess(exceptionInfo->ExceptionRecord->ExceptionCode);
|
|
return EXCEPTION_CONTINUE_SEARCH;
|
|
}
|
|
|
|
/**************************************************************************/
|