1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "Prerequisites/BsPrerequisitesUtil.h"
4
5#include <cxxabi.h>
6#include <execinfo.h>
7#include <dlfcn.h>
8#include <csignal>
9
10namespace bs
11{
12 INT32 SIGNALS[] = { SIGFPE, SIGILL, SIGSEGV, SIGTERM };
13 struct sigaction gSavedSignals[4];
14
15 void signalHandler(int signal, siginfo_t* info, void* context)
16 {
17 // Restore old signal handlers
18 INT32 i = 0;
19 for(auto& entry : SIGNALS)
20 {
21 sigaction(entry, &gSavedSignals[i], nullptr);
22 i++;
23 }
24
25 const char* signalNameSz = strsignal(signal);
26
27 String signalName;
28 if(signalNameSz)
29 signalName = signalNameSz;
30 else
31 signalName = "Unknown signal #" + toString(signal);
32
33 // Note: Not safe to grab a stack-trace here (nor do memory allocations), but we might as well try since we're
34 // crashing anyway
35 CrashHandler::instance().reportCrash(signalName, "Received fatal signal", "", "");
36
37 kill(getpid(), signal);
38 exit(signal);
39 }
40
41 CrashHandler::CrashHandler()
42 {
43 struct sigaction action;
44 sigemptyset(&action.sa_mask);
45 action.sa_sigaction = &signalHandler;
46 action.sa_flags = SA_SIGINFO;
47
48 INT32 i = 0;
49 for(auto& entry : SIGNALS)
50 {
51 memset(&gSavedSignals[i], 0, sizeof(struct sigaction));
52 sigaction(entry, &action, &gSavedSignals[i]);
53
54 i++;
55 }
56 }
57
58 CrashHandler::~CrashHandler() {}
59
60 String CrashHandler::getCrashTimestamp()
61 {
62 std::time_t t = time(0);
63 struct tm *now = localtime(&t);
64
65 String timeStamp = "{0}{1}{2}_{3}{4}";
66 String strYear = toString(now->tm_year, 4, '0');
67 String strMonth = toString(now->tm_mon, 2, '0');
68 String strDay = toString(now->tm_mday, 2, '0');
69 String strHour = toString(now->tm_hour, 2, '0');
70 String strMinute = toString(now->tm_min, 2, '0');
71 return StringUtil::format(timeStamp, strYear, strMonth, strDay, strHour, strMinute);
72 }
73
74 String CrashHandler::getStackTrace()
75 {
76 StringStream stackTrace;
77 void* trace[BS_MAX_STACKTRACE_DEPTH];
78
79 int traceSize = backtrace(trace, BS_MAX_STACKTRACE_DEPTH);
80 char** messages = backtrace_symbols(trace, traceSize);
81
82 for (int i = 0; i < traceSize && messages != nullptr; ++i)
83 {
84#if BS_PLATFORM == BS_PLATFORM_OSX
85 stackTrace << std::to_string(i) << ") " << messages[i];
86
87 // Try parsing a human readable name
88 Dl_info info;
89 if (dladdr(trace[i], &info) && info.dli_sname)
90 {
91 stackTrace << ": ";
92
93 if (info.dli_sname[0] == '_')
94 {
95 int status = -1;
96 char* demangledName = abi::__cxa_demangle(info.dli_sname, nullptr, nullptr, &status);
97
98 if(status == 0)
99 stackTrace << demangledName;
100 else
101 stackTrace << info.dli_sname;
102
103 free(demangledName);
104 }
105 else
106 stackTrace << info.dli_sname;
107
108 // Try to find the line number
109 for (char *p = messages[i]; *p; ++p)
110 {
111 if (*p == '+')
112 {
113 stackTrace << " " << p;
114 break;
115 }
116 }
117 }
118 else
119 stackTrace << String(messages[i]);
120#elif BS_PLATFORM == BS_PLATFORM_LINUX
121 // Try to find the characters surrounding the mangled name: '(' and '+'
122 char* mangedName = nullptr;
123 char* offsetBegin = nullptr;
124 char* offsetEnd = nullptr;
125 for (char *p = messages[i]; *p; ++p)
126 {
127 if (*p == '(')
128 mangedName = p;
129 else if (*p == '+')
130 offsetBegin = p;
131 else if (*p == ')')
132 {
133 offsetEnd = p;
134 break;
135 }
136 }
137
138 bool lineContainsMangledSymbol = mangedName != nullptr && offsetBegin != nullptr && offsetEnd != nullptr &&
139 mangedName < offsetBegin;
140
141 stackTrace << toString(i) << ") ";
142
143 if (lineContainsMangledSymbol)
144 {
145 *mangedName++ = '\0';
146 *offsetBegin++ = '\0';
147 *offsetEnd++ = '\0';
148
149 int status;
150 char *real_name = abi::__cxa_demangle(mangedName, 0, 0, &status);
151 char *output_name = status == 0 /* Demangling successful */? real_name : mangedName;
152 stackTrace << String(messages[i])
153 << ": " << output_name
154 << "+" << offsetBegin << offsetEnd;
155
156 free(real_name);
157 }
158 else
159 stackTrace << String(messages[i]);
160#endif
161
162 if (i < traceSize - 1)
163 stackTrace << "\n";
164 }
165
166 free(messages);
167
168 return stackTrace.str();
169 }
170
171 void CrashHandler::reportCrash(const String& type,
172 const String& description,
173 const String& function,
174 const String& file,
175 UINT32 line) const
176 {
177 logErrorAndStackTrace(type, description, function, file, line);
178 saveCrashLog();
179
180 // Allow the debugger a chance to attach
181 std::raise(SIGINT);
182 }
183}
184