LCOV - code coverage report
Current view: top level - src/Tools/Signal_handler - Signal_handler.cpp (source / functions) Hit Total Coverage
Test: streampu_clean.info Lines: 11 31 35.5 %
Date: 2024-06-12 12:04:18 Functions: 3 6 50.0 %

          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         159 : 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         159 :     struct sigaction action_sigint = {};
     199         159 :     action_sigint.sa_flags = 0;
     200         159 :     action_sigint.sa_sigaction = &signal_sigint_handler;
     201         159 :     if (sigaction(SIGINT, &action_sigint, NULL) == -1) perror("sigaction SIGINT");
     202             : #endif /* defined(_WIN64) || defined(_WIN32) */
     203             : 
     204         159 :     return 0;
     205             : }
     206             : 
     207             : bool
     208      199631 : Signal_handler::is_sigint()
     209             : {
     210      199631 :     return g_sigint;
     211             : }
     212             : 
     213             : void
     214        2585 : Signal_handler::reset_sigint()
     215             : {
     216        2585 :     g_sigint = false;
     217        2585 : }
     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 : }

Generated by: LCOV version 1.14