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

View File

@ -22,6 +22,7 @@
#include "gui/dialogues/ConfirmPrompt.h"
#include "gui/dialogues/ErrorMessage.h"
#include "gui/interface/Engine.h"
#include "gui/interface/TextWrapper.h"
#include "Config.h"
#include "SimulationConfig.h"
#include <optional>
@ -104,11 +105,6 @@ static void BlueScreen(String detailMessage, std::optional<std::vector<String>>
auto &engine = ui::Engine::Ref();
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 crashLogPath = ByteString("crash.log");
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";
}
addParapgraph(crashInfo.Build());
engine.g->BlendText(ui::Point((engine.g->Size().X - 440) / 2, 80), errorText, 0xFFFFFF_rgb .WithAlpha(0xFF));
String errorText = crashInfo.Build();
constexpr auto width = 440;
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();
std::cerr << crashLogData << std::endl;
Platform::WriteFile(std::vector<char>(crashLogData.begin(), crashLogData.end()), crashLogPath);
//Death loop

View File

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

View File

@ -3,6 +3,8 @@
#include <execinfo.h>
#include <cstdint>
#include <array>
#include <cxxabi.h>
#include <iostream>
namespace Platform
{
@ -19,7 +21,27 @@ std::optional<std::vector<String>> StackTrace()
{
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
{

View File

@ -7,9 +7,46 @@
#include <psapi.h>
#include <array>
#include <mutex>
#include <cstdint>
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()
{
static std::mutex mx;
@ -22,13 +59,11 @@ std::optional<std::vector<String>> StackTrace()
});
SymInitialize(process, NULL, TRUE);
CONTEXT context;
memset(&context, 0, sizeof(context));
CONTEXT context{};
context.ContextFlags = CONTEXT_FULL;
RtlCaptureContext(&context);
STACKFRAME64 frame;
memset(&frame, 0, sizeof(frame));
STACKFRAME64 frame{};
DWORD machine;
#if defined(_M_IX86)
machine = IMAGE_FILE_MACHINE_I386;
@ -71,24 +106,29 @@ std::optional<std::vector<String>> StackTrace()
std::vector<String> res;
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;
}
auto offset = uintptr_t(frame.AddrPC.Offset);
StringBuilder addr;
addr << "0x" << Format::Hex() << uintptr_t(frame.AddrPC.Offset);
std::array<wchar_t, 1000> moduleBaseName;
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)))
addr << Format::Hex();
if (auto moduleInfo = GetModuleInfo(process, offset))
{
auto offset = uintptr_t(frame.AddrPC.Offset) - uintptr_t(moduleInfo.lpBaseOfDll);
if (offset < moduleInfo.SizeOfImage)
addr << moduleInfo->name << "(";
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());
}

View File

@ -9,9 +9,13 @@ if host_platform == 'windows'
elif host_platform == 'darwin'
# TODO: good impl; current one is only slightly better than nothing
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'
# TODO: again, this is more like "posix" than "linux"
stacktrace_files = files('Execinfo.cpp')
# export symbols so backtrace_symbols works, see above
bluescreen_export_symbols = true
else
stacktrace_files = files('Null.cpp')
endif