blob: 3e1b7fe6a15198f85a9f9bb1a6f04587593baeec [file] [log] [blame]
Ed Tanous50c50c22017-05-12 16:58:06 -07001/** ==========================================================================
2 * 2011 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
3 * with no warranties. This code is yours to share, use and modify with no
4 * strings attached and no restrictions or obligations.
5 *
6 * For more information see g3log/LICENSE or refer refer to http://unlicense.org
7 * ============================================================================*/
8
9#include "g3log/crashhandler.hpp"
10#include "g3log/logmessage.hpp"
11#include "g3log/logcapture.hpp"
12#include "g3log/loglevels.hpp"
13
14#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
15#error "crashhandler_unix.cpp used but it's a windows system"
16#endif
17
18
19#include <csignal>
20#include <cstring>
21#include <unistd.h>
22#include <execinfo.h>
23#include <cxxabi.h>
24#include <cstdlib>
25#include <sstream>
26#include <iostream>
27#include <thread>
28#include <atomic>
29#include <map>
30#include <mutex>
31
32// Linux/Clang, OSX/Clang, OSX/gcc
33#if (defined(__clang__) || defined(__APPLE__))
34#include <sys/ucontext.h>
35#else
36#include <ucontext.h>
37#endif
38
39
40namespace {
41
42 const std::map<int, std::string> kSignals = {
43 {SIGABRT, "SIGABRT"},
44 {SIGFPE, "SIGFPE"},
45 {SIGILL, "SIGILL"},
46 {SIGSEGV, "SIGSEGV"},
47 {SIGTERM, "SIGTERM"},
48 };
49
50 std::map<int, std::string> gSignals = kSignals;
51
52
53 bool shouldDoExit() {
54 static std::atomic<uint64_t> firstExit{0};
55 auto const count = firstExit.fetch_add(1, std::memory_order_relaxed);
56 return (0 == count);
57 }
58
59 void restoreSignalHandler(int signal_number) {
60#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
61 struct sigaction action;
62 memset(&action, 0, sizeof (action)); //
63 sigemptyset(&action.sa_mask);
64 action.sa_handler = SIG_DFL; // take default action for the signal
65 sigaction(signal_number, &action, NULL);
66#endif
67 }
68
69
70 // Dump of stack,. then exit through g3log background worker
71 // ALL thanks to this thread at StackOverflow. Pretty much borrowed from:
72 // Ref: http://stackoverflow.com/questions/77005/how-to-generate-a-stacktrace-when-my-gcc-c-app-crashes
73 void signalHandler(int signal_number, siginfo_t* info, void* unused_context) {
74
75 // Only one signal will be allowed past this point
76 if (false == shouldDoExit()) {
77 while (true) {
78 std::this_thread::sleep_for(std::chrono::seconds(1));
79 }
80 }
81
82 using namespace g3::internal;
83 {
84 const auto dump = stackdump();
85 std::ostringstream fatal_stream;
86 const auto fatal_reason = exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
87 fatal_stream << "Received fatal signal: " << fatal_reason;
88 fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
89 fatal_stream << "\n***** SIGNAL " << fatal_reason << "(" << signal_number << ")" << std::endl;
90 LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
91 trigger.stream() << fatal_stream.str();
92 } // message sent to g3LogWorker
93 // wait to die
94 }
95
96
97
98 //
99 // Installs FATAL signal handler that is enough to handle most fatal events
100 // on *NIX systems
101 void installSignalHandler() {
102#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
103 struct sigaction action;
104 memset(&action, 0, sizeof (action));
105 sigemptyset(&action.sa_mask);
106 action.sa_sigaction = &signalHandler; // callback to crashHandler for fatal signals
107 // sigaction to use sa_sigaction file. ref: http://www.linuxprogrammingblog.com/code-examples/sigaction
108 action.sa_flags = SA_SIGINFO;
109
110 // do it verbose style - install all signal actions
111 for (const auto& sig_pair : gSignals) {
112 if (sigaction(sig_pair.first, &action, nullptr) < 0) {
113 const std::string error = "sigaction - " + sig_pair.second;
114 perror(error.c_str());
115 }
116 }
117#endif
118 }
119
120
121
122} // end anonymous namespace
123
124
125
126
127
128
129// Redirecting and using signals. In case of fatal signals g3log should log the fatal signal
130// and flush the log queue and then "rethrow" the signal to exit
131namespace g3 {
132 // References:
133 // sigaction : change the default action if a specific signal is received
134 // http://linux.die.net/man/2/sigaction
135 // http://publib.boulder.ibm.com/infocenter/aix/v6r1/index.jsp?topic=%2Fcom.ibm.aix.basetechref%2Fdoc%2Fbasetrf2%2Fsigaction.html
136 //
137 // signal: http://linux.die.net/man/7/signal and
138 // http://msdn.microsoft.com/en-us/library/xdkz3x12%28vs.71%29.asp
139 //
140 // memset + sigemptyset: Maybe unnecessary to do both but there seems to be some confusion here
141 // ,plenty of examples when both or either are used
142 // http://stackoverflow.com/questions/6878546/why-doesnt-parent-process-return-to-the-exact-location-after-handling-signal_number
143 namespace internal {
144
145 bool shouldBlockForFatalHandling() {
146 return true; // For windows we will after fatal processing change it to false
147 }
148
149
150 /// Generate stackdump. Or in case a stackdump was pre-generated and non-empty just use that one
151 /// i.e. the latter case is only for Windows and test purposes
152 std::string stackdump(const char* rawdump) {
153 if (nullptr != rawdump && !std::string(rawdump).empty()) {
154 return {rawdump};
155 }
156
157 const size_t max_dump_size = 50;
158 void* dump[max_dump_size];
159 size_t size = backtrace(dump, max_dump_size);
160 char** messages = backtrace_symbols(dump, static_cast<int>(size)); // overwrite sigaction with caller's address
161
162 // dump stack: skip first frame, since that is here
163 std::ostringstream oss;
164 for (size_t idx = 1; idx < size && messages != nullptr; ++idx) {
165 char* mangled_name = 0, *offset_begin = 0, *offset_end = 0;
166 // find parantheses and +address offset surrounding mangled name
167 for (char* p = messages[idx]; *p; ++p) {
168 if (*p == '(') {
169 mangled_name = p;
170 } else if (*p == '+') {
171 offset_begin = p;
172 } else if (*p == ')') {
173 offset_end = p;
174 break;
175 }
176 }
177
178 // if the line could be processed, attempt to demangle the symbol
179 if (mangled_name && offset_begin && offset_end &&
180 mangled_name < offset_begin) {
181 *mangled_name++ = '\0';
182 *offset_begin++ = '\0';
183 *offset_end++ = '\0';
184
185 int status;
186 char* real_name = abi::__cxa_demangle(mangled_name, 0, 0, &status);
187 // if demangling is successful, output the demangled function name
188 if (status == 0) {
189 oss << "\n\tstack dump [" << idx << "] " << messages[idx] << " : " << real_name << "+";
190 oss << offset_begin << offset_end << std::endl;
191 }// otherwise, output the mangled function name
192 else {
193 oss << "\tstack dump [" << idx << "] " << messages[idx] << mangled_name << "+";
194 oss << offset_begin << offset_end << std::endl;
195 }
196 free(real_name); // mallocated by abi::__cxa_demangle(...)
197 } else {
198 // no demangling done -- just dump the whole line
199 oss << "\tstack dump [" << idx << "] " << messages[idx] << std::endl;
200 }
201 } // END: for(size_t idx = 1; idx < size && messages != nullptr; ++idx)
202 free(messages);
203 return oss.str();
204 }
205
206
207
208 /// string representation of signal ID
209 std::string exitReasonName(const LEVELS& level, g3::SignalType fatal_id) {
210
211 int signal_number = static_cast<int>(fatal_id);
212 switch (signal_number) {
213 case SIGABRT: return "SIGABRT";
214 break;
215 case SIGFPE: return "SIGFPE";
216 break;
217 case SIGSEGV: return "SIGSEGV";
218 break;
219 case SIGILL: return "SIGILL";
220 break;
221 case SIGTERM: return "SIGTERM";
222 break;
223 default:
224 std::ostringstream oss;
225 oss << "UNKNOWN SIGNAL(" << signal_number << ") for " << level.text;
226 return oss.str();
227 }
228 }
229
230
231
232 // Triggered by g3log->g3LogWorker after receiving a FATAL trigger
233 // which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught.
234 // --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
235 void exitWithDefaultSignalHandler(const LEVELS& level, g3::SignalType fatal_signal_id) {
236 const int signal_number = static_cast<int>(fatal_signal_id);
237 restoreSignalHandler(signal_number);
238 std::cerr << "\n\n" << __FUNCTION__ << ":" << __LINE__ << ". Exiting due to " << level.text << ", " << signal_number << " \n\n" << std::flush;
239
240
241 kill(getpid(), signal_number);
242 exit(signal_number);
243
244 }
245 } // end g3::internal
246
247
248 // This will override the default signal handler setup and instead
249 // install a custom set of signals to handle
250 void overrideSetupSignals(const std::map<int, std::string> overrideSignals) {
251 static std::mutex signalLock;
252 std::lock_guard<std::mutex> guard(signalLock);
253 for (const auto& sig : gSignals) {
254 restoreSignalHandler(sig.first);
255 }
256
257 gSignals = overrideSignals;
258 installCrashHandler(); // installs all the signal handling for gSignals
259 }
260
261 // restores the signal handler back to default
262 void restoreSignalHandlerToDefault() {
263 overrideSetupSignals(kSignals);
264 }
265
266
267 // installs the signal handling for whatever signal set that is currently active
268 // If you want to setup your own signal handling then
269 // You should instead call overrideSetupSignals()
270 void installCrashHandler() {
271 installSignalHandler();
272 }
273} // end namespace g3
274