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);