diff --git a/.agents/codebase-insights.txt b/.agents/codebase-insights.txt index a886c88..c144ea7 100644 --- a/.agents/codebase-insights.txt +++ b/.agents/codebase-insights.txt @@ -1 +1,6 @@ -No insights yet. Please add content here and remove this line. +When the pure Ruby recorder traces a script that holds a reference to the +`PureRubyRecorder` instance in a local variable, the variable inspection code +would recursively serialise the tracer's internal state. This results in an +explosive amount of output and may appear as an infinite recursion when running +`examples/selective_tracing_pure.rb`. To avoid this, `load_variables` now skips +values that refer to the recorder or its `TraceRecord`. diff --git a/gems/codetracer-pure-ruby-recorder/lib/codetracer_pure_ruby_recorder.rb b/gems/codetracer-pure-ruby-recorder/lib/codetracer_pure_ruby_recorder.rb index 5795c5f..29b3fd7 100644 --- a/gems/codetracer-pure-ruby-recorder/lib/codetracer_pure_ruby_recorder.rb +++ b/gems/codetracer-pure-ruby-recorder/lib/codetracer_pure_ruby_recorder.rb @@ -268,16 +268,20 @@ def disable_tracepoints @tracing = false end + # Collect local variables from the current binding and convert them + # into CodeTracer values. Variables that refer to the recorder itself + # (or its TraceRecord) are ignored to avoid serialising the entire + # tracer state, which quickly leads to deep recursion and huge traces. def load_variables(binding) - if !binding.nil? - # $stdout.write binding.local_variables - binding.local_variables.map do |name| - v = binding.local_variable_get(name) - out = @record.to_value(v) - [name, out] - end - else - [] + return [] if binding.nil? + + binding.local_variables.filter_map do |name| + v = binding.local_variable_get(name) + + next if v.equal?(self) || v.equal?(@record) + + out = @record.to_value(v) + [name, out] end end end