Attempt to show and demangle symbols in bluescreen stack traces

This commit is contained in:
Tamás Bálint Misius 2024-01-02 18:35:33 +01:00
parent 9a05e56f15
commit d05b0093b9
No known key found for this signature in database
GPG Key ID: 5B472A12F6ECA9F2
6 changed files with 99 additions and 24 deletions

View File

@ -365,6 +365,8 @@ project_deps = []
data_files = [] data_files = []
powder_deps = [] powder_deps = []
project_export_dynamic = false
subdir('src') subdir('src')
subdir('resources') subdir('resources')
@ -411,6 +413,7 @@ if get_option('build_powder')
win_subsystem: is_debug ? 'console' : 'windows', win_subsystem: is_debug ? 'console' : 'windows',
link_args: project_link_args, link_args: project_link_args,
dependencies: powder_deps, dependencies: powder_deps,
export_dynamic: project_export_dynamic,
install: true, install: true,
) )
endif endif
@ -439,6 +442,7 @@ if get_option('build_render')
cpp_args: project_cpp_args, cpp_args: project_cpp_args,
link_args: render_link_args, link_args: render_link_args,
dependencies: render_deps, dependencies: render_deps,
export_dynamic: project_export_dynamic,
) )
endif endif
@ -462,5 +466,6 @@ if get_option('build_font')
cpp_args: project_cpp_args, cpp_args: project_cpp_args,
link_args: project_link_args, link_args: project_link_args,
dependencies: font_deps, dependencies: font_deps,
export_dynamic: project_export_dynamic,
) )
endif endif

View File

@ -22,6 +22,7 @@
#include "gui/dialogues/ConfirmPrompt.h" #include "gui/dialogues/ConfirmPrompt.h"
#include "gui/dialogues/ErrorMessage.h" #include "gui/dialogues/ErrorMessage.h"
#include "gui/interface/Engine.h" #include "gui/interface/Engine.h"
#include "gui/interface/TextWrapper.h"
#include "Config.h" #include "Config.h"
#include "SimulationConfig.h" #include "SimulationConfig.h"
#include <optional> #include <optional>
@ -104,11 +105,6 @@ static void BlueScreen(String detailMessage, std::optional<std::vector<String>>
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));
String errorText;
auto addParapgraph = [&errorText](String str) {
errorText += str + "\n\n";
};
auto crashPrevLogPath = ByteString("crash.prev.log"); auto crashPrevLogPath = ByteString("crash.prev.log");
auto crashLogPath = ByteString("crash.log"); auto crashLogPath = ByteString("crash.log");
Platform::RenameFile(crashLogPath, crashPrevLogPath, true); Platform::RenameFile(crashLogPath, crashPrevLogPath, true);
@ -133,11 +129,14 @@ static void BlueScreen(String detailMessage, std::optional<std::vector<String>>
{ {
crashInfo << "Stack trace not available\n"; crashInfo << "Stack trace not available\n";
} }
addParapgraph(crashInfo.Build()); String errorText = crashInfo.Build();
constexpr auto width = 440;
engine.g->BlendText(ui::Point((engine.g->Size().X - 440) / 2, 80), errorText, 0xFFFFFF_rgb .WithAlpha(0xFF)); ui::TextWrapper tw;
tw.Update(errorText, true, width);
engine.g->BlendText(ui::Point((engine.g->Size().X - width) / 2, 80), tw.WrappedText(), 0xFFFFFF_rgb .WithAlpha(0xFF));
auto crashLogData = errorText.ToUtf8(); auto crashLogData = errorText.ToUtf8();
std::cerr << crashLogData << std::endl;
Platform::WriteFile(std::vector<char>(crashLogData.begin(), crashLogData.end()), crashLogPath); Platform::WriteFile(std::vector<char>(crashLogData.begin(), crashLogData.end()), crashLogPath);
//Death loop //Death loop

View File

@ -78,10 +78,15 @@ else
) )
endif endif
bluescreen_export_symbols = false
subdir('stacktrace') subdir('stacktrace')
if use_bluescreen if use_bluescreen
common_files += stacktrace_files common_files += stacktrace_files
if bluescreen_export_symbols
project_export_dynamic = true
endif
else else
common_files += files('stacktrace/Null.cpp') common_files += files('stacktrace/Null.cpp')
endif endif

View File

