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
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 break;
213 }
214
216 {
217 if(g_termination_handler)
218 {
219 try { g_termination_handler(sig_info); } catch(...) {}
220 }
221 break;
222 }
223
225 {
226
227 if(g_crash_handler)
228 {
229 // Capture stack trace for crashes
230 trace_info trace = generate_stack_trace();
231 try { g_crash_handler(sig_info, trace); } catch(...) {}
232 }
233 break;
234 }
235 }
236
237}
238
239
240[[noreturn]] void terminate_handler()
241{
242 // Prevent recursive terminate calls
243 if(g_handling.exchange(true, std::memory_order_acq_rel))
244 {
245 std::abort();
246 }
247
248 if(!g_exception_handler)
249 {
250 std::abort();
251 }
252
253 // Prepare exception info
254 exception_info exc_info{
255 .exception_type = "Unknown",
256 .exception_message = "No active exception"
257 };
258
259 // Prepare trace info
260 trace_info trace{};
261
262 try
263 {
264 auto ptr = std::current_exception();
265 if(ptr != nullptr)
266 {
267 std::rethrow_exception(ptr);
268 }
269 else
270 {
271 exc_info.exception_message = "Terminate called without an active exception";
272 }
273 }
274 catch(const std::exception& e)
275 {
276 // Standard exception - generate our own trace
277 exc_info.exception_type = demangle_exception_type(e);
278 exc_info.exception_message = "Terminate called after throwing " + exc_info.exception_type + " : " + e.what();
279
280 // Generate stack trace
281 trace = generate_stack_trace();
282 }
283 catch(...)
284 {
285 // Unknown exception type
286 exc_info.exception_type = "Unknown Exception Type";
287 exc_info.exception_message = "Terminate called after throwing an unknown exception";
288
289 // Generate stack trace
290 trace = generate_stack_trace();
291 }
292
293 // Call the user's exception handler if available
294 try
295 {
296 g_exception_handler(exc_info, trace);
297 }
298 catch(...)
299 {
300 // Handler threw - can't do much about it
301 }
302
303
304 std::abort();
305}
306
308{
309 std::set_terminate(terminate_handler);
310}
311
312
313auto install_handlers(const crash_handlers& handlers) -> void
314{
315 // Prevent multiple installations
316 static std::atomic<bool> installed{false};
317 if(installed.exchange(true, std::memory_order_acq_rel))
318 {
319 return;
320 }
321
322 set_interrupt_handler(handlers.interrupt_handler);
323 set_termination_handler(handlers.termination_handler);
324 set_crash_handler(handlers.crash_handler);
325 set_exception_handler(handlers.exception_handler);
326
328
329 // Install unified signal handlers for all platforms
330
331 // Crash signals
332 std::signal(SIGABRT, signal_handler); // Process abort
333 std::signal(SIGILL, signal_handler); // Illegal instruction
334 std::signal(SIGFPE, signal_handler); // Floating point exception
335 std::signal(SIGSEGV, signal_handler); // Segmentation fault
336
337 // Interrupt signals
338 std::signal(SIGINT, signal_handler); // Ctrl+C
339
340 // Termination signals
341 std::signal(SIGTERM, signal_handler); // Termination request
342
343 // Platform-specific signals
344#ifdef SIGBUS
345 std::signal(SIGBUS, signal_handler); // Bus error (POSIX)
346#endif
347#ifdef SIGQUIT
348 std::signal(SIGQUIT, signal_handler); // Quit request (POSIX)
349#endif
350#ifdef SIGHUP
351 std::signal(SIGHUP, signal_handler); // Terminal hangup (POSIX)
352#endif
353#ifdef SIGBREAK
354 std::signal(SIGBREAK, signal_handler); // Ctrl+Break (Windows)
355#endif
356#ifdef SIGABRT_COMPAT
357 std::signal(SIGABRT_COMPAT, signal_handler); // Process abort compat
358#endif
359#ifdef SIGKILL
360 std::signal(SIGKILL, signal_handler); // Kill request (POSIX)
361#endif
362
363 // Ignored signals
364#ifdef SIGPIPE
365 std::signal(SIGPIPE, SIG_IGN); // Broken pipe (don't crash on this)
366#endif
367
368}
369
370
371} // namespace unravel::crash
std::string name
Definition hub.cpp:27
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:313
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:307
auto set_crash_handler(crash_handler_t handler) -> void
Definition crash.cpp:76
void terminate_handler()
Definition crash.cpp:240
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