Skip to content

Commit

Permalink
Add callstack logs on crash for linux.
Browse files Browse the repository at this point in the history
  • Loading branch information
TLeonardUK committed Mar 24, 2024
1 parent 771bf47 commit df53f99
Show file tree
Hide file tree
Showing 3 changed files with 145 additions and 3 deletions.
140 changes: 139 additions & 1 deletion Source/Shared/Platform/Linux/LinuxPlatform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,13 @@
#include <csignal>
#include <cstdio>
#include <uuid/uuid.h>
#include <execinfo.h>
#include <signal.h>
#include <cstring>
#include <cxxabi.h>
#include <link.h>
#include <dlfcn.h>
#include <sstream>

struct LinuxCtrlSignalHandler
{
Expand Down Expand Up @@ -40,8 +47,37 @@ struct LinuxCtrlSignalHandler

} gLinuxCtrlSignalHandler;

void CrashSignalHandler(int signal)
{
Log("================== Crash Log ==================");
Log("Signal: %i", signal);
Log("");

std::unique_ptr<Callstack> Stack = CaptureCallstack(1);
for (auto& Frame : Stack->Frames)
{
Log("0x%016zx %-50s %s@%zi",
Frame.Address,
Frame.Function.empty() ? "<unknown>" : Frame.Function.c_str(),
Frame.Filename.empty() ? "<unknown>" : 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;
}

Expand Down Expand Up @@ -88,9 +124,111 @@ bool UnloadSymbols()
return true;
}

bool exec(const char* cmd, std::string& result)
{
std::array<char, 128> buffer;
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
if (!pipe)
{
return false;
}
while (fgets(buffer.data(), static_cast<int>(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<Callstack> 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<Callstack> result = std::make_unique<Callstack>();
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<size_t>(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()
Expand Down
2 changes: 1 addition & 1 deletion Source/Shared/Platform/Win32/Win32Platform.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
std::unique_ptr<Callstack> 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() ? "<unknown>" : Frame.Function.c_str(),
Frame.Filename.empty() ? "<unknown>" : Frame.Filename.c_str(),
Expand Down
6 changes: 5 additions & 1 deletion Tools/Build/build/cpp-settings.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down

0 comments on commit df53f99

Please sign in to comment.