Unravel Engine C++ Reference
Loading...
Searching...
No Matches
crash.cpp
Go to the documentation of this file.
1
6#include "crash.hpp"
7
9
10#include <atomic>
11#include <cpptrace/cpptrace.hpp>
12#include <cpptrace/formatting.hpp>
13#include <csignal>
14#include <string_view>
15#include <exception>
16#include <typeinfo>
17
18#include <cassert>
19
21{
22
23// Internal declarations and types
24namespace
25{
26
28std::atomic<bool> g_handling{false};
29
30
31
33interrupt_handler_t g_interrupt_handler{nullptr};
34termination_handler_t g_termination_handler{nullptr};
35crash_handler_t g_crash_handler{nullptr};
36exception_handler_t g_exception_handler{nullptr};
37
38
39auto demangle_exception_type(const std::exception& e) -> std::string
40{
41 return cpptrace::demangle(typeid(e).name());
42}
43
44auto generate_stack_trace() -> trace_info
45{
46 try
47 {
48 auto stack_trace = cpptrace::generate_trace(1);
49 return trace_info{stack_trace.to_string(false)};
50 }
51 catch(...)
52 {
53 return trace_info{};
54 }
55}
56
57} // anonymous namespace
58
59// Internal function declarations
60auto get_signal_name(int sig) noexcept -> std::string_view;
61auto is_crash_signal(int sig) noexcept -> bool;
62
63auto signal_handler(int sig) -> void;
64
65// Setter functions
67{
68 g_interrupt_handler = handler;
69}
70
72{
73 g_termination_handler = handler;
74}
75
77{
78 g_crash_handler = handler;
79}
80
82{
83 g_exception_handler = handler;
84}
85
86auto get_signal_name(int sig) noexcept -> std::string_view
87{
88 switch(sig)
89 {
90 case SIGSEGV:
91 return "SIGSEGV (Segmentation fault)";
92 case SIGABRT:
93 return "SIGABRT (Process abort)";
94 case SIGILL:
95 return "SIGILL (Illegal instruction)";
96 case SIGFPE:
97 return "SIGFPE (Floating point exception)";
98#ifdef SIGBUS
99 case SIGBUS:
100 return "SIGBUS (Bus error)";
101#endif
102 case SIGINT:
103 return "SIGINT (User interrupt)";
104 case SIGTERM:
105 return "SIGTERM (Termination request)";
106#ifdef SIGPIPE
107 case SIGPIPE:
108 return "SIGPIPE (Broken pipe)";
109#endif
110#ifdef SIGQUIT
111 case SIGQUIT:
112 return "SIGQUIT (User quit)";
113#endif
114#ifdef SIGKILL
115 case SIGKILL:
116 return "SIGKILL (User kill process)";
117#endif
118#ifdef SIGHUP
119 case SIGHUP:
120 return "SIGHUP (Terminal hangup)";
121#endif
122#ifdef SIGBREAK
123 case SIGBREAK:
124 return "SIGBREAK (Break request)";
125#endif
126#ifdef SIGABRT_COMPAT
127 case SIGABRT_COMPAT:
128 return "SIGABRT_COMPAT (Process abort compat)";
129#endif
130 default:
131 return "Unknown signal";
132 }
133}
134
136
137auto get_signal_type(int sig) noexcept -> signal_type
138{
139 switch(sig)
140 {
141 // User interrupts
142 case SIGINT: // Ctrl+C
143#ifdef SIGBREAK
144 case SIGBREAK: // Ctrl+Break (Windows)
145#endif
147
148 // Termination requests
149 case SIGTERM: // Termination request
150#ifdef SIGQUIT
151 case SIGQUIT: // Quit request
152#endif
153#ifdef SIGKILL
154 case SIGKILL: // Kill request
155#endif
156#ifdef SIGHUP
157 case SIGHUP: // Terminal hangup
158#endif
159#ifdef SIGPIPE
160 case SIGPIPE: // Broken pipe
161#endif
163
164 // Actual crashes
165 case SIGSEGV: // Segmentation fault
166 case SIGABRT: // Process abort
167 case SIGILL: // Illegal instruction
168 case SIGFPE: // Floating point exception
169#ifdef SIGBUS
170 case SIGBUS: // Bus error
171#endif
172#ifdef SIGABRT_COMPAT
173 case SIGABRT_COMPAT: // Process abort compat
174#endif
175 default: // Unknown signals treated as crashes for safety
176 return signal_type::crash;
177 }
178}
179
180auto is_crash_signal(int sig) noexcept -> bool
181{
182 return get_signal_type(sig) == signal_type::crash;
183}
184
185auto signal_handler(int sig) -> void
186{
187 // Prevent recursive crashes
188 if(g_handling.exchange(true, std::memory_order_acq_rel))
189 {
190 std::exit(128 + sig);
191 }
192
193 const auto sig_type = get_signal_type(sig);
194 const char* sig_name = get_signal_name(sig).data();
195
196 // Create signal info for callbacks (no crash address since we have stack traces)
197 signal_info sig_info{
198 .signal_number = sig,
199 .signal_name = sig_name,
200 };
201
202
203 // Handle based on signal type
204 switch(sig_type)
205 {
207 {
208 if(g_interrupt_handler)
209 {
210 try { g_interrupt_handler(sig_info); } catch(...) {}
211 }
212 // Reset handling flag for interrupts (they might be handled gracefully)
213 g_handling.store(false, std::memory_order_release);
214 return;
215 }
216
218 {
219 if(g_termination_handler)
220 {
221 try { g_termination_handler(sig_info); } catch(...) {}
222 }
223 // Reset handling flag for termination (they might be handled gracefully)
224 g_handling.store(false, std::memory_order_release);
225 return;
226 }
227
229 {
230
231 if(g_crash_handler)
232 {
233 // Capture stack trace for crashes
234 trace_info trace = generate_stack_trace();
235 try { g_crash_handler(sig_info, trace); } catch(...) {}
236 }
237
238 std::abort();
239 return;
240 }
241 }
242}
243
244
245[[noreturn]] void terminate_handler()
246{
247 // Prevent recursive terminate calls
248 if(g_handling.exchange(true, std::memory_order_acq_rel))
249 {
250 std::abort();
251 }
252
253 if(!g_exception_handler)
254 {
255 std::abort();
256 }
257
258 // Prepare exception info
259 exception_info exc_info{
260 .exception_type = "Unknown",
261 .exception_message = "No active exception"
262 };
263
264 // Prepare trace info
265 trace_info trace{};
266
267 try
268 {
269 auto ptr = std::current_exception();
270 if(ptr != nullptr)
271 {
272 std::rethrow_exception(ptr);
273 }
274 else
275 {
276 exc_info.exception_message = "Terminate called without an active exception";
277 }
278 }
279 catch(const std::exception& e)
280 {
281 // Standard exception - generate our own trace
282 exc_info.exception_type = demangle_exception_type(e);
283 exc_info.exception_message = "Terminate called after throwing " + exc_info.exception_type + " : " + e.what();
284
285 // Generate stack trace
286 trace = generate_stack_trace();
287 }
288 catch(...)
289 {
290 // Unknown exception type
291 exc_info.exception_type = "Unknown Exception Type";
292 exc_info.exception_message = "Terminate called after throwing an unknown exception";
293
294 // Generate stack trace
295 trace = generate_stack_trace();
296 }
297
298 // Call the user's exception handler if available
299 try
300 {
301 g_exception_handler(exc_info, trace);
302 }
303 catch(...)
304 {
305 // Handler threw - can't do much about it
306 }
307
308
309 std::abort();
310}
311
313{
314 std::set_terminate(terminate_handler);
315}
316
317
318auto install_handlers(const crash_handlers& handlers) -> void
319{
320 // Prevent multiple installations
321 static std::atomic<bool> installed{false};
322 if(installed.exchange(true, std::memory_order_acq_rel))
323 {
324 return;
325 }
326
327 set_interrupt_handler(handlers.interrupt_handler);
328 set_termination_handler(handlers.termination_handler);
329 set_crash_handler(handlers.crash_handler);
330 set_exception_handler(handlers.exception_handler);
331
333
334 // Install unified signal handlers for all platforms
335
336 // Crash signals
337 std::signal(SIGABRT, signal_handler); // Process abort
338 std::signal(SIGILL, signal_handler); // Illegal instruction
339 std::signal(SIGFPE, signal_handler); // Floating point exception
340 std::signal(SIGSEGV, signal_handler); // Segmentation fault
341
342 // Interrupt signals
343 std::signal(SIGINT, signal_handler); // Ctrl+C
344
345 // Termination signals
346 std::signal(SIGTERM, signal_handler); // Termination request
347
348 // Platform-specific signals
349#ifdef SIGBUS
350 std::signal(SIGBUS, signal_handler); // Bus error (POSIX)
351#endif
352#ifdef SIGQUIT
353 std::signal(SIGQUIT, signal_handler); // Quit request (POSIX)
354#endif
355#ifdef SIGHUP
356 std::signal(SIGHUP, signal_handler); // Terminal hangup (POSIX)
357#endif
358#ifdef SIGBREAK
359 std::signal(SIGBREAK, signal_handler); // Ctrl+Break (Windows)
360#endif
361#ifdef SIGABRT_COMPAT
362 std::signal(SIGABRT_COMPAT, signal_handler); // Process abort compat
363#endif
364#ifdef SIGKILL
365 std::signal(SIGKILL, signal_handler); // Kill request (POSIX)
366#endif
367
368 // Ignored signals
369#ifdef SIGPIPE
370 std::signal(SIGPIPE, SIG_IGN); // Broken pipe (don't crash on this)
371#endif
372
373}
374
375
376} // namespace unravel::crash
std::string name
Definition hub.cpp:33
void(*)(const signal_info &info) interrupt_handler_t
Definition crash.hpp:46
auto get_signal_name(int sig) noexcept -> std::string_view
Definition crash.cpp:86
auto set_interrupt_handler(interrupt_handler_t handler) -> void
Definition crash.cpp:66
void(*)(const signal_info &info) termination_handler_t
Definition crash.hpp:47
auto install_handlers(const crash_handlers &handlers) -> void
Install comprehensive crash handlers.
Definition crash.cpp:318
auto signal_handler(int sig) -> void
Definition crash.cpp:185
auto get_signal_type(int sig) noexcept -> signal_type
Definition crash.cpp:137
void(*)(const exception_info &info, const trace_info &trace) exception_handler_t
Definition crash.hpp:49
auto is_crash_signal(int sig) noexcept -> bool
Definition crash.cpp:180
auto set_termination_handler(termination_handler_t handler) -> void
Definition crash.cpp:71
void(*)(const signal_info &info, const trace_info &trace) crash_handler_t
Definition crash.hpp:48
void register_terminate_handler()
Definition crash.cpp:312
auto set_crash_handler(crash_handler_t handler) -> void
Definition crash.cpp:76
void terminate_handler()
Definition crash.cpp:245
auto set_exception_handler(exception_handler_t handler) -> void
Definition crash.cpp:81
Exception information structure
Definition crash.hpp:35
Signal information structure.
Definition crash.hpp:29
Stack trace information.
Definition crash.hpp:41