Fix bluescreen stack trace not showing exception frames
This commit is contained in:
parent
ba056746b8
commit
5411269fb2
@ -29,6 +29,8 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <SDL.h>
|
#include <SDL.h>
|
||||||
|
#include <exception>
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
void LoadWindowPosition()
|
void LoadWindowPosition()
|
||||||
{
|
{
|
||||||
@ -97,7 +99,7 @@ void TickClient()
|
|||||||
Client::Ref().Tick();
|
Client::Ref().Tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
void BlueScreen(String detailMessage, std::optional<std::vector<String>> stackTrace)
|
static void BlueScreen(String detailMessage, std::optional<std::vector<String>> stackTrace)
|
||||||
{
|
{
|
||||||
auto &engine = ui::Engine::Ref();
|
auto &engine = ui::Engine::Ref();
|
||||||
engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
|
engine.g->BlendFilledRect(engine.g->Size().OriginRect(), 0x1172A9_rgb .WithAlpha(0xD2));
|
||||||
@ -140,16 +142,29 @@ void BlueScreen(String detailMessage, std::optional<std::vector<String>> stackTr
|
|||||||
|
|
||||||
//Death loop
|
//Death loop
|
||||||
SDL_Event event;
|
SDL_Event event;
|
||||||
while(true)
|
auto running = true;
|
||||||
|
while (running)
|
||||||
{
|
{
|
||||||
while (SDL_PollEvent(&event))
|
while (SDL_PollEvent(&event))
|
||||||
if(event.type == SDL_QUIT)
|
{
|
||||||
exit(-1); // Don't use Platform::Exit, we're practically zombies at this point anyway.
|
if (event.type == SDL_QUIT)
|
||||||
|
{
|
||||||
|
running = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
blit(engine.g->Data());
|
blit(engine.g->Data());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Don't use Platform::Exit, we're practically zombies at this point anyway.
|
||||||
|
#if defined(__MINGW32__) || defined(__APPLE__)
|
||||||
|
// Come on...
|
||||||
|
exit(-1);
|
||||||
|
#else
|
||||||
|
quick_exit(-1);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
struct
|
static struct
|
||||||
{
|
{
|
||||||
int sig;
|
int sig;
|
||||||
const char *message;
|
const char *message;
|
||||||
@ -161,7 +176,7 @@ struct
|
|||||||
{ 0, nullptr },
|
{ 0, nullptr },
|
||||||
};
|
};
|
||||||
|
|
||||||
void SigHandler(int signal)
|
static void SigHandler(int signal)
|
||||||
{
|
{
|
||||||
const char *message = "Unknown signal";
|
const char *message = "Unknown signal";
|
||||||
for (auto *msg = signalMessages; msg->message; ++msg)
|
for (auto *msg = signalMessages; msg->message; ++msg)
|
||||||
@ -172,7 +187,29 @@ void SigHandler(int signal)
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BlueScreen(ByteString(message).FromUtf8(), Platform::StackTrace(Platform::stackTraceFromHere));
|
BlueScreen(ByteString(message).FromUtf8(), Platform::StackTrace());
|
||||||
|
}
|
||||||
|
|
||||||
|
static void TerminateHandler()
|
||||||
|
{
|
||||||
|
ByteString err = "std::terminate called without a current exception";
|
||||||
|
auto eptr = std::current_exception();
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (eptr)
|
||||||
|
{
|
||||||
|
std::rethrow_exception(eptr);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (const std::exception &e)
|
||||||
|
{
|
||||||
|
err = "unhandled exception: " + ByteString(e.what());
|
||||||
|
}
|
||||||
|
catch (...)
|
||||||
|
{
|
||||||
|
err = "unhandled exception not derived from std::exception, cannot determine reason";
|
||||||
|
}
|
||||||
|
BlueScreen(err.FromUtf8(), Platform::StackTrace());
|
||||||
}
|
}
|
||||||
|
|
||||||
constexpr int SCALE_MAXIMUM = 10;
|
constexpr int SCALE_MAXIMUM = 10;
|
||||||
@ -431,6 +468,7 @@ int Main(int argc, char *argv[])
|
|||||||
{
|
{
|
||||||
signal(msg->sig, SigHandler);
|
signal(msg->sig, SigHandler);
|
||||||
}
|
}
|
||||||
|
std::set_terminate(TerminateHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
if constexpr (X86)
|
if constexpr (X86)
|
||||||
@ -438,114 +476,97 @@ int Main(int argc, char *argv[])
|
|||||||
X86KillDenormals();
|
X86KillDenormals();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto wrapWithBluescreen = [&]() {
|
explicitSingletons->simulationData = std::make_unique<SimulationData>();
|
||||||
explicitSingletons->simulationData = std::make_unique<SimulationData>();
|
explicitSingletons->gameController = std::make_unique<GameController>();
|
||||||
explicitSingletons->gameController = std::make_unique<GameController>();
|
auto *gameController = explicitSingletons->gameController.get();
|
||||||
auto *gameController = explicitSingletons->gameController.get();
|
engine.ShowWindow(gameController->GetView());
|
||||||
engine.ShowWindow(gameController->GetView());
|
|
||||||
|
|
||||||
auto openArg = arguments["open"];
|
auto openArg = arguments["open"];
|
||||||
if (openArg.has_value())
|
if (openArg.has_value())
|
||||||
|
{
|
||||||
|
if constexpr (DEBUG)
|
||||||
{
|
{
|
||||||
if constexpr (DEBUG)
|
std::cout << "Loading " << openArg.value() << std::endl;
|
||||||
{
|
|
||||||
std::cout << "Loading " << openArg.value() << std::endl;
|
|
||||||
}
|
|
||||||
if (Platform::FileExists(openArg.value()))
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
std::vector<char> gameSaveData;
|
|
||||||
if (!Platform::ReadFile(gameSaveData, openArg.value()))
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Could not read file");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
auto newFile = std::make_unique<SaveFile>(openArg.value());
|
|
||||||
auto newSave = std::make_unique<GameSave>(std::move(gameSaveData));
|
|
||||||
newFile->SetGameSave(std::move(newSave));
|
|
||||||
gameController->LoadSaveFile(std::move(newFile));
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (std::exception & e)
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
new ErrorMessage("Error", "Could not open file");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
if (Platform::FileExists(openArg.value()))
|
||||||
auto ptsaveArg = arguments["ptsave"];
|
|
||||||
if (ptsaveArg.has_value())
|
|
||||||
{
|
{
|
||||||
engine.g->Clear();
|
|
||||||
engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
|
|
||||||
String loadingText = "Loading save...";
|
|
||||||
engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle);
|
|
||||||
|
|
||||||
blit(engine.g->Data());
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ByteString saveIdPart;
|
std::vector<char> gameSaveData;
|
||||||
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
|
if (!Platform::ReadFile(gameSaveData, openArg.value()))
|
||||||
{
|
{
|
||||||
if (split.Before() != "ptsave")
|
new ErrorMessage("Error", "Could not read file");
|
||||||
throw std::runtime_error("Not a ptsave link");
|
|
||||||
saveIdPart = split.After().SplitBy('#').Before();
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
throw std::runtime_error("Invalid save link");
|
{
|
||||||
|
auto newFile = std::make_unique<SaveFile>(openArg.value());
|
||||||
|
auto newSave = std::make_unique<GameSave>(std::move(gameSaveData));
|
||||||
|
newFile->SetGameSave(std::move(newSave));
|
||||||
|
gameController->LoadSaveFile(std::move(newFile));
|
||||||
|
}
|
||||||
|
|
||||||
if (!saveIdPart.size())
|
|
||||||
throw std::runtime_error("No Save ID");
|
|
||||||
if constexpr (DEBUG)
|
|
||||||
{
|
|
||||||
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
|
|
||||||
}
|
|
||||||
ByteString saveHistoryPart = "0";
|
|
||||||
if (auto split = saveIdPart.SplitBy('@'))
|
|
||||||
{
|
|
||||||
saveHistoryPart = split.After();
|
|
||||||
saveIdPart = split.Before();
|
|
||||||
}
|
|
||||||
int saveId = saveIdPart.ToNumber<int>();
|
|
||||||
int saveHistory = saveHistoryPart.ToNumber<int>();
|
|
||||||
gameController->OpenSavePreview(saveId, saveHistory, savePreviewUrl);
|
|
||||||
}
|
}
|
||||||
catch (std::exception & e)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
|
||||||
Platform::MarkPresentable();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Platform::MarkPresentable();
|
new ErrorMessage("Error", "Could not open file");
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
MainLoop();
|
auto ptsaveArg = arguments["ptsave"];
|
||||||
};
|
if (ptsaveArg.has_value())
|
||||||
|
|
||||||
if (enableBluescreen)
|
|
||||||
{
|
{
|
||||||
|
engine.g->Clear();
|
||||||
|
engine.g->DrawRect(RectSized(engine.g->Size() / 2 - Vec2(100, 25), Vec2(200, 50)), 0xB4B4B4_rgb);
|
||||||
|
String loadingText = "Loading save...";
|
||||||
|
engine.g->BlendText(engine.g->Size() / 2 - Vec2((Graphics::TextSize(loadingText).X - 1) / 2, 5), loadingText, style::Colour::InformationTitle);
|
||||||
|
|
||||||
|
blit(engine.g->Data());
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
wrapWithBluescreen();
|
ByteString saveIdPart;
|
||||||
|
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
|
||||||
|
{
|
||||||
|
if (split.Before() != "ptsave")
|
||||||
|
throw std::runtime_error("Not a ptsave link");
|
||||||
|
saveIdPart = split.After().SplitBy('#').Before();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
throw std::runtime_error("Invalid save link");
|
||||||
|
|
||||||
|
if (!saveIdPart.size())
|
||||||
|
throw std::runtime_error("No Save ID");
|
||||||
|
if constexpr (DEBUG)
|
||||||
|
{
|
||||||
|
std::cout << "Got Ptsave: id: " << saveIdPart << std::endl;
|
||||||
|
}
|
||||||
|
ByteString saveHistoryPart = "0";
|
||||||
|
if (auto split = saveIdPart.SplitBy('@'))
|
||||||
|
{
|
||||||
|
saveHistoryPart = split.After();
|
||||||
|
saveIdPart = split.Before();
|
||||||
|
}
|
||||||
|
int saveId = saveIdPart.ToNumber<int>();
|
||||||
|
int saveHistory = saveHistoryPart.ToNumber<int>();
|
||||||
|
gameController->OpenSavePreview(saveId, saveHistory, savePreviewUrl);
|
||||||
}
|
}
|
||||||
catch (const std::exception &e)
|
catch (std::exception & e)
|
||||||
{
|
{
|
||||||
BlueScreen(ByteString(e.what()).FromUtf8(), Platform::StackTrace(Platform::stackTraceFromException));
|
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
|
||||||
|
Platform::MarkPresentable();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
wrapWithBluescreen();
|
Platform::MarkPresentable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MainLoop();
|
||||||
|
|
||||||
Platform::Exit(0);
|
Platform::Exit(0);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -69,12 +69,7 @@ namespace Platform
|
|||||||
|
|
||||||
int InvokeMain(int argc, char *argv[]);
|
int InvokeMain(int argc, char *argv[]);
|
||||||
|
|
||||||
enum StackTraceType
|
std::optional<std::vector<String>> StackTrace();
|
||||||
{
|
|
||||||
stackTraceFromHere,
|
|
||||||
stackTraceFromException,
|
|
||||||
};
|
|
||||||
std::optional<std::vector<String>> StackTrace(StackTraceType StackTraceType);
|
|
||||||
|
|
||||||
void MarkPresentable();
|
void MarkPresentable();
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@
|
|||||||
|
|
||||||
namespace Platform
|
namespace Platform
|
||||||
{
|
{
|
||||||
std::optional<std::vector<String>> StackTrace(StackTraceType)
|
std::optional<std::vector<String>> StackTrace()
|
||||||
{
|
{
|
||||||
std::array<void *, 100> buf;
|
std::array<void *, 100> buf;
|
||||||
auto used = backtrace(&buf[0], buf.size());
|
auto used = backtrace(&buf[0], buf.size());
|
||||||
|
@ -2,7 +2,7 @@
|
|||||||
|
|
||||||
namespace Platform
|
namespace Platform
|
||||||
{
|
{
|
||||||
std::optional<std::vector<String>> StackTrace(StackTraceType)
|
std::optional<std::vector<String>> StackTrace()
|
||||||
{
|
{
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
@ -8,23 +8,9 @@
|
|||||||
#include <array>
|
#include <array>
|
||||||
#include <mutex>
|
#include <mutex>
|
||||||
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
|
||||||
extern "C" void **__cdecl __current_exception_context();
|
|
||||||
#endif
|
|
||||||
|
|
||||||
static CONTEXT *CurrentExceptionContext()
|
|
||||||
{
|
|
||||||
// TODO: find the corresponding hack for mingw and older msvc; stack traces printed for exceptions are broken without it
|
|
||||||
CONTEXT **pctx = nullptr;
|
|
||||||
#if defined(_MSC_VER) && _MSC_VER >= 1900
|
|
||||||
pctx = (CONTEXT **)__current_exception_context();
|
|
||||||
#endif
|
|
||||||
return pctx ? *pctx : nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
namespace Platform
|
namespace Platform
|
||||||
{
|
{
|
||||||
std::optional<std::vector<String>> StackTrace(StackTraceType stackTraceType)
|
std::optional<std::vector<String>> StackTrace()
|
||||||
{
|
{
|
||||||
static std::mutex mx;
|
static std::mutex mx;
|
||||||
std::unique_lock lk(mx);
|
std::unique_lock lk(mx);
|
||||||
@ -38,27 +24,8 @@ std::optional<std::vector<String>> StackTrace(StackTraceType stackTraceType)
|
|||||||
|
|
||||||
CONTEXT context;
|
CONTEXT context;
|
||||||
memset(&context, 0, sizeof(context));
|
memset(&context, 0, sizeof(context));
|
||||||
switch (stackTraceType)
|
context.ContextFlags = CONTEXT_FULL;
|
||||||
{
|
RtlCaptureContext(&context);
|
||||||
case stackTraceFromException:
|
|
||||||
if (auto *ec = CurrentExceptionContext())
|
|
||||||
{
|
|
||||||
if (ec->ContextFlags)
|
|
||||||
{
|
|
||||||
context = *ec;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!context.ContextFlags)
|
|
||||||
{
|
|
||||||
return std::nullopt;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
context.ContextFlags = CONTEXT_FULL;
|
|
||||||
RtlCaptureContext(&context);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
STACKFRAME64 frame;
|
STACKFRAME64 frame;
|
||||||
memset(&frame, 0, sizeof(frame));
|
memset(&frame, 0, sizeof(frame));
|
||||||
|
@ -7,8 +7,8 @@ if host_platform == 'windows'
|
|||||||
endif
|
endif
|
||||||
stacktrace_files = files('Windows.cpp')
|
stacktrace_files = files('Windows.cpp')
|
||||||
elif host_platform == 'darwin'
|
elif host_platform == 'darwin'
|
||||||
# stacktrace_files = files('Execinfo.cpp') # macos has this but it's useless >_>
|
# TODO: good impl; current one is only slightly better than nothing
|
||||||
stacktrace_files = files('Null.cpp')
|
stacktrace_files = files('Execinfo.cpp')
|
||||||
elif host_platform == 'linux'
|
elif host_platform == 'linux'
|
||||||
# TODO: again, this is more like "posix" than "linux"
|
# TODO: again, this is more like "posix" than "linux"
|
||||||
stacktrace_files = files('Execinfo.cpp')
|
stacktrace_files = files('Execinfo.cpp')
|
||||||
|
Reference in New Issue
Block a user