Feature #15854
openTracing instance variable assignment
Description
I suggest a feature "tracing instance variable assignment". It's useful for debugging.
Use case:
In Rails, we use instance variables in views and controllers. When we got a bug caused by instance variable unintentional values, if we traced instance variable assignment timing, it would be good informations.
And in Rails views, there are no source codes of self class. That's built dynamically.
Current behavior (Ruby2.6):
In Ruby 2.6, only if there is a source code file to assign instance variable, we can trace instance variable assignment by following code (check_instance_variable_assignment.rb). But it's difficult if the assignment codes are defined dynamically. For example, in Rails view.
(And in another story, global variables assignment are traced by Kernel#trace_var.)
check_instance_variable_assignment.rb
def trace_start
TracePoint.trace(:line) do |tp|
target_class_name = "Foo"
target_instance_variable_name = "@bar"
line = File.open(tp.path, "r"){|f| f.readlines[tp.lineno - 1] }
node = RubyVM::AbstractSyntaxTree.parse(line).children.last
# check instance variable assignment
next unless node.type == :IASGN
# check class name
target_class = Kernel.const_get(target_class_name)
next unless tp.self.is_a?(target_class)
# check variable name
instance_variable_name = node.children.first
next unless instance_variable_name == target_instance_variable_name.to_sym
puts "#{target_class_name} #{target_instance_variable_name} is assigned in #{tp.path}:#{tp.lineno} #{tp.defined_class} #{tp.method_id}"
end
end
class Foo
def bar
@bar = "text"
end
end
trace_start
Foo.new.bar
#=> Foo @bar is assigned in check_instance_variable_assignment.rb:25 Foo bar
Suggesting feature example:
Add new arguments for TracePoint.new method like :line and :call to trace instance variables assignment.
- :iasgn (IASGN name from RubyVM::AbstractSyntaxTree::Node)
- :casgn (CVASGN (or CASGN?) name from RubyVM::AbstractSyntaxTree::Node. I think class variables tracing is useful too.)
And get informations
- class name (It might be get by trace_point.self)
- variable name ("@foo", "@@foo")
A sample code to use the feature:
tp_iasgn.rb
TracePoint.trace(:iasgn) do |tp|
target_class_name = "Foo"
target_instance_variable_name = "@bar"
# check class name
target_class = Kernel.const_get(target_class_name)
next unless tp.self.is_a?(target_class)
# check variable name
next unless target_instance_variable_name == tp.variable_name
puts "#{target_class_name} #{target_instance_variable_name} is assigned in #{tp.path}:#{tp.lineno} #{tp.method_id} #{tp.defined_class}"
puts caller # even in dynamic code case, we can get caller informations.
end