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: 12 33 36.4 %
Date: 2025-07-17 17:04:07 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/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 : }

Generated by: LCOV version 1.14