Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 11 additions & 2 deletions ext/fiber/profiler/capture.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,6 +627,15 @@ static size_t Fiber_Profiler_Capture_absolute_nesting(struct Fiber_Profiler_Capt
// If a call is within this threshold of the parent call, it will be skipped when printing the call stack - it's considered inconsequential to the performance of the parent call.
static const double Fiber_Profiler_Capture_SKIP_THRESHOLD = 0.98;

// Hidden classes (e.g. the singleton class of RubyVM::FrozenCore, which implements lambda literals) raise NotImplementedError from #inspect, so we use their class path instead:
static VALUE Fiber_Profiler_Capture_Call_class_inspect(struct Fiber_Profiler_Capture_Call *call) {
if (!RB_SPECIAL_CONST_P(call->klass) && RBASIC_CLASS(call->klass) == 0) {
return rb_class_path(call->klass);
}

return rb_inspect(call->klass);
}

void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *capture, FILE *restrict stream, double duration) {
double start_time = Fiber_Profiler_Time_delta(&capture->start_time, &capture->switch_time);

Expand Down Expand Up @@ -677,7 +686,7 @@ void Fiber_Profiler_Capture_print_tty(struct Fiber_Profiler_Capture *capture, FI
fprintf(stream, "\e[31m");
}

VALUE class_inspect = rb_inspect(call->klass);
VALUE class_inspect = Fiber_Profiler_Capture_Call_class_inspect(call);
const char *name = rb_id2name(call->id);

struct timespec offset;
Expand Down Expand Up @@ -731,7 +740,7 @@ void Fiber_Profiler_Capture_print_json(struct Fiber_Profiler_Capture *capture, F
call->nesting = call->parent->nesting + 1;
}

VALUE class_inspect = rb_inspect(call->klass);
VALUE class_inspect = Fiber_Profiler_Capture_Call_class_inspect(call);
const char *name = rb_id2name(call->id);

size_t nesting = Fiber_Profiler_Capture_absolute_nesting(capture, call);
Expand Down
26 changes: 26 additions & 0 deletions test/fiber/profiler/capture.rb
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,32 @@ def nested(n = 100, &block)
end
end

with "hidden classes" do
let(:capture) {subject.new(stall_threshold: 0, filter_threshold: 0, output: output)}

it "can report garbage collection inside methods of hidden classes" do
capture.start

Fiber.new do
GC.stress = true

# Lambda literals call a method on the hidden singleton class of RubyVM::FrozenCore:
->{}
ensure
GC.stress = false
end.resume

capture.stop

stall = JSON.parse(output.string)
calls = stall["calls"]
expect(calls).to have_value(have_keys(
"class" => be =~ /\A#<Class:0x\h+>\z/,
"method" => be == "lambda",
))
end
end

with "Process.fork" do
it "should disable the profiler in the child process after fork" do
capture.start
Expand Down