48#ifndef LASS_GUARDIAN_OF_INCLUSION_UTIL_IMPL_CRASH_DUMP_WIN32_INL
49#define LASS_GUARDIAN_OF_INCLUSION_UTIL_IMPL_CRASH_DUMP_WIN32_INL
61#pragma warning(disable: 4996)
62#pragma warning(disable: 4091)
66#define LASS_EXPERIMENTAL_CATCH_AND_REPORT(expression)\
71 catch (std::exception& error)\
73 std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR WARNING: '" LASS_STRINGIFY(expression)\
74 "' failed in '" LASS_HERE "' with std::exception: " << error.what() << std::endl << std::flush;\
78 std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR WARNING: '" LASS_STRINGIFY(expression)\
79 "' failed in '" LASS_HERE "' with unknown exception." << std::endl << std::flush;\
97 typedef void(*TCallback)(
const char* ,
void* );
99 CrashDumpImpl(
const std::string& name, TCallback callback,
void* callbackClosure):
101 callbackClosure_(callbackClosure),
105 isHandlingException_(false)
109 LASS_THROW(
"You can install only one crash dumper at the same time");
113 LASS_ENFORCE_COM(::CoCreateGuid(&guid));
114 const size_t guidSize = 39;
115 char guidString[guidSize];
116 _snprintf(guidString, guidSize,
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
117 int(guid.Data1),
int(guid.Data2),
int(guid.Data3),
int(guid.Data4[0]),
int(guid.Data4[1]),
118 int(guid.Data4[2]),
int(guid.Data4[3]),
int(guid.Data4[4]),
int(guid.Data4[5]),
119 int(guid.Data4[6]),
int(guid.Data4[7]));
120 guidString[guidSize - 1] =
'\0';
121 const size_t guidLength = strlen(guidString);
123 const size_t n = name.length();
124 const char* c_str = name.c_str();
125 std::copy(c_str, c_str + n, std::back_inserter(dumpFilename_));
126 dumpFilename_.push_back(
'-');
127 std::copy(guidString + 1, guidString + guidLength - 1, std::back_inserter(dumpFilename_));
128 const size_t extensionSize = 5;
129 char extension[extensionSize] = {
'.',
'd',
'm',
'p',
'\0'};
130 std::copy(extension, extension + extensionSize, std::back_inserter(dumpFilename_));
132 if (!dumpFilename_.empty())
135 const char* filename = &dumpFilename_[0];
136 const int bufferLength = LASS_ENFORCE_WINAPI(
137 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, 0, 0));
138 dumpFilenameT_.resize(bufferLength);
139 LASS_ENFORCE_WINAPI(::MultiByteToWideChar(CP_UTF8, 0, filename, -1, &dumpFilenameT_[0], bufferLength));
141 dumpFilenameT_ = dumpFilename_;
150 if (instance_ !=
this || mutex_)
152 LASS_THROW(
"you should have called this!");
156 dbghelp_ = loadLibrary(_T(
"dbghelp.dll"));
157 miniDumpWriteDump_ =
reinterpret_cast<TMiniDumpWriteDump
>(LASS_ENFORCE_WINAPI(
158 ::GetProcAddress(dbghelp_,
"MiniDumpWriteDump")));
161 handlerThread_.reset(util::threadMemFun(
this, &CrashDumpImpl::handlerThread,
util::threadJoinable));
162 handlerThread_->run();
164 oldFilter_ = ::SetUnhandledExceptionFilter(unhandledExceptionFilter);
165 patchSetUnhandledExceptionFilter(
true);
174 LASS_EXPERIMENTAL_CATCH_AND_REPORT(patchSetUnhandledExceptionFilter(
false));
175 ::SetUnhandledExceptionFilter(oldFilter_);
178 handleCondition_.signal();
179 handlerThread_->join();
181 LASS_WARN_WINAPI(::FreeLibrary(dbghelp_));
191 bufferSize_ = _MAX_PATH,
192#if LASS_ADDRESS_SIZE == 64
199 typedef BOOL (WINAPI *TMiniDumpWriteDump)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType,
200 CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
201 CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
203 typedef util::CriticalSection TMutex;
205 HMODULE loadLibrary(
const TCHAR* libraryName)
const
210 library = loadSideLibrary(libraryName, 0);
213 if (!library && dll::getLassInstance())
216 library = loadSideLibrary(libraryName, dll::getLassInstance());
223 library = ::LoadLibrary(libraryName);
228 LASS_THROW(
"Failed to install CrashDump: failed to load " << libraryName);
234 HMODULE loadSideLibrary(
const TCHAR* libraryName, HMODULE me)
const
236 TCHAR modulePath[bufferSize_];
237 const DWORD pathLength = ::GetModuleFileName(me, modulePath, bufferSize_);
238 if (pathLength != 0 && pathLength < bufferSize_)
240 TCHAR*
const lastBackSlash = ::_tcsrchr(modulePath, _T(
'\\'));
243 TCHAR*
const filename = lastBackSlash + 1;
244 ::_tcsncpy(filename, libraryName, (bufferSize_ - 1) - (filename - modulePath));
245 modulePath[bufferSize_ - 1] = _T(
'\0');
246 if (::_tcscmp(filename, libraryName) == 0)
248 return ::LoadLibrary(modulePath);
264 void patchSetUnhandledExceptionFilter(
bool patch)
266 HMODULE kernel32 = LASS_ENFORCE_WINAPI(::GetModuleHandle(_T(
"kernel32.dll")));
267 void* fun =
reinterpret_cast<void*
>(LASS_ENFORCE_WINAPI(::GetProcAddress(kernel32,
"SetUnhandledExceptionFilter")));
270 LASS_ENFORCE(!::IsBadReadPtr(fun, numPatchBytes_));
271 ::memcpy(oldBytes_, fun, numPatchBytes_);
272 writeMemory(fun, patchBytes_, numPatchBytes_);
276 writeMemory(fun, oldBytes_, numPatchBytes_);
280 static void writeMemory(
void* dest,
const void* source,
size_t size)
282 DWORD oldProtect, dummy;
283 LASS_ENFORCE_WINAPI(::VirtualProtect(dest, size, PAGE_EXECUTE_READWRITE, &oldProtect));
284 ::memcpy(dest, source, size);
285 ::VirtualProtect(dest, size, oldProtect, &dummy);
292 while (!(isHandlingException_ || isExiting_))
294 handleCondition_.wait(2000);
303 const char* filename = &dumpFilename_[0];
306 callback_(filename, callbackClosure_);
310 std::cerr <<
"[LASS RUN MSG] dumped minidump at \"" << filename <<
"\"."
311 << std::endl << std::flush;
313 isHandlingException_ =
false;
314 resultCondition_.signal();
318 void doHandleException()
320 handlerResult_ = EXCEPTION_CONTINUE_SEARCH;
321 const TCHAR* filename = &dumpFilenameT_[0];
322 HANDLE file = ::CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
323 if (file == INVALID_HANDLE_VALUE)
328 HANDLE process = ::GetCurrentProcess();
329 DWORD processId = ::GetCurrentProcessId();
330 MINIDUMP_EXCEPTION_INFORMATION exceptionParam;
331 exceptionParam.ThreadId = threadId_;
332 exceptionParam.ExceptionPointers = exceptionInfo_;
333 exceptionParam.ClientPointers = 0;
335 if (miniDumpWriteDump_(process, processId, file, ::MiniDumpNormal, &exceptionParam, 0, 0))
337 handlerResult_ = EXCEPTION_EXECUTE_HANDLER;
343 static LONG WINAPI unhandledExceptionFilter(
struct _EXCEPTION_POINTERS* exceptionInfo)
345 const DWORD code = exceptionInfo->ExceptionRecord->ExceptionCode;
346 if (code == EXCEPTION_BREAKPOINT || code == EXCEPTION_SINGLE_STEP)
348 return EXCEPTION_CONTINUE_SEARCH;
352 instance_->exceptionInfo_ = exceptionInfo;
353 instance_->threadId_ = ::GetCurrentThreadId();
354 instance_->isHandlingException_ =
true;
355 instance_->handleCondition_.signal();
356 while (instance_->isHandlingException_)
358 instance_->resultCondition_.wait(500);
360 return instance_->handlerResult_;
362 return EXCEPTION_CONTINUE_SEARCH;
365 std::vector<char> dumpFilename_;
366 std::vector<TCHAR> dumpFilenameT_;
368 void* callbackClosure_;
370 TMiniDumpWriteDump miniDumpWriteDump_;
371 std::unique_ptr<Thread> handlerThread_;
372 util::Condition handleCondition_;
373 util::Condition resultCondition_;
375 LPTOP_LEVEL_EXCEPTION_FILTER oldFilter_;
376 unsigned char oldBytes_[numPatchBytes_];
378 _EXCEPTION_POINTERS* exceptionInfo_;
382 std::atomic<bool> isExiting_;
383 std::atomic<bool> isHandlingException_;
385 static CrashDumpImpl* instance_;
386 static unsigned char patchBytes_[numPatchBytes_];
389CrashDumpImpl* CrashDumpImpl::instance_ = 0;
390#if LASS_ADDRESS_SIZE == 64
391unsigned char CrashDumpImpl::patchBytes_[numPatchBytes_] = { 0x33, 0xc0, 0xc3 };
393unsigned char CrashDumpImpl::patchBytes_[numPatchBytes_] = { 0x33, 0xc0, 0xc2, 0x04, 0x00 };
#define LASS_LOCK(iLock)
Locks a iLock and starts a scope block in which it remains locked.
@ threadJoinable
joinable thread, can be waited for
general utility, debug facilities, ...
Library for Assembled Shared Sources.