From df53f99498aa8e502810f61822ef6fa17ed326ef Mon Sep 17 00:00:00 2001 From: Tim Leonard Date: Sun, 24 Mar 2024 18:50:29 +0000 Subject: [PATCH] Add callstack logs on crash for linux. --- .../Shared/Platform/Linux/LinuxPlatform.cpp | 140 +++++++++++++++++- .../Shared/Platform/Win32/Win32Platform.cpp | 2 +- Tools/Build/build/cpp-settings.cmake | 6 +- 3 files changed, 145 insertions(+), 3 deletions(-) diff --git a/Source/Shared/Platform/Linux/LinuxPlatform.cpp b/Source/Shared/Platform/Linux/LinuxPlatform.cpp index 99c7eaca..8fda09c7 100644 --- a/Source/Shared/Platform/Linux/LinuxPlatform.cpp +++ b/Source/Shared/Platform/Linux/LinuxPlatform.cpp @@ -13,6 +13,13 @@ #include #include #include +#include +#include +#include +#include +#include +#include +#include struct LinuxCtrlSignalHandler { @@ -40,8 +47,37 @@ struct LinuxCtrlSignalHandler } gLinuxCtrlSignalHandler; +void CrashSignalHandler(int signal) +{ + Log("================== Crash Log =================="); + Log("Signal: %i", signal); + Log(""); + + std::unique_ptr Stack = CaptureCallstack(1); + for (auto& Frame : Stack->Frames) + { + Log("0x%016zx %-50s %s@%zi", + Frame.Address, + Frame.Function.empty() ? "" : Frame.Function.c_str(), + Frame.Filename.empty() ? "" : Frame.Filename.c_str(), + Frame.Line + ); + } + + exit(1); +} + bool PlatformInit() { + signal(SIGSEGV, CrashSignalHandler); + signal(SIGBUS, CrashSignalHandler); + signal(SIGFPE, CrashSignalHandler); + signal(SIGILL, CrashSignalHandler); + signal(SIGABRT, CrashSignalHandler); + signal(SIGSYS, CrashSignalHandler); + signal(SIGXCPU, CrashSignalHandler); + signal(SIGXFSZ, CrashSignalHandler); + return true; } @@ -88,9 +124,111 @@ bool UnloadSymbols() return true; } +bool exec(const char* cmd, std::string& result) +{ + std::array buffer; + std::unique_ptr pipe(popen(cmd, "r"), pclose); + if (!pipe) + { + return false; + } + while (fgets(buffer.data(), static_cast(buffer.size()), pipe.get()) != nullptr) + { + result += buffer.data(); + } + return true; +} + +size_t GetVmaAddress(size_t Addr) +{ + Dl_info info; + struct link_map* link_map; + dladdr1((void*)Addr, &info, (void**)&link_map, RTLD_DL_LINKMAP); + return Addr - link_map->l_addr; +} + std::unique_ptr CaptureCallstack(size_t FrameOffset, size_t FrameCount) { - return {}; + void* FramePointers[128]; + size_t MaxFrames = std::min((size_t)128, FrameCount); + size_t FramesCaptured = backtrace(FramePointers, MaxFrames); + + std::unique_ptr result = std::make_unique(); + if (FramesCaptured <= FrameOffset) + { + return result; + } + + char** FrameSymbols = backtrace_symbols(FramePointers, FramesCaptured); + + result->Frames.resize(FramesCaptured - FrameOffset); + for (size_t i = FrameOffset; i < FramesCaptured; i++) + { + Callstack::Frame& frame = result->Frames[i - FrameOffset]; + + frame.Address = GetVmaAddress(reinterpret_cast(FramePointers[i])); + frame.Function = ""; + frame.Module = ""; + frame.Filename = ""; + frame.Line = 0; + + // Get module for resolving filename/line + Dl_info info; + if (dladdr(FramePointers[i], &info)) + { + // Try and resolve filename/line. + char cmd[256]; + snprintf(cmd, sizeof(cmd), "addr2line -e %s -Cis %zx", info.dli_fname, frame.Address); + + std::string cmdOutput; + if (exec(cmd, cmdOutput)) + { + if (const char* Ptr = strchr(cmdOutput.c_str(), ':'); Ptr != nullptr) + { + frame.Filename.assign(cmdOutput.c_str(), std::distance(cmdOutput.c_str(), Ptr)); + if (frame.Filename == "??") + { + frame.Filename = ""; + } + + frame.Line = atoi(Ptr + 1); + } + } + } + + // Extract module + const char* SymbolPointer = FrameSymbols[i]; + if (const char* Ptr = strchr(SymbolPointer, '('); Ptr != nullptr) + { + frame.Module.assign(SymbolPointer, std::distance(SymbolPointer, Ptr)); + SymbolPointer = Ptr + 1; + } + // Extract and demangle function. + if (const char* Ptr = strchr(SymbolPointer, ')'); Ptr != nullptr) + { + if (const char* PlusPtr = strchr(SymbolPointer, '+'); PlusPtr != nullptr && PlusPtr < Ptr) + { + Ptr = PlusPtr; + } + + size_t NameLength = std::distance(SymbolPointer, Ptr); + if (NameLength >= 1) + { + frame.Function.assign(SymbolPointer, NameLength); + + // Demangle the symbol. + int Status = 0; + const char* DemangledName = abi::__cxa_demangle(frame.Function.c_str(), nullptr, 0, &Status); + if (DemangledName && Status == 0) + { + frame.Function = DemangledName; + } + } + SymbolPointer = Ptr + 1; + } + } + + return result; } std::string MakeGUID() diff --git a/Source/Shared/Platform/Win32/Win32Platform.cpp b/Source/Shared/Platform/Win32/Win32Platform.cpp index ce63f88a..eb30a332 100644 --- a/Source/Shared/Platform/Win32/Win32Platform.cpp +++ b/Source/Shared/Platform/Win32/Win32Platform.cpp @@ -63,7 +63,7 @@ LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo) std::unique_ptr Stack = CaptureCallstack(1); for (auto& Frame : Stack->Frames) { - Log("0x%016p %-30s %s@%zi", + Log("0x%016zx %-50s %s@%zi", Frame.Address, Frame.Function.empty() ? "" : Frame.Function.c_str(), Frame.Filename.empty() ? "" : Frame.Filename.c_str(), diff --git a/Tools/Build/build/cpp-settings.cmake b/Tools/Build/build/cpp-settings.cmake index f26dfc6a..173a612b 100644 --- a/Tools/Build/build/cpp-settings.cmake +++ b/Tools/Build/build/cpp-settings.cmake @@ -27,7 +27,11 @@ elseif (UNIX) # Use permissive flags, GCC/clang are stricter that MSVC. # We should go in and fix up the problematic areas and remove this later. - SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive") + SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -g") + + # Use dynamic linker flag so we can lookup symbols at runtime. + # Also enforce symbolic information in all builds. + set(LINK_OPTIONS ${LINK_OPTIONS} -rdynamic -g) endif()