Skip to content

Commit df53f99

Browse files
committed
Add callstack logs on crash for linux.
1 parent 771bf47 commit df53f99

File tree

3 files changed

+145
-3
lines changed

3 files changed

+145
-3
lines changed

Source/Shared/Platform/Linux/LinuxPlatform.cpp

Lines changed: 139 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,13 @@
1313
#include <csignal>
1414
#include <cstdio>
1515
#include <uuid/uuid.h>
16+
#include <execinfo.h>
17+
#include <signal.h>
18+
#include <cstring>
19+
#include <cxxabi.h>
20+
#include <link.h>
21+
#include <dlfcn.h>
22+
#include <sstream>
1623

1724
struct LinuxCtrlSignalHandler
1825
{
@@ -40,8 +47,37 @@ struct LinuxCtrlSignalHandler
4047

4148
} gLinuxCtrlSignalHandler;
4249

50+
void CrashSignalHandler(int signal)
51+
{
52+
Log("================== Crash Log ==================");
53+
Log("Signal: %i", signal);
54+
Log("");
55+
56+
std::unique_ptr<Callstack> Stack = CaptureCallstack(1);
57+
for (auto& Frame : Stack->Frames)
58+
{
59+
Log("0x%016zx %-50s %s@%zi",
60+
Frame.Address,
61+
Frame.Function.empty() ? "<unknown>" : Frame.Function.c_str(),
62+
Frame.Filename.empty() ? "<unknown>" : Frame.Filename.c_str(),
63+
Frame.Line
64+
);
65+
}
66+
67+
exit(1);
68+
}
69+
4370
bool PlatformInit()
4471
{
72+
signal(SIGSEGV, CrashSignalHandler);
73+
signal(SIGBUS, CrashSignalHandler);
74+
signal(SIGFPE, CrashSignalHandler);
75+
signal(SIGILL, CrashSignalHandler);
76+
signal(SIGABRT, CrashSignalHandler);
77+
signal(SIGSYS, CrashSignalHandler);
78+
signal(SIGXCPU, CrashSignalHandler);
79+
signal(SIGXFSZ, CrashSignalHandler);
80+
4581
return true;
4682
}
4783

@@ -88,9 +124,111 @@ bool UnloadSymbols()
88124
return true;
89125
}
90126

127+
bool exec(const char* cmd, std::string& result)
128+
{
129+
std::array<char, 128> buffer;
130+
std::unique_ptr<FILE, decltype(&pclose)> pipe(popen(cmd, "r"), pclose);
131+
if (!pipe)
132+
{
133+
return false;
134+
}
135+
while (fgets(buffer.data(), static_cast<int>(buffer.size()), pipe.get()) != nullptr)
136+
{
137+
result += buffer.data();
138+
}
139+
return true;
140+
}
141+
142+
size_t GetVmaAddress(size_t Addr)
143+
{
144+
Dl_info info;
145+
struct link_map* link_map;
146+
dladdr1((void*)Addr, &info, (void**)&link_map, RTLD_DL_LINKMAP);
147+
return Addr - link_map->l_addr;
148+
}
149+
91150
std::unique_ptr<Callstack> CaptureCallstack(size_t FrameOffset, size_t FrameCount)
92151
{
93-
return {};
152+
void* FramePointers[128];
153+
size_t MaxFrames = std::min((size_t)128, FrameCount);
154+
size_t FramesCaptured = backtrace(FramePointers, MaxFrames);
155+
156+
std::unique_ptr<Callstack> result = std::make_unique<Callstack>();
157+
if (FramesCaptured <= FrameOffset)
158+
{
159+
return result;
160+
}
161+
162+
char** FrameSymbols = backtrace_symbols(FramePointers, FramesCaptured);
163+
164+
result->Frames.resize(FramesCaptured - FrameOffset);
165+
for (size_t i = FrameOffset; i < FramesCaptured; i++)
166+
{
167+
Callstack::Frame& frame = result->Frames[i - FrameOffset];
168+
169+
frame.Address = GetVmaAddress(reinterpret_cast<size_t>(FramePointers[i]));
170+
frame.Function = "";
171+
frame.Module = "";
172+
frame.Filename = "";
173+
frame.Line = 0;
174+
175+
// Get module for resolving filename/line
176+
Dl_info info;
177+
if (dladdr(FramePointers[i], &info))
178+
{
179+
// Try and resolve filename/line.
180+
char cmd[256];
181+
snprintf(cmd, sizeof(cmd), "addr2line -e %s -Cis %zx", info.dli_fname, frame.Address);
182+
183+
std::string cmdOutput;
184+
if (exec(cmd, cmdOutput))
185+
{
186+
if (const char* Ptr = strchr(cmdOutput.c_str(), ':'); Ptr != nullptr)
187+
{
188+
frame.Filename.assign(cmdOutput.c_str(), std::distance(cmdOutput.c_str(), Ptr));
189+
if (frame.Filename == "??")
190+
{
191+
frame.Filename = "";
192+
}
193+
194+
frame.Line = atoi(Ptr + 1);
195+
}
196+
}
197+
}
198+
199+
// Extract module
200+
const char* SymbolPointer = FrameSymbols[i];
201+
if (const char* Ptr = strchr(SymbolPointer, '('); Ptr != nullptr)
202+
{
203+
frame.Module.assign(SymbolPointer, std::distance(SymbolPointer, Ptr));
204+
SymbolPointer = Ptr + 1;
205+
}
206+
// Extract and demangle function.
207+
if (const char* Ptr = strchr(SymbolPointer, ')'); Ptr != nullptr)
208+
{
209+
if (const char* PlusPtr = strchr(SymbolPointer, '+'); PlusPtr != nullptr && PlusPtr < Ptr)
210+
{
211+
Ptr = PlusPtr;
212+
}
213+
214+
size_t NameLength = std::distance(SymbolPointer, Ptr);
215+
if (NameLength >= 1)
216+
{
217+
frame.Function.assign(SymbolPointer, NameLength);
218+
219+
// Demangle the symbol.
220+
int Status = 0;
221+
const char* DemangledName = abi::__cxa_demangle(frame.Function.c_str(), nullptr, 0, &Status);
222+
if (DemangledName && Status == 0)
223+
{
224+
frame.Function = DemangledName;
225+
}
226+
}
227+
SymbolPointer = Ptr + 1;
228+
}
229+
}
230+
231+
return result;
94232
}
95233

96234
std::string MakeGUID()

Source/Shared/Platform/Win32/Win32Platform.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,7 @@ LONG WINAPI ExceptionHandler(PEXCEPTION_POINTERS pExceptionInfo)
6363
std::unique_ptr<Callstack> Stack = CaptureCallstack(1);
6464
for (auto& Frame : Stack->Frames)
6565
{
66-
Log("0x%016p %-30s %s@%zi",
66+
Log("0x%016zx %-50s %s@%zi",
6767
Frame.Address,
6868
Frame.Function.empty() ? "<unknown>" : Frame.Function.c_str(),
6969
Frame.Filename.empty() ? "<unknown>" : Frame.Filename.c_str(),

Tools/Build/build/cpp-settings.cmake

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,11 @@ elseif (UNIX)
2727

2828
# Use permissive flags, GCC/clang are stricter that MSVC.
2929
# We should go in and fix up the problematic areas and remove this later.
30-
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive")
30+
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fpermissive -g")
31+
32+
# Use dynamic linker flag so we can lookup symbols at runtime.
33+
# Also enforce symbolic information in all builds.
34+
set(LINK_OPTIONS ${LINK_OPTIONS} -rdynamic -g)
3135

3236
endif()
3337

0 commit comments

Comments
 (0)