Ruby has a built-in tracing system which you can access using the TracePoint class. Some of the things you can trace are method calls, new threads & exceptions.
Why would you want to use this?
Well, it could be useful if you want to trace the execution of a certain method. You will be able to see what other methods are being called & what are the return values.
Let’s see a few examples!
Tracing Method Calls
Most of the time you will want TracePoint to trace application code & not built-in methods (like puts, size, etc).
You can do this using the call event.
Example:
def the_method; other_method; end
def other_method; end
def start_trace
trace =
TracePoint.new(:call) { |tp| p [tp.path, tp.lineno, tp.event, tp.method_id] }
trace.enable
yield
trace.disable
end
start_trace { the_method }
This prints the file path, the line number, the event name & the method name.
If you don’t specify any events Ruby will call your block for all of them, resulting in more output. So I would recommend that you focus on specific events to find what you want faster 🙂
If you want to see some massive call graphs you just have to trace some Rails methods 😉
Return Values
In the introduction I mentioned that you can also get return values…
For this you will need to trace the return event and use the return_value method.
Example:
def the_method; "A" * 10; end
trace = TracePoint.new(:return) { |tp| puts "Return value for #{tp.method_id} is #{tp.return_value}." }
trace.enable
the_method
trace.disable
This will print:
Return value for the_method is AAAAAAAAAA.
Events First
Someone asked on reddit how it’s possible to avoid having the word “bar” printed when calling the foo method in the following code:
class Thing
def foo
puts "foo"
bar
end
def bar
puts "bar"
end
end
# your code here
t = Thing.new
t.foo
There are many ways to achieve this, like prepending a module, redirecting $stdout or redefining the bar method.
If you are feeling creative, comment on this post with your own idea!
But I found one of the answers particularly interesting because it used the TracePoint class.
Here it is:
TracePoint.trace(:call) { |tp| exit if tp.method_id == :bar }
This code will call exit when the method bar is called, which prevents the string from being printed by ending the program.
Probably not something you want to use in real code, but it proves one thing about TracePoint: Events are triggered before they happen.
Something to keep in mind if you are going to build some sort of tool around this 🙂
Summary
In this post you learned about the TracePoint class, which allows you to trace a few events like methods calls or new threads. This can be useful as a debugging tool or for code exploration.
Remember to share this post so more people can enjoy it 🙂