|
13 | 13 | #include <csignal> |
14 | 14 | #include <cstdio> |
15 | 15 | #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> |
16 | 23 |
|
17 | 24 | struct LinuxCtrlSignalHandler |
18 | 25 | { |
@@ -40,8 +47,37 @@ struct LinuxCtrlSignalHandler |
40 | 47 |
|
41 | 48 | } gLinuxCtrlSignalHandler; |
42 | 49 |
|
| 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 | + |
43 | 70 | bool PlatformInit() |
44 | 71 | { |
| 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 | + |
45 | 81 | return true; |
46 | 82 | } |
47 | 83 |
|
@@ -88,9 +124,111 @@ bool UnloadSymbols() |
88 | 124 | return true; |
89 | 125 | } |
90 | 126 |
|
| 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 | + |
91 | 150 | std::unique_ptr<Callstack> CaptureCallstack(size_t FrameOffset, size_t FrameCount) |
92 | 151 | { |
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; |
94 | 232 | } |
95 | 233 |
|
96 | 234 | std::string MakeGUID() |
|
0 commit comments