00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048 #ifndef LASS_GUARDIAN_OF_INCLUSION_UTIL_IMPL_CRASH_DUMP_WIN32_INL
00049 #define LASS_GUARDIAN_OF_INCLUSION_UTIL_IMPL_CRASH_DUMP_WIN32_INL
00050
00051 #pragma LASS_NOTE("util::CrashDump: using win32 implementation")
00052
00053 #include "../util_common.h"
00054 #include "lass_errno.h"
00055 #include "../thread_fun.h"
00056 #include "../scoped_ptr.h"
00057
00058 #if LASS_BUILD_DLL
00059 # include "../../dll/dll_main.h"
00060 #endif
00061
00062 #include <windows.h>
00063 #include <dbghelp.h>
00064 #include <objbase.h>
00065 #include <tchar.h>
00066 #include <stdlib.h>
00067
00068 #pragma warning(push)
00069 #pragma warning(disable: 4996) // This function or variable may be unsafe.
00070
00071 #define LASS_EXPERIMENTAL_CATCH_AND_REPORT(expression)\
00072 try\
00073 {\
00074 expression;\
00075 }\
00076 catch (std::exception& error)\
00077 {\
00078 std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR WARNING: '" LASS_STRINGIFY(expression)\
00079 "' failed in '" LASS_HERE "' with std::exception: " << error.what() << std::endl << std::flush;\
00080 }\
00081 catch (...)\
00082 {\
00083 std::cerr << "[LASS RUN MSG] UNDEFINED BEHAVIOUR WARNING: '" LASS_STRINGIFY(expression)\
00084 "' failed in '" LASS_HERE "' with unknown exception." << std::endl << std::flush;\
00085 }\
00086
00087
00088 namespace lass
00089 {
00090 namespace util
00091 {
00092 namespace impl
00093 {
00094
00095
00096
00097
00098 class CrashDumpImpl
00099 {
00100 public:
00101
00102 typedef void(*TCallback)(const char* , void* );
00103
00104 CrashDumpImpl(const std::string& name, TCallback callback, void* callbackClosure):
00105 oldFilter_(0),
00106 callback_(callback),
00107 callbackClosure_(callbackClosure),
00108 mutex_(0),
00109 isExiting_(false),
00110 isHandlingException_(false)
00111 {
00112 if (instance_)
00113 {
00114 LASS_THROW("You can install only one crash dumper at the same time");
00115 }
00116
00117 ::GUID guid;
00118 LASS_ENFORCE_COM(::CoCreateGuid(&guid));
00119 const size_t guidSize = 39;
00120 char guidString[guidSize];
00121 _snprintf(guidString, guidSize, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
00122 int(guid.Data1), int(guid.Data2), int(guid.Data3), int(guid.Data4[0]), int(guid.Data4[1]),
00123 int(guid.Data4[2]), int(guid.Data4[3]), int(guid.Data4[4]), int(guid.Data4[5]),
00124 int(guid.Data4[6]), int(guid.Data4[7]));
00125 guidString[guidSize - 1] = '\0';
00126 const size_t guidLength = strlen(guidString);
00127
00128 const size_t n = name.length();
00129 const char* c_str = name.c_str();
00130 std::copy(c_str, c_str + n, std::back_inserter(dumpFilename_));
00131 dumpFilename_.push_back('-');
00132 std::copy(guidString + 1, guidString + guidLength - 1, std::back_inserter(dumpFilename_));
00133 const size_t extensionSize = 5;
00134 char extension[extensionSize] = {'.', 'd', 'm', 'p', '\0'};
00135 std::copy(extension, extension + extensionSize, std::back_inserter(dumpFilename_));
00136
00137 if (!dumpFilename_.empty())
00138 {
00139 #ifdef _UNICODE
00140 const char* filename = &dumpFilename_[0];
00141 const int bufferLength = LASS_ENFORCE_WINAPI(
00142 ::MultiByteToWideChar(CP_UTF8, 0, filename, -1, 0, 0));
00143 dumpFilenameT_.resize(bufferLength);
00144 LASS_ENFORCE_WINAPI(::MultiByteToWideChar(CP_UTF8, 0, filename, -1, &dumpFilenameT_[0], bufferLength));
00145 #else
00146 dumpFilenameT_ = dumpFilename_;
00147 #endif
00148 }
00149
00150 instance_ = this;
00151 }
00152
00153 void init()
00154 {
00155 if (instance_ != this || mutex_)
00156 {
00157 LASS_THROW("you should have called this!");
00158 }
00159 mutex_ = new TMutex;
00160
00161 dbghelp_ = loadLibrary(_T("dbghelp.dll"));
00162 miniDumpWriteDump_ = reinterpret_cast<TMiniDumpWriteDump>(LASS_ENFORCE_WINAPI(
00163 ::GetProcAddress(dbghelp_, "MiniDumpWriteDump")));
00164
00165 isExiting_ = false;
00166 handlerThread_.reset(util::threadMemFun(this, &CrashDumpImpl::handlerThread, util::threadJoinable));
00167 handlerThread_->run();
00168
00169 oldFilter_ = ::SetUnhandledExceptionFilter(unhandledExceptionFilter);
00170 patchSetUnhandledExceptionFilter(true);
00171
00172 instance_ = this;
00173 }
00174
00175 ~CrashDumpImpl()
00176 {
00177 instance_ = 0;
00178
00179 LASS_EXPERIMENTAL_CATCH_AND_REPORT(patchSetUnhandledExceptionFilter(false));
00180 ::SetUnhandledExceptionFilter(oldFilter_);
00181
00182 isExiting_ = true;
00183 handleCondition_.signal();
00184 handlerThread_->join();
00185
00186 LASS_WARN_WINAPI(::FreeLibrary(dbghelp_));
00187
00188 delete mutex_;
00189 mutex_ = 0;
00190 }
00191
00192 public:
00193
00194 enum
00195 {
00196 bufferSize_ = _MAX_PATH,
00197 numPatchBytes_ = 5,
00198 };
00199
00200 typedef BOOL (WINAPI *TMiniDumpWriteDump)(HANDLE hProcess, DWORD ProcessId, HANDLE hFile, MINIDUMP_TYPE DumpType,
00201 CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam, CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
00202 CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
00203
00204 typedef util::CriticalSection TMutex;
00205
00206 const HMODULE loadLibrary(const TCHAR* libraryName) const
00207 {
00208 HMODULE library = 0;
00209
00210
00211 library = loadSideLibrary(libraryName, 0);
00212
00213 #if LASS_BUILD_DLL
00214 if (!library && dll::getLassInstance())
00215 {
00216
00217 library = loadSideLibrary(libraryName, dll::getLassInstance());
00218 }
00219 #endif
00220
00221 if (!library)
00222 {
00223
00224 library = ::LoadLibrary(libraryName);
00225 }
00226
00227 if (!library)
00228 {
00229 LASS_THROW("Failed to install CrashDump: failed to load " << libraryName);
00230 }
00231
00232 return library;
00233 }
00234
00235 const HMODULE loadSideLibrary(const TCHAR* libraryName, HMODULE me) const
00236 {
00237 TCHAR modulePath[bufferSize_];
00238 const DWORD pathLength = ::GetModuleFileName(me, modulePath, bufferSize_);
00239 if (pathLength != 0 && pathLength < bufferSize_)
00240 {
00241 TCHAR* const lastBackSlash = ::_tcsrchr(modulePath, _T('\\'));
00242 if (lastBackSlash)
00243 {
00244 TCHAR* const filename = lastBackSlash + 1;
00245 ::_tcsncpy(filename, libraryName, (bufferSize_ - 1) - (filename - modulePath));
00246 modulePath[bufferSize_ - 1] = _T('\0');
00247 if (::_tcscmp(filename, libraryName) == 0)
00248 {
00249 return ::LoadLibrary(modulePath);
00250 }
00251 }
00252 }
00253 return 0;
00254 }
00255
00256
00257
00258
00259
00260
00261
00262
00263
00264
00265 void patchSetUnhandledExceptionFilter(bool patch)
00266 {
00267 HMODULE kernel32 = LASS_ENFORCE_WINAPI(::GetModuleHandle(_T("kernel32.dll")));
00268 void* fun = LASS_ENFORCE_WINAPI(::GetProcAddress(kernel32, "SetUnhandledExceptionFilter"));
00269 if (patch)
00270 {
00271 LASS_ENFORCE(!::IsBadReadPtr(fun, numPatchBytes_));
00272 ::memcpy(oldBytes_, fun, numPatchBytes_);
00273 writeMemory(fun, patchBytes_, numPatchBytes_);
00274 }
00275 else
00276 {
00277 writeMemory(fun, oldBytes_, numPatchBytes_);
00278 }
00279 }
00280
00281 static void writeMemory(void* dest, const void* source, size_t size)
00282 {
00283 DWORD oldProtect, dummy;
00284 LASS_ENFORCE_WINAPI(::VirtualProtect(dest, size, PAGE_EXECUTE_READWRITE, &oldProtect));
00285 ::memcpy(dest, source, size);
00286 ::VirtualProtect(dest, size, oldProtect, &dummy);
00287 }
00288
00289 void handlerThread()
00290 {
00291 while (true)
00292 {
00293 while (!(isHandlingException_ || isExiting_))
00294 {
00295 handleCondition_.wait(2000);
00296 }
00297 if (isExiting_)
00298 {
00299 return;
00300 }
00301
00302 doHandleException();
00303
00304 const char* filename = &dumpFilename_[0];
00305 if (callback_)
00306 {
00307 callback_(filename, callbackClosure_);
00308 }
00309 else
00310 {
00311 std::cerr << "[LASS RUN MSG] dumped minidump at \"" << filename << "\"."
00312 << std::endl << std::flush;
00313 }
00314 isHandlingException_ = false;
00315 resultCondition_.signal();
00316 }
00317 }
00318
00319 void doHandleException()
00320 {
00321 handlerResult_ = EXCEPTION_CONTINUE_SEARCH;
00322 const TCHAR* filename = &dumpFilenameT_[0];
00323 HANDLE file = ::CreateFile(filename, GENERIC_WRITE, 0, 0, CREATE_NEW, FILE_ATTRIBUTE_NORMAL, 0);
00324 if (file == INVALID_HANDLE_VALUE)
00325 {
00326 return;
00327 }
00328
00329 HANDLE process = ::GetCurrentProcess();
00330 DWORD processId = ::GetCurrentProcessId();
00331 MINIDUMP_EXCEPTION_INFORMATION exceptionParam;
00332 exceptionParam.ThreadId = threadId_;
00333 exceptionParam.ExceptionPointers = exceptionInfo_;
00334 exceptionParam.ClientPointers = 0;
00335
00336 if (miniDumpWriteDump_(process, processId, file, ::MiniDumpNormal, &exceptionParam, 0, 0))
00337 {
00338 handlerResult_ = EXCEPTION_EXECUTE_HANDLER;
00339 }
00340
00341 ::CloseHandle(file);
00342 }
00343
00344 static LONG WINAPI unhandledExceptionFilter(struct _EXCEPTION_POINTERS* exceptionInfo)
00345 {
00346 const DWORD code = exceptionInfo->ExceptionRecord->ExceptionCode;
00347 if (code == EXCEPTION_BREAKPOINT || code == EXCEPTION_SINGLE_STEP)
00348 {
00349 return EXCEPTION_CONTINUE_SEARCH;
00350 };
00351 LASS_LOCK(*instance_->mutex_)
00352 {
00353 instance_->exceptionInfo_ = exceptionInfo;
00354 instance_->threadId_ = ::GetCurrentThreadId();
00355 instance_->isHandlingException_ = true;
00356 instance_->handleCondition_.signal();
00357 while (instance_->isHandlingException_)
00358 {
00359 instance_->resultCondition_.wait(500);
00360 }
00361 return instance_->handlerResult_;
00362 }
00363 return EXCEPTION_CONTINUE_SEARCH;
00364 }
00365
00366 std::vector<char> dumpFilename_;
00367 std::vector<TCHAR> dumpFilenameT_;
00368 TCallback callback_;
00369 void* callbackClosure_;
00370 HMODULE dbghelp_;
00371 TMiniDumpWriteDump miniDumpWriteDump_;
00372 util::ScopedPtr<Thread> handlerThread_;
00373 util::Condition handleCondition_;
00374 util::Condition resultCondition_;
00375 TMutex* mutex_;
00376 LPTOP_LEVEL_EXCEPTION_FILTER oldFilter_;
00377 char oldBytes_[numPatchBytes_];
00378
00379 _EXCEPTION_POINTERS* exceptionInfo_;
00380 DWORD threadId_;
00381 LONG handlerResult_;
00382
00383 volatile bool isExiting_;
00384 volatile bool isHandlingException_;
00385
00386 static CrashDumpImpl* instance_;
00387 static char patchBytes_[numPatchBytes_];
00388 };
00389
00390 CrashDumpImpl* CrashDumpImpl::instance_ = 0;
00391 char CrashDumpImpl::patchBytes_[numPatchBytes_] = { 0x33, 0xc0, 0xc2, 0x04, 0x00 };
00392
00393 }
00394 }
00395 }
00396
00397 #pragma warning(pop)
00398
00399 #endif
00400
00401