blob: acaebc6a2d101286a18e70996a81eec51cf7962f [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#if !(defined(WIN32) || defined(_WIN32) || defined(__WIN32__))
10#error "crashhandler_windows.cpp used but not on a windows system"
11#endif
12
13#include <windows.h>
14#include <csignal>
15#include <sstream>
16#include <atomic>
17#include <process.h> // getpid
18#include "g3log/crashhandler.hpp"
19#include "g3log/stacktrace_windows.hpp"
20#include "g3log/logcapture.hpp"
21
22#define getpid _getpid
23
24namespace {
25 std::atomic<bool> gBlockForFatal {true};
26 LPTOP_LEVEL_EXCEPTION_FILTER g_previous_unexpected_exception_handler = nullptr;
27
28#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
29 thread_local bool g_installed_thread_signal_handler = false;
30#endif
31
32#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
33 void *g_vector_exception_handler = nullptr;
34#endif
35
36
37
38 // Restore back to default fatal event handling
39 void ReverseToOriginalFatalHandling() {
40 SetUnhandledExceptionFilter (g_previous_unexpected_exception_handler);
41
42#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
43 RemoveVectoredExceptionHandler (g_vector_exception_handler);
44#endif
45
46#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
47 if (SIG_ERR == signal(SIGABRT, SIG_DFL))
48 perror("signal - SIGABRT");
49
50 if (SIG_ERR == signal(SIGFPE, SIG_DFL))
51 perror("signal - SIGABRT");
52
53 if (SIG_ERR == signal(SIGSEGV, SIG_DFL))
54 perror("signal - SIGABRT");
55
56 if (SIG_ERR == signal(SIGILL, SIG_DFL))
57 perror("signal - SIGABRT");
58
59 if (SIG_ERR == signal(SIGTERM, SIG_DFL))
60 perror("signal - SIGABRT");
61#endif
62 }
63
64
65
66 // called for fatal signals SIGABRT, SIGFPE, SIGSEGV, SIGILL, SIGTERM
67 void signalHandler(int signal_number) {
68 using namespace g3::internal;
69 std::string dump = stacktrace::stackdump();
70
71 std::ostringstream fatal_stream;
72 fatal_stream << "\n***** Received fatal signal " << g3::internal::exitReasonName(g3::internal::FATAL_SIGNAL, signal_number);
73 fatal_stream << "(" << signal_number << ")\tPID: " << getpid() << std::endl;
74
75
76 LogCapture trigger(FATAL_SIGNAL, static_cast<g3::SignalType>(signal_number), dump.c_str());
77 trigger.stream() << fatal_stream.str();
78
79 // Trigger debug break point, if we're in debug. This breakpoint CAN cause a slowdown when it happens.
80 // Be patient. The "Debug" dialogue should pop-up eventually if you doing it in Visual Studio.
81 // For fatal signals only, not exceptions.
82 // This is a way to tell the IDE (if in dev mode) that it can stop at this breakpoint
83 // Note that at this time the fatal log event with stack trace is NOT yet flushed to the logger
84 // This call will do nothing unless we're in DEBUG and "DEBUG_BREAK_AT_FATAL_SIGNAL" is enabled
85 // ref: g3log/Options.cmake
86#if (!defined(NDEBUG) && defined(DEBUG_BREAK_AT_FATAL_SIGNAL))
87 __debugbreak();
88#endif
89 } // scope exit - message sent to LogWorker, wait to die...
90
91
92
93 // Unhandled exception catching
94 LONG WINAPI exceptionHandling(EXCEPTION_POINTERS *info, const std::string &handler) {
95 std::string dump = stacktrace::stackdump(info);
96
97 std::ostringstream fatal_stream;
98 const g3::SignalType exception_code = info->ExceptionRecord->ExceptionCode;
99 fatal_stream << "\n***** " << handler << ": Received fatal exception " << g3::internal::exitReasonName(g3::internal::FATAL_EXCEPTION, exception_code);
100 fatal_stream << "\tPID: " << getpid() << std::endl;
101
102 const auto fatal_id = static_cast<g3::SignalType>(exception_code);
103 LogCapture trigger(g3::internal::FATAL_EXCEPTION, fatal_id, dump.c_str());
104 trigger.stream() << fatal_stream.str();
105 // FATAL Exception: It doesn't necessarily stop here we pass on continue search
106 // if no one else will catch that then it's goodbye anyhow.
107 // The RISK here is if someone is cathing this and returning "EXCEPTION_EXECUTE_HANDLER"
108 // but does not shutdown then the software will be running with g3log shutdown.
109 // .... However... this must be seen as a bug from standard handling of fatal exceptions
110 // https://msdn.microsoft.com/en-us/library/6wxdsc38.aspx
111 return EXCEPTION_CONTINUE_SEARCH;
112 }
113
114
115 // Unhandled exception catching
116 LONG WINAPI unexpectedExceptionHandling(EXCEPTION_POINTERS *info) {
117 ReverseToOriginalFatalHandling();
118 return exceptionHandling(info, "Unexpected Exception Handler");
119 }
120
121
122 /// Setup through (Windows API) AddVectoredExceptionHandler
123 /// Ref: http://blogs.msdn.com/b/zhanli/archive/2010/06/25/c-tips-addvectoredexceptionhandler-addvectoredcontinuehandler-and-setunhandledexceptionfilter.aspx
124#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
125 LONG WINAPI vectorExceptionHandling(PEXCEPTION_POINTERS p) {
126 const g3::SignalType exception_code = p->ExceptionRecord->ExceptionCode;
127 if (false == stacktrace::isKnownException(exception_code)) {
128 // The unknown exception is ignored. Since it is not a Windows
129 // fatal exception generated by the OS we leave the
130 // responsibility to deal with this by the client software.
131 return EXCEPTION_CONTINUE_SEARCH;
132 } else {
133 ReverseToOriginalFatalHandling();
134 return exceptionHandling(p, "Vectored Exception Handler");
135 }
136 }
137#endif
138
139
140
141
142} // end anonymous namespace
143
144
145namespace g3 {
146 namespace internal {
147 // For windows exceptions this might ONCE be set to false, in case of a
148 // windows exceptions and not a signal
149 bool shouldBlockForFatalHandling() {
150 return gBlockForFatal;
151 }
152
153
154 /// Generate stackdump. Or in case a stackdump was pre-generated and
155 /// non-empty just use that one. i.e. the latter case is only for
156 /// Windows and test purposes
157 std::string stackdump(const char *dump) {
158 if (nullptr != dump && !std::string(dump).empty()) {
159 return {dump};
160 }
161
162 return stacktrace::stackdump();
163 }
164
165
166
167 /// string representation of signal ID or Windows exception id
168 std::string exitReasonName(const LEVELS &level, g3::SignalType fatal_id) {
169 if (level == g3::internal::FATAL_EXCEPTION) {
170 return stacktrace::exceptionIdToText(fatal_id);
171 }
172
173 switch (fatal_id) {
174 case SIGABRT: return "SIGABRT"; break;
175 case SIGFPE: return "SIGFPE"; break;
176 case SIGSEGV: return "SIGSEGV"; break;
177 case SIGILL: return "SIGILL"; break;
178 case SIGTERM: return "SIGTERM"; break;
179 default:
180 std::ostringstream oss;
181 oss << "UNKNOWN SIGNAL(" << fatal_id << ")";
182 return oss.str();
183 }
184 }
185
186
187 // Triggered by g3log::LogWorker after receiving a FATAL trigger
188 // which is LOG(FATAL), CHECK(false) or a fatal signal our signalhandler caught.
189 // --- If LOG(FATAL) or CHECK(false) the signal_number will be SIGABRT
190 void exitWithDefaultSignalHandler(const LEVELS &level, g3::SignalType fatal_signal_id) {
191
192 ReverseToOriginalFatalHandling();
193 // For windows exceptions we want to continue the possibility of
194 // exception handling now when the log and stacktrace are flushed
195 // to sinks. We therefore avoid to kill the preocess here. Instead
196 // it will be the exceptionHandling functions above that
197 // will let exception handling continue with: EXCEPTION_CONTINUE_SEARCH
198 if (g3::internal::FATAL_EXCEPTION == level) {
199 gBlockForFatal = false;
200 return;
201 }
202
203 // for a sigal however, we exit through that fatal signal
204 const int signal_number = static_cast<int>(fatal_signal_id);
205 raise(signal_number);
206 }
207
208
209
210 void installSignalHandler() {
211 g3::installSignalHandlerForThread();
212 }
213
214
215 } // end g3::internal
216
217
218 /// SIGFPE, SIGILL, and SIGSEGV handling must be installed per thread
219 /// on Windows. This is automatically done if you do at least one LOG(...) call
220 /// you can also use this function call, per thread so make sure these three
221 /// fatal signals are covered in your thread (even if you don't do a LOG(...) call
222 void installSignalHandlerForThread() {
223#if !(defined(DISABLE_FATAL_SIGNALHANDLING))
224 if (!g_installed_thread_signal_handler) {
225 g_installed_thread_signal_handler = true;
226 if (SIG_ERR == signal(SIGTERM, signalHandler))
227 perror("signal - SIGTERM");
228 if (SIG_ERR == signal(SIGABRT, signalHandler))
229 perror("signal - SIGABRT");
230 if (SIG_ERR == signal(SIGFPE, signalHandler))
231 perror("signal - SIGFPE");
232 if (SIG_ERR == signal(SIGSEGV, signalHandler))
233 perror("signal - SIGSEGV");
234 if (SIG_ERR == signal(SIGILL, signalHandler))
235 perror("signal - SIGILL");
236 }
237#endif
238 }
239
240 void installCrashHandler() {
241 internal::installSignalHandler();
242 g_previous_unexpected_exception_handler = SetUnhandledExceptionFilter(unexpectedExceptionHandling);
243
244#if !(defined(DISABLE_VECTORED_EXCEPTIONHANDLING))
245 // const size_t kFirstExceptionHandler = 1;
246 // kFirstExeptionsHandler is kept here for documentational purposes.
247 // The last exception seems more what we want
248 const size_t kLastExceptionHandler = 0;
249 g_vector_exception_handler = AddVectoredExceptionHandler(kLastExceptionHandler, vectorExceptionHandling);
250#endif
251 }
252
253} // end namespace g3