123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853 |
- /*
- * 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
|