diff options
author | Jean-Francois Mauguit <jfmauguit@mac.com> | 2024-09-24 09:03:25 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-09-24 09:03:25 -0400 |
commit | bab614c421ed7ae329d26bf028c4a3b1d2450f5a (patch) | |
tree | 12f17f78986871dd2cfb0a56e5e93b545c1ae0d0 /Src/external_dependencies/openmpt-trunk/mptrack/ExceptionHandler.cpp | |
parent | 4bde6044fddf053f31795b9eaccdd2a5a527d21f (diff) | |
parent | 20d28e80a5c861a9d5f449ea911ab75b4f37ad0d (diff) | |
download | winamp-bab614c421ed7ae329d26bf028c4a3b1d2450f5a.tar.gz |
Merge pull request #5 from WinampDesktop/community
Merge to main
Diffstat (limited to 'Src/external_dependencies/openmpt-trunk/mptrack/ExceptionHandler.cpp')
-rw-r--r-- | Src/external_dependencies/openmpt-trunk/mptrack/ExceptionHandler.cpp | 853 |
1 files changed, 853 insertions, 0 deletions
diff --git a/Src/external_dependencies/openmpt-trunk/mptrack/ExceptionHandler.cpp b/Src/external_dependencies/openmpt-trunk/mptrack/ExceptionHandler.cpp new file mode 100644 index 00000000..c4d8c5d5 --- /dev/null +++ b/Src/external_dependencies/openmpt-trunk/mptrack/ExceptionHandler.cpp @@ -0,0 +1,853 @@ +/* + * ExceptionHandler.cpp + * -------------------- + * Purpose: Code for handling crashes (unhandled exceptions) in OpenMPT. + * Notes : (currently none) + * Authors: OpenMPT Devs + * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. + */ + + +#include "stdafx.h" +#include "Mainfrm.h" +#include "Mptrack.h" +#include "AboutDialog.h" +#include "InputHandler.h" +#include "openmpt/sounddevice/SoundDevice.hpp" +#include "Moddoc.h" +#include <shlwapi.h> +#include "ExceptionHandler.h" +#include "../misc/WriteMemoryDump.h" +#include "../common/version.h" +#include "../common/mptFileIO.h" +#include "../soundlib/mod_specifications.h" + +#include <atomic> + + +OPENMPT_NAMESPACE_BEGIN + + +// Write full memory dump instead of minidump. +bool ExceptionHandler::fullMemDump = false; + +bool ExceptionHandler::stopSoundDeviceOnCrash = true; + +bool ExceptionHandler::stopSoundDeviceBeforeDump = false; + +// Delegate to system-specific crash processing once our own crash handler is +// finished. This is useful to allow attaching a debugger. +bool ExceptionHandler::delegateToWindowsHandler = false; + +// Allow debugging the unhandled exception filter. Normally, if a debugger is +// attached, no exceptions are unhandled because the debugger handles them. If +// debugExceptionHandler is true, an additional __try/__catch is inserted around +// InitInstance(), ExitInstance() and the main message loop, which will call our +// filter, which then can be stepped through in a debugger. +bool ExceptionHandler::debugExceptionHandler = false; + + +bool ExceptionHandler::useAnyCrashHandler = false; +bool ExceptionHandler::useImplicitFallbackSEH = false; +bool ExceptionHandler::useExplicitSEH = false; +bool ExceptionHandler::handleStdTerminate = false; +bool ExceptionHandler::handleMfcExceptions = false; + + +static thread_local ExceptionHandler::Context *g_Context = nullptr; + +static LPTOP_LEVEL_EXCEPTION_FILTER g_OriginalUnhandledExceptionFilter = nullptr; +static std::terminate_handler g_OriginalTerminateHandler = nullptr; + +static UINT g_OriginalErrorMode = 0; + + +ExceptionHandler::Context *ExceptionHandler::SetContext(Context *newContext) noexcept +{ + Context *oldContext = g_Context; + g_Context = newContext; + return oldContext; +} + + +static std::atomic<int> & g_CrashCount() +{ + static std::atomic<int> s_CrashCount(0); + return s_CrashCount; +} + + +static std::atomic<int> & g_TaintCountDriver() +{ + static std::atomic<int> s_TaintCountDriver(0); + return s_TaintCountDriver; +} + + +static std::atomic<int> & g_TaintCountPlugin() +{ + static std::atomic<int> s_TaintCountPlugin(0); + return s_TaintCountPlugin; +} + + +void ExceptionHandler::TaintProcess(ExceptionHandler::TaintReason reason) +{ + switch(reason) + { + case ExceptionHandler::TaintReason::Driver: + g_TaintCountDriver().fetch_add(1); + break; + case ExceptionHandler::TaintReason::Plugin: + g_TaintCountPlugin().fetch_add(1); + break; + default: + MPT_ASSERT_NOTREACHED(); + break; + } +} + + +enum DumpMode +{ + DumpModeCrash = 0, // crash + DumpModeWarning = 1, // assert + DumpModeDebug = 2, // debug output (e.g. trace log) +}; + + +struct CrashOutputDirectory +{ + bool valid; + mpt::PathString path; + CrashOutputDirectory() + : valid(true) + { + const mpt::PathString timestampDir = mpt::PathString::FromCString((CTime::GetCurrentTime()).Format(_T("%Y-%m-%d %H.%M.%S\\"))); + // Create a crash directory + path = mpt::GetTempDirectory() + P_("OpenMPT Crash Files\\"); + if(!path.IsDirectory()) + { + CreateDirectory(path.AsNative().c_str(), nullptr); + } + // Set compressed attribute in order to save disk space. + // Debugging information should clutter the users computer as little as possible. + // Performance is not important here. + // Ignore any errors. + SetFilesystemCompression(path); + // Compression will be inherited by children directories and files automatically. + path += timestampDir; + if(!path.IsDirectory()) + { + if(!CreateDirectory(path.AsNative().c_str(), nullptr)) + { + valid = false; + } + } + } +}; + + +class DebugReporter +{ +private: + int crashCount; + int taintCountDriver; + int taintCountPlugin; + bool stateFrozen; + const DumpMode mode; + const CrashOutputDirectory crashDirectory; + bool writtenMiniDump; + bool writtenTraceLog; + int rescuedFiles; +private: + static bool FreezeState(DumpMode mode); + static bool Cleanup(DumpMode mode); + bool GenerateDump(_EXCEPTION_POINTERS *pExceptionInfo); + bool GenerateTraceLog(); + int RescueFiles(); + bool HasWrittenDebug() const { return writtenMiniDump || writtenTraceLog; } + static void StopSoundDevice(); +public: + DebugReporter(DumpMode mode, _EXCEPTION_POINTERS *pExceptionInfo); + ~DebugReporter(); + void ReportError(mpt::ustring errorMessage); +}; + + +DebugReporter::DebugReporter(DumpMode mode, _EXCEPTION_POINTERS *pExceptionInfo) + : crashCount(g_CrashCount().fetch_add(1) + 1) + , taintCountDriver(g_TaintCountDriver().load()) + , taintCountPlugin(g_TaintCountPlugin().load()) + , stateFrozen(FreezeState(mode)) + , mode(mode) + , writtenMiniDump(false) + , writtenTraceLog(false) + , rescuedFiles(0) +{ + if(mode == DumpModeCrash || mode == DumpModeWarning) + { + writtenMiniDump = GenerateDump(pExceptionInfo); + } + if(mode == DumpModeCrash || mode == DumpModeWarning || mode == DumpModeDebug) + { + writtenTraceLog = GenerateTraceLog(); + } + if(mode == DumpModeCrash || mode == DumpModeWarning) + { + rescuedFiles = RescueFiles(); + } +} + + +DebugReporter::~DebugReporter() +{ + Cleanup(mode); +} + + +bool DebugReporter::GenerateDump(_EXCEPTION_POINTERS *pExceptionInfo) +{ + return WriteMemoryDump(pExceptionInfo, (crashDirectory.path + P_("crash.dmp")).AsNative().c_str(), ExceptionHandler::fullMemDump); +} + + +bool DebugReporter::GenerateTraceLog() +{ + return mpt::log::Trace::Dump(crashDirectory.path + P_("trace.log")); +} + + +static void SaveDocumentSafe(CModDoc *pModDoc, const mpt::PathString &filename) +{ + __try + { + pModDoc->OnSaveDocument(filename); + } __except(EXCEPTION_EXECUTE_HANDLER) + { + // nothing + } +} + + +// Rescue modified files... +int DebugReporter::RescueFiles() +{ + int numFiles = 0; + auto documents = theApp.GetOpenDocuments(); + for(auto modDoc : documents) + { + if(modDoc->IsModified()) + { + if(numFiles == 0) + { + // Show the rescue directory in Explorer... + CTrackApp::OpenDirectory(crashDirectory.path); + } + + mpt::PathString filename; + filename += crashDirectory.path; + filename += mpt::PathString::FromUnicode(mpt::ufmt::val(++numFiles)); + filename += P_("_"); + filename += mpt::PathString::FromCString(modDoc->GetTitle()).SanitizeComponent(); + filename += P_("."); + filename += mpt::PathString::FromUTF8(modDoc->GetSoundFile().GetModSpecifications().fileExtension); + + try + { + SaveDocumentSafe(modDoc, filename); + } catch(...) + { + continue; + } + } + } + return numFiles; +} + + +void DebugReporter::ReportError(mpt::ustring errorMessage) +{ + + if(!crashDirectory.valid) + { + errorMessage += UL_("\n\n"); + errorMessage += UL_("Could not create the following directory for saving debug information and modified files to:\n"); + errorMessage += crashDirectory.path.ToUnicode(); + } + + if(HasWrittenDebug()) + { + errorMessage += UL_("\n\n"); + errorMessage += UL_("Debug information has been saved to\n"); + errorMessage += crashDirectory.path.ToUnicode(); + } + + if(rescuedFiles > 0) + { + errorMessage += UL_("\n\n"); + if(rescuedFiles == 1) + { + errorMessage += UL_("1 modified file has been rescued, but it cannot be guaranteed that it is still intact."); + } else + { + errorMessage += MPT_UFORMAT("{} modified files have been rescued, but it cannot be guaranteed that they are still intact.")(rescuedFiles); + } + } + + errorMessage += UL_("\n\n"); + errorMessage += MPT_UFORMAT("OpenMPT {} {} ({} ({}))") + ( Build::GetVersionStringExtended() + , mpt::OS::Windows::Name(mpt::OS::Windows::GetProcessArchitecture()) + , SourceInfo::Current().GetUrlWithRevision() + , SourceInfo::Current().GetStateString() + ); + + errorMessage += UL_("\n\n"); + errorMessage += MPT_UFORMAT("Session error count: {}\n")(crashCount); + if(taintCountDriver > 0 || taintCountPlugin > 0) + { + errorMessage += UL_("Process is in tainted state!\n"); + errorMessage += MPT_UFORMAT("Previously masked driver crashes: {}\n")(taintCountDriver); + errorMessage += MPT_UFORMAT("Previously masked plugin crashes: {}\n")(taintCountPlugin); + } + + errorMessage += UL_("\n"); + + { + mpt::SafeOutputFile sf(crashDirectory.path + P_("error.txt"), std::ios::binary, mpt::FlushMode::Full); + mpt::ofstream& f = sf; + f.imbue(std::locale::classic()); + f << mpt::replace(mpt::ToCharset(mpt::Charset::UTF8, errorMessage), std::string("\n"), std::string("\r\n")); + } + + if(auto ih = CMainFrame::GetInputHandler(); ih != nullptr) + { + mpt::SafeOutputFile sf(crashDirectory.path + P_("last-commands.txt"), std::ios::binary, mpt::FlushMode::Full); + mpt::ofstream &f = sf; + f.imbue(std::locale::classic()); + + const auto commandSet = ih->m_activeCommandSet.get(); + f << "Last commands:\n"; + for(size_t i = 0; i < ih->m_lastCommands.size(); i++) + { + CommandID id = ih->m_lastCommands[(ih->m_lastCommandPos + i) % ih->m_lastCommands.size()]; + if(id == kcNull) + continue; + f << mpt::afmt::val(id); + if(commandSet) + f << " (" << mpt::ToCharset(mpt::Charset::UTF8, commandSet->GetCommandText(id)) << ")"; + f << "\n"; + } + } + + { + mpt::SafeOutputFile sf(crashDirectory.path + P_("threads.txt"), std::ios::binary, mpt::FlushMode::Full); + mpt::ofstream& f = sf; + f.imbue(std::locale::classic()); + f << MPT_AFORMAT("current : {}")(mpt::afmt::hex0<8>(GetCurrentThreadId())) << "\r\n"; + f << MPT_AFORMAT("GUI : {}")(mpt::afmt::hex0<8>(mpt::log::Trace::GetThreadId(mpt::log::Trace::ThreadKindGUI))) << "\r\n"; + f << MPT_AFORMAT("Audio : {}")(mpt::afmt::hex0<8>(mpt::log::Trace::GetThreadId(mpt::log::Trace::ThreadKindAudio))) << "\r\n"; + f << MPT_AFORMAT("Notify : {}")(mpt::afmt::hex0<8>(mpt::log::Trace::GetThreadId(mpt::log::Trace::ThreadKindNotify))) << "\r\n"; + f << MPT_AFORMAT("WatchDir: {}")(mpt::afmt::hex0<8>(mpt::log::Trace::GetThreadId(mpt::log::Trace::ThreadKindWatchdir))) << "\r\n"; + } + + static constexpr struct { const mpt::uchar * section; const mpt::uchar * key; } configAnonymize[] = + { + { UL_("Version"), UL_("InstallGUID") }, + { UL_("Recent File List"), nullptr }, + }; + + { + mpt::SafeOutputFile sf(crashDirectory.path + P_("active-settings.txt"), std::ios::binary, mpt::FlushMode::Full); + mpt::ofstream& f = sf; + f.imbue(std::locale::classic()); + if(theApp.GetpSettings()) + { + SettingsContainer &settings = theApp.GetSettings(); + for(const auto &it : settings) + { + bool skipPath = false; + for(const auto &path : configAnonymize) + { + if((path.key == nullptr && path.section == it.first.GetRefSection()) // Omit entire section + || (path.key != nullptr && it.first == SettingPath(path.section, path.key))) // Omit specific key + { + skipPath = true; + } + } + if(skipPath) + { + continue; + } + f + << mpt::ToCharset(mpt::Charset::UTF8, it.first.FormatAsString() + U_(" = ") + it.second.GetRefValue().FormatValueAsString()) + << std::endl; + } + } + } + + { + const mpt::PathString crashStoredSettingsFilename = crashDirectory.path + P_("stored-mptrack.ini"); + CopyFile + ( theApp.GetConfigFileName().AsNative().c_str() + , crashStoredSettingsFilename.AsNative().c_str() + , FALSE + ); + IniFileSettingsContainer crashStoredSettings{crashStoredSettingsFilename}; + for(const auto &path : configAnonymize) + { + if(path.key) + { + crashStoredSettings.Write(SettingPath(path.section, path.key), SettingValue(mpt::ustring())); + } else + { + crashStoredSettings.Remove(path.section); + } + } + crashStoredSettings.Flush(); + } + + /* + // This is very slow, we instead write active-settings.txt above. + { + IniFileSettingsBackend f(crashDirectory.path + P_("active-mptrack.ini")); + if(theApp.GetpSettings()) + { + SettingsContainer & settings = theApp.GetSettings(); + for(const auto &it : settings) + { + f.WriteSetting(it.first, it.second.GetRefValue()); + } + } + } + */ + + { + mpt::SafeOutputFile sf(crashDirectory.path + P_("about-openmpt.txt"), std::ios::binary, mpt::FlushMode::Full); + mpt::ofstream& f = sf; + f.imbue(std::locale::classic()); + f << mpt::ToCharset(mpt::Charset::UTF8, CAboutDlg::GetTabText(0)); + } + + { + mpt::SafeOutputFile sf(crashDirectory.path + P_("about-components.txt"), std::ios::binary, mpt::FlushMode::Full); + mpt::ofstream& f = sf; + f.imbue(std::locale::classic()); + f << mpt::ToCharset(mpt::Charset::UTF8, CAboutDlg::GetTabText(1)); + } + + Reporting::Error(errorMessage, (mode == DumpModeWarning) ? "OpenMPT Warning" : "OpenMPT Crash", CMainFrame::GetMainFrame()); + +} + + +// Freezes the state as much as possible in order to avoid further confusion by +// other (possibly still running) threads +bool DebugReporter::FreezeState(DumpMode mode) +{ + MPT_TRACE(); + + // seal the trace log as early as possible + mpt::log::Trace::Seal(); + + if(mode == DumpModeCrash || mode == DumpModeWarning) + { + if(CMainFrame::GetMainFrame() && CMainFrame::GetMainFrame()->gpSoundDevice && CMainFrame::GetMainFrame()->gpSoundDevice->DebugIsFragileDevice()) + { + // For fragile devices, always stop the device. Stop before the dumping if not in realtime context. + if(!CMainFrame::GetMainFrame()->gpSoundDevice->DebugInRealtimeCallback()) + { + StopSoundDevice(); + } + } else + { + if(ExceptionHandler::stopSoundDeviceOnCrash && ExceptionHandler::stopSoundDeviceBeforeDump) + { + StopSoundDevice(); + } + } + } + + return true; +} + + +static void StopSoundDeviceSafe(CMainFrame *pMainFrame) +{ + __try + { + if(pMainFrame->gpSoundDevice) + { + pMainFrame->gpSoundDevice->Close(); + } + if(pMainFrame->m_NotifyTimer) + { + pMainFrame->KillTimer(pMainFrame->m_NotifyTimer); + pMainFrame->m_NotifyTimer = 0; + } + } __except(EXCEPTION_EXECUTE_HANDLER) + { + // nothing + } +} + + +void DebugReporter::StopSoundDevice() +{ + CMainFrame* pMainFrame = CMainFrame::GetMainFrame(); + if(pMainFrame) + { + try + { + StopSoundDeviceSafe(pMainFrame); + } catch(...) + { + // nothing + } + } +} + + +bool DebugReporter::Cleanup(DumpMode mode) +{ + MPT_TRACE(); + + if(mode == DumpModeCrash || mode == DumpModeWarning) + { + if(CMainFrame::GetMainFrame() && CMainFrame::GetMainFrame()->gpSoundDevice && CMainFrame::GetMainFrame()->gpSoundDevice->DebugIsFragileDevice()) + { + // For fragile devices, always stop the device. Stop after the dumping if in realtime context. + if(CMainFrame::GetMainFrame()->gpSoundDevice->DebugInRealtimeCallback()) + { + StopSoundDevice(); + } + } else + { + if(ExceptionHandler::stopSoundDeviceOnCrash && !ExceptionHandler::stopSoundDeviceBeforeDump) + { + StopSoundDevice(); + } + } + } + + return true; +} + + +// Different entry points for different situations in which we want to dump some information + + +static bool IsCxxException(_EXCEPTION_POINTERS *pExceptionInfo) +{ + if (!pExceptionInfo) + return false; + if (!pExceptionInfo->ExceptionRecord) + return false; + if (pExceptionInfo->ExceptionRecord->ExceptionCode != 0xE06D7363u) + return false; + return true; +} + + +template <typename E> +static const E * GetCxxException(_EXCEPTION_POINTERS *pExceptionInfo) +{ + // https://blogs.msdn.microsoft.com/oldnewthing/20100730-00/?p=13273 + struct info_a_t + { + DWORD bitmask; // Probably: 1=Const, 2=Volatile + DWORD destructor; // RVA (Relative Virtual Address) of destructor for that exception object + DWORD unknown; + DWORD catchableTypesPtr; // RVA of instance of type "B" + }; + struct info_c_t + { + DWORD someBitmask; + DWORD typeInfo; // RVA of std::type_info for that type + DWORD memberDisplacement; // Add to ExceptionInformation[1] in EXCEPTION_RECORD to obtain 'this' pointer. + DWORD virtBaseRelated1; // -1 if no virtual base + DWORD virtBaseRelated2; // ? + DWORD objectSize; // Size of the object in bytes + DWORD probablyCopyCtr; // RVA of copy constructor (?) + }; + if(!pExceptionInfo) + return nullptr; + if (!pExceptionInfo->ExceptionRecord) + return nullptr; + if(pExceptionInfo->ExceptionRecord->ExceptionCode != 0xE06D7363u) + return nullptr; + #ifdef _WIN64 + if(pExceptionInfo->ExceptionRecord->NumberParameters != 4) + return nullptr; + #else + if(pExceptionInfo->ExceptionRecord->NumberParameters != 3) + return nullptr; + #endif + if(pExceptionInfo->ExceptionRecord->ExceptionInformation[0] != 0x19930520u) + return nullptr; + std::uintptr_t base_address = 0; + #ifdef _WIN64 + base_address = pExceptionInfo->ExceptionRecord->ExceptionInformation[3]; + #else + base_address = 0; + #endif + std::uintptr_t obj_address = pExceptionInfo->ExceptionRecord->ExceptionInformation[1]; + if(!obj_address) + return nullptr; + std::uintptr_t info_a_address = pExceptionInfo->ExceptionRecord->ExceptionInformation[2]; + if(!info_a_address) + return nullptr; + const info_a_t * info_a = reinterpret_cast<const info_a_t *>(info_a_address); + std::uintptr_t info_b_offset = info_a->catchableTypesPtr; + if(!info_b_offset) + return nullptr; + const DWORD * info_b = reinterpret_cast<const DWORD *>(base_address + info_b_offset); + for(DWORD type = 1; type <= info_b[0]; ++type) + { + std::uintptr_t info_c_offset = info_b[type]; + if(!info_c_offset) + continue; + const info_c_t * info_c = reinterpret_cast<const info_c_t *>(base_address + info_c_offset); + if(!info_c->typeInfo) + continue; + const std::type_info * ti = reinterpret_cast<const std::type_info *>(base_address + info_c->typeInfo); + if(*ti != typeid(E)) + continue; + const E * e = reinterpret_cast<const E *>(obj_address + info_c->memberDisplacement); + return e; + } + return nullptr; +} + + +void ExceptionHandler::UnhandledMFCException(CException * e, const MSG * pMsg) +{ + DebugReporter report(DumpModeCrash, nullptr); + mpt::ustring errorMessage; + if(e && dynamic_cast<CSimpleException*>(e)) + { + TCHAR tmp[1024 + 1]; + MemsetZero(tmp); + if(dynamic_cast<CSimpleException*>(e)->GetErrorMessage(tmp, static_cast<UINT>(std::size(tmp) - 1)) != 0) + { + tmp[1024] = 0; + errorMessage = MPT_UFORMAT("Unhandled MFC exception occurred while processming window message '{}': {}.") + (mpt::ufmt::dec(pMsg ? pMsg->message : 0) + , mpt::ToUnicode(CString(tmp)) + ); + } else + { + errorMessage = MPT_UFORMAT("Unhandled MFC exception occurred while processming window message '{}': {}.") + (mpt::ufmt::dec(pMsg ? pMsg->message : 0) + , mpt::ToUnicode(CString(tmp)) + ); + } + } + else + { + errorMessage = MPT_UFORMAT("Unhandled MFC exception occurred while processming window message '{}'.") + ( mpt::ufmt::dec(pMsg ? pMsg->message : 0) + ); + } + report.ReportError(errorMessage); +} + + +static void UnhandledExceptionFilterImpl(_EXCEPTION_POINTERS *pExceptionInfo) +{ + DebugReporter report(DumpModeCrash, pExceptionInfo); + + mpt::ustring errorMessage; + const std::exception * pE = GetCxxException<std::exception>(pExceptionInfo); + if(g_Context) + { + if(!g_Context->description.empty()) + { + errorMessage += MPT_UFORMAT("OpenMPT detected a crash in '{}'.\nThis is very likely not an OpenMPT bug. Please report the problem to the respective software author.\n")(g_Context->description); + } else + { + errorMessage += MPT_UFORMAT("OpenMPT detected a crash in unknown foreign code.\nThis is likely not an OpenMPT bug.\n")(); + } + } + if(pE) + { + const std::exception & e = *pE; + errorMessage += MPT_UFORMAT("Unhandled C++ exception '{}' occurred at address 0x{}: '{}'.") + ( mpt::ToUnicode(mpt::Charset::ASCII, typeid(e).name()) + , mpt::ufmt::hex0<mpt::pointer_size*2>(reinterpret_cast<std::uintptr_t>(pExceptionInfo->ExceptionRecord->ExceptionAddress)) + , mpt::get_exception_text<mpt::ustring>(e) + ); + } else + { + errorMessage += MPT_UFORMAT("Unhandled exception 0x{} at address 0x{} occurred.") + ( mpt::ufmt::HEX0<8>(pExceptionInfo->ExceptionRecord->ExceptionCode) + , mpt::ufmt::hex0<mpt::pointer_size*2>(reinterpret_cast<std::uintptr_t>(pExceptionInfo->ExceptionRecord->ExceptionAddress)) + ); + } + report.ReportError(errorMessage); + +} + + +LONG ExceptionHandler::UnhandledExceptionFilterContinue(_EXCEPTION_POINTERS *pExceptionInfo) +{ + + UnhandledExceptionFilterImpl(pExceptionInfo); + + // Disable the call to std::terminate() as that would re-renter the crash + // handler another time, but with less information available. +#if 0 + // MSVC implements calling std::terminate by its own UnhandledExeptionFilter. + // However, we do overwrite it here, thus we have to call std::terminate + // ourselves. + if (IsCxxException(pExceptionInfo)) + { + std::terminate(); + } +#endif + + // Let a potential debugger handle the exception... + return EXCEPTION_CONTINUE_SEARCH; + +} + + +LONG ExceptionHandler::ExceptionFilter(_EXCEPTION_POINTERS *pExceptionInfo) +{ + UnhandledExceptionFilterImpl(pExceptionInfo); + // Let a potential debugger handle the exception... + return EXCEPTION_EXECUTE_HANDLER; +} + + +static void mpt_unexpected_handler(); +static void mpt_terminate_handler(); + + +void ExceptionHandler::Register() +{ + if(useImplicitFallbackSEH) + { + g_OriginalUnhandledExceptionFilter = ::SetUnhandledExceptionFilter(&UnhandledExceptionFilterContinue); + } + if(handleStdTerminate) + { + g_OriginalTerminateHandler = std::set_terminate(&mpt_terminate_handler); + } +} + + +void ExceptionHandler::ConfigureSystemHandler() +{ +#if (_WIN32_WINNT >= 0x0600) + if(delegateToWindowsHandler) + { + //SetErrorMode(0); + g_OriginalErrorMode = ::GetErrorMode(); + } else + { + g_OriginalErrorMode = ::SetErrorMode(::GetErrorMode() | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + } +#else // _WIN32_WINNT < 0x0600 + if(delegateToWindowsHandler) + { + g_OriginalErrorMode = ::SetErrorMode(0); + } else + { + g_OriginalErrorMode = ::SetErrorMode(::SetErrorMode(0) | SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX); + } +#endif // _WIN32_WINNT +} + + +void ExceptionHandler::UnconfigureSystemHandler() +{ + ::SetErrorMode(g_OriginalErrorMode); + g_OriginalErrorMode = 0; +} + + +void ExceptionHandler::Unregister() +{ + if(handleStdTerminate) + { + std::set_terminate(g_OriginalTerminateHandler); + g_OriginalTerminateHandler = nullptr; + } + if(useImplicitFallbackSEH) + { + ::SetUnhandledExceptionFilter(g_OriginalUnhandledExceptionFilter); + g_OriginalUnhandledExceptionFilter = nullptr; + } +} + + +static void mpt_terminate_handler() +{ + DebugReporter(DumpModeCrash, nullptr).ReportError(U_("A C++ runtime crash occurred: std::terminate() called.")); +#if 1 + std::abort(); +#else + if(g_OriginalTerminateHandler) + { + g_OriginalTerminateHandler(); + } else + { + std::abort(); + } +#endif +} + + +#if defined(MPT_ASSERT_HANDLER_NEEDED) + +MPT_NOINLINE void AssertHandler(const mpt::source_location &loc, const char *expr, const char *msg) +{ + DebugReporter report(msg ? DumpModeWarning : DumpModeCrash, nullptr); + if(IsDebuggerPresent()) + { + OutputDebugString(_T("ASSERT(")); + OutputDebugString(mpt::ToWin(mpt::Charset::ASCII, expr).c_str()); + OutputDebugString(_T(") failed\n")); + DebugBreak(); + } else + { + mpt::ustring errorMessage; + if(msg) + { + errorMessage = MPT_UFORMAT("Internal state inconsistency detected at {}({}). This is just a warning that could potentially lead to a crash later on: {} [{}].") + ( mpt::ToUnicode(mpt::Charset::ASCII, loc.file_name() ? loc.file_name() : "") + , loc.line() + , mpt::ToUnicode(mpt::Charset::ASCII, msg) + , mpt::ToUnicode(mpt::Charset::ASCII, loc.function_name() ? loc.function_name() : "") + ); + } else + { + errorMessage = MPT_UFORMAT("Internal error occurred at {}({}): ASSERT({}) failed in [{}].") + ( mpt::ToUnicode(mpt::Charset::ASCII, loc.file_name() ? loc.file_name() : "") + , loc.line() + , mpt::ToUnicode(mpt::Charset::ASCII, expr) + , mpt::ToUnicode(mpt::Charset::ASCII, loc.function_name() ? loc.function_name() : "") + ); + } + report.ReportError(errorMessage); + } +} + +#endif // MPT_ASSERT_HANDLER_NEEDED + + +void DebugInjectCrash() +{ + DebugReporter(DumpModeCrash, nullptr).ReportError(U_("Injected crash.")); +} + + +void DebugTraceDump() +{ + DebugReporter report(DumpModeDebug, nullptr); +} + + +OPENMPT_NAMESPACE_END |