winamp/Src/external_dependencies/openmpt-trunk/mptrack/ExceptionHandler.cpp
2024-09-24 14:54:57 +02:00

853 lines
23 KiB
C++

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