Line data Source code
1 : /* 2 : * The following code is strongly inspired by the one from the cpptrace lib 3 : * - see: https://github.com/jeremy-rifkin/cpptrace/blob/main/docs/signal-safe-tracing.md 4 : */ 5 : 6 : #include <chrono> 7 : #include <csignal> 8 : #include <cstdio> 9 : #include <cstdlib> 10 : #include <iostream> 11 : #include <thread> 12 : 13 : #include "Runtime/Sequence/Sequence.hpp" 14 : #include "Tools/Display/rang_format/rang_format.h" 15 : #include "Tools/Signal_handler/Signal_handler.hpp" 16 : 17 : using namespace spu; 18 : using namespace spu::tools; 19 : 20 : bool g_sigsegv = false; 21 : bool g_sigint = false; 22 : 23 : #ifdef SPU_STACKTRACE_SEGFAULT 24 : 25 : #include <cstring> 26 : 27 : #include <cpptrace/cpptrace.hpp> 28 : 29 : #ifdef SPU_STACKTRACE_SEGFAULT_LIBUNWIND 30 : 31 : #include <sys/wait.h> 32 : #include <unistd.h> 33 : 34 : // this is just a utility I like, it makes the pipe API more expressive. 35 : struct pipe_t 36 : { 37 : union 38 : { 39 : struct 40 : { 41 : int read_end; 42 : int write_end; 43 : }; 44 : int data[2]; 45 : }; 46 : }; 47 : 48 : #if defined(_WIN64) || defined(_WIN32) 49 : void 50 : signal_sigsegv_handler(int signo) 51 : #else /* Unix-like */ 52 : void 53 : signal_sigsegv_handler(int signo, siginfo_t* info, void* context) 54 : #endif 55 : { 56 : g_sigsegv = true; 57 : 58 : // print basic message 59 : std::cerr << rang::tag::error << "Signal \"Segmentation Violation\" caught by StreamPU signal handler!" 60 : << std::endl; 61 : std::cerr << rang::tag::error << "Printing stack trace (if possible) and then, exiting..." << std::endl; 62 : 63 : cpptrace::frame_ptr buffer[100]; 64 : size_t count = cpptrace::safe_generate_raw_trace(buffer, 100); 65 : pipe_t input_pipe; 66 : pipe(input_pipe.data); 67 : const pid_t pid = fork(); 68 : if (pid == -1) 69 : // some error occurred :-( 70 : return; 71 : 72 : if (pid == 0) // child 73 : { 74 : dup2(input_pipe.read_end, STDIN_FILENO); 75 : close(input_pipe.read_end); 76 : close(input_pipe.write_end); 77 : execlp("streampu-signal-tracer", "streampu-signal-tracer", nullptr); 78 : _exit(1); 79 : } 80 : 81 : // resolve to safe_object_frames and write those to the pipe 82 : for (size_t i = 0; i < count; i++) 83 : { 84 : cpptrace::safe_object_frame frame; 85 : cpptrace::get_safe_object_frame(buffer[i], &frame); 86 : write(input_pipe.write_end, &frame, sizeof(frame)); 87 : } 88 : close(input_pipe.read_end); 89 : close(input_pipe.write_end); 90 : 91 : // wait for child 92 : waitpid(pid, nullptr, 0); 93 : 94 : _exit(1); 95 : } 96 : 97 : #else /* SPU_STACKTRACE_SEGFAULT_LIBUNWIND */ 98 : 99 : // /!\ UNSAFE METHOD 100 : #if defined(_WIN64) || defined(_WIN32) 101 : void 102 : signal_sigsegv_handler(int signo) 103 : #else /* Unix-like */ 104 : void 105 : signal_sigsegv_handler(int signo, siginfo_t* info, void* context) 106 : #endif 107 : { 108 : g_sigsegv = true; 109 : 110 : // print basic message 111 : std::cerr << rang::tag::error << "Signal \"Segmentation Violation\" caught by StreamPU signal handler!" 112 : << std::endl; 113 : std::cerr << rang::tag::error << "Printing stack trace (if possible) and then, exiting..." << std::endl; 114 : 115 : #ifdef SPU_COLORS 116 : bool enable_color = true; 117 : #else 118 : bool enable_color = false; 119 : #endif 120 : // according to the documentation, calling 'cpptrace::generate_trace()' in a signal handler is unsafe and may lead 121 : // to deadlock or memory corruption... 122 : cpptrace::generate_trace().print(std::cerr, enable_color); 123 : 124 : std::exit(1); 125 : } 126 : 127 : #endif /* SPU_STACKTRACE_SEGFAULT_LIBUNWIND */ 128 : 129 : void 130 : warmup_cpptrace() 131 : { 132 : // This is done for any dynamic-loading shenanigans 133 : cpptrace::frame_ptr buffer[10]; 134 : cpptrace::safe_generate_raw_trace(buffer, 10); 135 : cpptrace::safe_object_frame frame; 136 : cpptrace::get_safe_object_frame(buffer[0], &frame); 137 : } 138 : 139 : #endif /* SPU_STACKTRACE_SEGFAULT */ 140 : 141 : bool g_is_interrupt = false; 142 : #if defined(_WIN64) || defined(_WIN32) 143 : void 144 : signal_sigint_handler(int signo) 145 : #else /* Unix-like */ 146 : void 147 0 : signal_sigint_handler(int signo, siginfo_t* info, void* context) 148 : #endif 149 : { 150 0 : if (g_is_interrupt) 151 : { 152 : // print basic message 153 0 : std::cerr << std::endl 154 0 : << rang::tag::error << "Signal \"Interruption\" caught twice by StreamPU signal handler!" 155 0 : << std::endl; 156 0 : std::cerr << rang::tag::error << "Killing the application RIGHT NOW sir!" << std::endl; 157 0 : exit(1); 158 : } 159 : else 160 : { 161 0 : g_is_interrupt = true; 162 0 : std::this_thread::sleep_for(std::chrono::milliseconds(250)); // 250 ms of passive waiting 163 : 164 : // print basic message 165 0 : std::clog << std::endl 166 0 : << rang::tag::info << "Signal \"Interruption\" caught by StreamPU signal handler!" << std::endl; 167 0 : std::clog << rang::tag::info << "Stopping Sequence::exec() and/or Pipeline::exec()..." << std::endl; 168 : 169 0 : g_sigint = true; 170 : 171 0 : g_is_interrupt = false; 172 : } 173 0 : } 174 : 175 : int 176 170 : Signal_handler::init() 177 : { 178 : // SIGSEGV handler 179 : #ifdef SPU_STACKTRACE_SEGFAULT 180 : cpptrace::absorb_trace_exceptions(false); 181 : // cpptrace::register_terminate_handler(); 182 : warmup_cpptrace(); 183 : 184 : #if defined(_WIN64) || defined(_WIN32) 185 : signal(SIGSEGV, signal_sigsegv_handler); 186 : #else 187 : struct sigaction action_sigsegv = {}; 188 : action_sigsegv.sa_flags = 0; 189 : action_sigsegv.sa_sigaction = &signal_sigsegv_handler; 190 : if (sigaction(SIGSEGV, &action_sigsegv, NULL) == -1) perror("sigaction SIGSEGV"); 191 : #endif /* defined(_WIN64) || defined(_WIN32) */ 192 : #endif /* SPU_STACKTRACE_SEGFAULT */ 193 : 194 : // SIGINT handler 195 : #if defined(_WIN64) || defined(_WIN32) 196 : signal(SIGINT, signal_sigint_handler); 197 : #else 198 170 : struct sigaction action_sigint = {}; 199 170 : action_sigint.sa_flags = 0; 200 170 : action_sigint.sa_sigaction = &signal_sigint_handler; 201 170 : if (sigaction(SIGINT, &action_sigint, NULL) == -1) perror("sigaction SIGINT"); 202 : #endif /* defined(_WIN64) || defined(_WIN32) */ 203 : 204 170 : return 0; 205 : } 206 : 207 : bool 208 768381 : Signal_handler::is_sigint() 209 : { 210 768381 : return g_sigint; 211 : } 212 : 213 : void 214 3070 : Signal_handler::reset_sigint() 215 : { 216 3070 : g_sigint = false; 217 3070 : } 218 : 219 : bool 220 0 : Signal_handler::is_sigsegv() 221 : { 222 0 : return g_sigsegv; 223 : } 224 : 225 : void 226 0 : Signal_handler::reset_sigsegv() 227 : { 228 0 : g_sigsegv = false; 229 0 : }