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 <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;
} }

View File

@ -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();
} }

View File

@ -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());

View File

@ -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;
} }

View File

@ -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));

View File

@ -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')