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