@ -3,6 +3,8 @@
#include <execinfo.h> #include <execinfo.h>
#include <cstdint> #include <cstdint>
#include <array> #include <array>
#include <cxxabi.h>
#include <iostream>
namespace Platform namespace Platform
{ {
@ -19,7 +21,27 @@ std::optional<std::vector<String>> StackTrace()
{ {
if (strs) if (strs)
{ {
res.push_back(ByteString(strs[i]).FromUtf8()); auto line = ByteString(strs[i]);
if (auto beginSymbolName = line.SplitBy('('))
{
auto afterBeginSymbolName = beginSymbolName.After();
if (auto endSymbolName = afterBeginSymbolName.SplitBy('+'))
{
auto beforeSymbolName = beginSymbolName.Before();
auto symbolName = endSymbolName.Before();
auto afterSymbolName = endSymbolName.After();
int status;
char *demangled = abi::__cxa_demangle(symbolName.c_str(), NULL, NULL, &status);
Defer freeDemangled([demangled]() {
free(demangled);
});
if (!status)
{
line = ByteString::Build(beforeSymbolName, "(", demangled, "+", afterSymbolName);
}
}
}
res.push_back(line.FromUtf8());
} }
else else
{ {

View File

@ -7,9 +7,46 @@
#include <psapi.h> #include <psapi.h>
#include <array> #include <array>
#include <mutex> #include <mutex>
#include <cstdint>
namespace Platform namespace Platform
{ {
struct SymbolInfo
{
String name;
uintptr_t displacement;
};
static std::optional<SymbolInfo> GetSymbolInfo(HANDLE process, uintptr_t offset)
{
DWORD64 displacement;
std::array<char, sizeof(SYMBOL_INFOW) + 1000> symbolData{};
auto &symbol = *reinterpret_cast<SYMBOL_INFOW *>(&symbolData[0]);
symbol.SizeOfStruct = sizeof(symbol);
symbol.MaxNameLen = symbolData.size() - sizeof(symbol);
if (SymFromAddrW(process, offset, &displacement, &symbol))
{
return SymbolInfo{ WinNarrow(&symbol.Name[0]).FromUtf8(), uintptr_t(displacement) };
}
return std::nullopt;
}
struct ModuleInfo
{
String name;
uintptr_t displacement;
};
static std::optional<ModuleInfo> GetModuleInfo(HANDLE process, uintptr_t offset)
{
IMAGEHLP_MODULEW64 module{};
module.SizeOfStruct = sizeof(module);
if (SymGetModuleInfoW64(process, offset, &module))
{
auto displacement = offset - uintptr_t(module.BaseOfImage);
return ModuleInfo{ WinNarrow(&module.LoadedImageName[0]).FromUtf8(), displacement };
}
return std::nullopt;
}
std::optional<std::vector<String>> StackTrace() std::optional<std::vector<String>> StackTrace()
{ {
static std::mutex mx; static std::mutex mx;
@ -22,13 +59,11 @@ std::optional<std::vector<String>> StackTrace()
}); });
SymInitialize(process, NULL, TRUE); SymInitialize(process, NULL, TRUE);
CONTEXT context; CONTEXT context{};
memset(&context, 0, sizeof(context));
context.ContextFlags = CONTEXT_FULL; context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context); RtlCaptureContext(&context);
STACKFRAME64 frame; STACKFRAME64 frame{};
memset(&frame, 0, sizeof(frame));
DWORD machine; DWORD machine;
#if defined(_M_IX86) #if defined(_M_IX86)
machine = IMAGE_FILE_MACHINE_I386; machine = IMAGE_FILE_MACHINE_I386;
@ -71,24 +106,29 @@ std::optional<std::vector<String>> StackTrace()
std::vector<String> res; std::vector<String> res;
for (auto i = 0; i < 100; ++i) for (auto i = 0; i < 100; ++i)
{ {
if (!StackWalk64( machine, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL)) if (!StackWalk64(machine, process, thread, &frame, &context, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL))
{ {
break; break;
} }
auto offset = uintptr_t(frame.AddrPC.Offset);
StringBuilder addr; StringBuilder addr;
addr << "0x" << Format::Hex() << uintptr_t(frame.AddrPC.Offset); addr << Format::Hex();
std::array<wchar_t, 1000> moduleBaseName; if (auto moduleInfo = GetModuleInfo(process, offset))
HMODULE module;
MODULEINFO moduleInfo;
if (GetModuleHandleExW(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, (LPCWSTR)frame.AddrPC.Offset, &module) &&
GetModuleBaseNameW(process, module, &moduleBaseName[0], moduleBaseName.size()) &&
GetModuleInformation(process, module, &moduleInfo, sizeof(moduleInfo)))
{ {
auto offset = uintptr_t(frame.AddrPC.Offset) - uintptr_t(moduleInfo.lpBaseOfDll); addr << moduleInfo->name << "(";
if (offset < moduleInfo.SizeOfImage) if (auto symbolInfo = GetSymbolInfo(process, offset))
{ {
addr << " (" << WinNarrow(&moduleBaseName[0]).FromUtf8() << "+0x" << Format::Hex() << offset << ")"; addr << symbolInfo->name << "+0x" << symbolInfo->displacement;
} }
else
{
addr << "+0x" << moduleInfo->displacement;
}
addr << ") [0x" << offset << "]";
}
else
{
addr << "0x" << offset;
} }
res.push_back(addr.Build()); res.push_back(addr.Build());
} }

View File

@ -9,9 +9,13 @@ if host_platform == 'windows'
elif host_platform == 'darwin' elif host_platform == 'darwin'
# TODO: good impl; current one is only slightly better than nothing # TODO: good impl; current one is only slightly better than nothing
stacktrace_files = files('Execinfo.cpp') stacktrace_files = files('Execinfo.cpp')
# export symbols so backtrace_symbols works, see https://www.gnu.org/software/libc/manual/html_node/Backtraces.html
bluescreen_export_symbols = true
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')
# export symbols so backtrace_symbols works, see above
bluescreen_export_symbols = true
else else
stacktrace_files = files('Null.cpp') stacktrace_files = files('Null.cpp')
endif endif