Fix bluescreen stack trace not showing exception frames

This commit is contained in:
Tamás Bálint Misius 2024-01-02 08:53:21 +01:00
parent ba056746b8
commit 5411269fb2
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
6 changed files with 115 additions and 132 deletions

View File

@ -29,6 +29,8 @@
#include <iostream>
#include <csignal>
#include <SDL.h>
#include <exception>
#include <cstdlib>
void LoadWindowPosition()
{
@ -97,7 +99,7 @@ void TickClient()
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();
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
SDL_Event event;
while(true)
auto running = true;
while (running)
{
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());
}
// 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;
const char *message;
@ -161,7 +176,7 @@ struct
{ 0, nullptr },
};
void SigHandler(int signal)
static void SigHandler(int signal)
{
const char *message = "Unknown signal";
for (auto *msg = signalMessages; msg->message; ++msg)
@ -172,7 +187,29 @@ void SigHandler(int signal)
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;
@ -431,6 +468,7 @@ int Main(int argc, char *argv[])
{
signal(msg->sig, SigHandler);
}
std::set_terminate(TerminateHandler);
}
if constexpr (X86)
@ -438,114 +476,97 @@ int Main(int argc, char *argv[])
X86KillDenormals();
}
auto wrapWithBluescreen = [&]() {
explicitSingletons->simulationData = std::make_unique<SimulationData>();
explicitSingletons->gameController = std::make_unique<GameController>();
auto *gameController = explicitSingletons->gameController.get();
engine.ShowWindow(gameController->GetView());
explicitSingletons->simulationData = std::make_unique<SimulationData>();
explicitSingletons->gameController = std::make_unique<GameController>();
auto *gameController = explicitSingletons->gameController.get();
engine.ShowWindow(gameController->GetView());
auto openArg = arguments["open"];
if (openArg.has_value())
auto openArg = arguments["open"];
if (openArg.has_value())
{
if constexpr (DEBUG)
{
if constexpr (DEBUG)
{
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");
}
std::cout << "Loading " << openArg.value() << std::endl;
}
auto ptsaveArg = arguments["ptsave"];
if (ptsaveArg.has_value())
if (Platform::FileExists(openArg.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
{
ByteString saveIdPart;
if (ByteString::Split split = ptsaveArg.value().SplitBy(':'))
std::vector<char> gameSaveData;
if (!Platform::ReadFile(gameSaveData, openArg.value()))
{
if (split.Before() != "ptsave")
throw std::runtime_error("Not a ptsave link");
saveIdPart = split.After().SplitBy('#').Before();
new ErrorMessage("Error", "Could not read file");
}
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)
{
new ErrorMessage("Error", ByteString(e.what()).FromUtf8());
Platform::MarkPresentable();
new ErrorMessage("Error", "Could not open save file:\n" + ByteString(e.what()).FromUtf8()) ;
}
}
else
{
Platform::MarkPresentable();
new ErrorMessage("Error", "Could not open file");
}
}
MainLoop();
};
if (enableBluescreen)
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
{
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
{
wrapWithBluescreen();
Platform::MarkPresentable();
}
MainLoop();
Platform::Exit(0);
return 0;
}

View File

@ -69,12 +69,7 @@ namespace Platform
int InvokeMain(int argc, char *argv[]);
enum StackTraceType
{
stackTraceFromHere,
stackTraceFromException,
};
std::optional<std::vector<String>> StackTrace(StackTraceType StackTraceType);
std::optional<std::vector<String>> StackTrace();
void MarkPresentable();
}

View File

@ -6,7 +6,7 @@
namespace Platform
{
std::optional<std::vector<String>> StackTrace(StackTraceType)
std::optional<std::vector<String>> StackTrace()
{
std::array<void *, 100> buf;
auto used = backtrace(&buf[0], buf.size());

View File

@ -2,7 +2,7 @@
namespace Platform
{
std::optional<std::vector<String>> StackTrace(StackTraceType)
std::optional<std::vector<String>> StackTrace()
{
return std::nullopt;
}

View File

@ -8,23 +8,9 @@
#include <array>
#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
{
std::optional<std::vector<String>> StackTrace(StackTraceType stackTraceType)
std::optional<std::vector<String>> StackTrace()
{
static std::mutex mx;
std::unique_lock lk(mx);
@ -38,27 +24,8 @@ std::optional<std::vector<String>> StackTrace(StackTraceType stackTraceType)
CONTEXT context;
memset(&context, 0, sizeof(context));
switch (stackTraceType)
{
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;
}
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
STACKFRAME64 frame;
memset(&frame, 0, sizeof(frame));

View File

@ -7,8 +7,8 @@ if host_platform == 'windows'
endif
stacktrace_files = files('Windows.cpp')
elif host_platform == 'darwin'
# stacktrace_files = files('Execinfo.cpp') # macos has this but it's useless >_>
stacktrace_files = files('Null.cpp')
# TODO: good impl; current one is only slightly better than nothing
stacktrace_files = files('Execinfo.cpp')
elif host_platform == 'linux'
# TODO: again, this is more like "posix" than "linux"
stacktrace_files = files('Execinfo.cpp')