From 63fa5abc4d0457c7dcc9742f67dc79b4fd50ce6f Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Mon, 28 Sep 2026 23:18:32 -0500 Subject: [PATCH 1/2] Reset the class of each captured call. `Fiber_Profiler_Capture_Call_initialize` reset `id` but not `klass`, and `rb_frame_method_id_and_class` leaves `klass` untouched when there is no method frame (e.g. a block defined outside of a method, or GC outside of a method). Such calls reported whatever class was left in their slot: `false` for a fresh slot, or the class of an earlier call for a reused one. That earlier class is no longer marked by the capture, so it may have been freed or moved, and the report can show an unrelated object. Initialize `klass` to `nil` so these calls report `nil` instead. --- ext/fiber/profiler/capture.c | 1 + test/fiber/profiler/capture.rb | 23 +++++++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/ext/fiber/profiler/capture.c b/ext/fiber/profiler/capture.c index bbd418b..189ae50 100644 --- a/ext/fiber/profiler/capture.c +++ b/ext/fiber/profiler/capture.c @@ -140,6 +140,7 @@ void Fiber_Profiler_Capture_Call_initialize(void *element) { call->event_flag = 0; call->id = 0; + call->klass = Qnil; call->path = NULL; call->line = 0; diff --git a/test/fiber/profiler/capture.rb b/test/fiber/profiler/capture.rb index 1fb915d..b4d9df5 100644 --- a/test/fiber/profiler/capture.rb +++ b/test/fiber/profiler/capture.rb @@ -185,6 +185,29 @@ def nested(n = 100, &block) end end + with "blocks outside of methods" do + # Blocks defined outside of a method don't have a method or class: + work = proc do + String.new + [1, 2].each{sleep 0.001} + end + + it "doesn't report a class left over from an earlier call" do + capture.start + + Fiber.new(&work).resume + + capture.stop + + stall = JSON.parse(output.string) + classes = stall["calls"].map{|call| call["class"]} + + expect(classes).to have_value(be == "nil") + expect(classes).not.to have_value(be == "String") + expect(classes).not.to have_value(be == "false") + end + end + with "Process.fork" do it "should disable the profiler in the child process after fork" do capture.start From 4c874fadbc77d9c590decfe9ccb7bb4b2d3382ae Mon Sep 17 00:00:00 2001 From: Trevor Turk Date: Mon, 28 Sep 2026 23:23:31 -0500 Subject: [PATCH 2/2] Don't depend on how an unknown class is labelled in the test. --- test/fiber/profiler/capture.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/test/fiber/profiler/capture.rb b/test/fiber/profiler/capture.rb index b4d9df5..0696711 100644 --- a/test/fiber/profiler/capture.rb +++ b/test/fiber/profiler/capture.rb @@ -202,7 +202,6 @@ def nested(n = 100, &block) stall = JSON.parse(output.string) classes = stall["calls"].map{|call| call["class"]} - expect(classes).to have_value(be == "nil") expect(classes).not.to have_value(be == "String") expect(classes).not.to have_value(be == "false") end