ext/stackprof/stackprof.c in stackprof-0.2.23 vs ext/stackprof/stackprof.c in stackprof-0.2.24

- old
+ new

@@ -10,10 +10,11 @@ #include <ruby/version.h> #include <ruby/debug.h> #include <ruby/st.h> #include <ruby/io.h> #include <ruby/intern.h> +#include <ruby/vm.h> #include <signal.h> #include <sys/time.h> #include <time.h> #include <pthread.h> @@ -30,10 +31,11 @@ "(marking)", "(sweeping)", }; static int stackprof_use_postponed_job = 1; +static int ruby_vm_running = 0; #define TOTAL_FAKE_FRAMES (sizeof(fake_frame_cstrs) / sizeof(char *)) #ifdef _POSIX_MONOTONIC_CLOCK #define timestamp_t timespec @@ -723,10 +725,15 @@ _stackprof.overall_signals++; if (!_stackprof.running) return; + // There's a possibility that the signal handler is invoked *after* the Ruby + // VM has been shut down (e.g. after ruby_cleanup(0)). In this case, things + // that rely on global VM state (e.g. rb_during_gc) will segfault. + if (!ruby_vm_running) return; + if (_stackprof.mode == sym_wall) { // In "wall" mode, the SIGALRM signal will arrive at an arbitrary thread. // In order to provide more useful results, especially under threaded web // servers, we want to forward this signal to the original thread // StackProf was started from. @@ -843,18 +850,27 @@ { stackprof_use_postponed_job = 1; return Qnil; } +static void +stackprof_at_exit(ruby_vm_t* vm) +{ + ruby_vm_running = 0; +} + void Init_stackprof(void) { size_t i; /* * As of Ruby 3.0, it should be safe to read stack frames at any time, unless YJIT is enabled * See https://github.com/ruby/ruby/commit/0e276dc458f94d9d79a0f7c7669bde84abe80f21 */ stackprof_use_postponed_job = RUBY_API_VERSION_MAJOR < 3; + + ruby_vm_running = 1; + ruby_vm_at_exit(stackprof_at_exit); #define S(name) sym_##name = ID2SYM(rb_intern(#name)); S(object); S(custom); S(wall);