Proposals for extending Kernel#caller come up regularly on ruby-core and ruby-talk.
Indeed, the information it returns in textual form isn't as utile and easy to
use as it could be, forcing one to parse it:
def foo;barenddef bar;putscaller;endfoo
kernel_caller.rb:1:in `foo'
kernel_caller.rb:3
I decided to implement a Kernel#caller substitute for rcov which would provide
more information in a more convenient format:
(it actually returns an array of CallSite structs).
Actually, the main reason to implement it was performance-related: Kernel#caller
doesn't allow you to specify how deep the backtrace information should be (one how
many levels to skip). When creating cross-referenced code coverage repots for Rails applications, Kernel#caller proved to be way too slow since
the stack was very deep (over 60 levels...). I didn't need anything past the second
stack frame, so Kernel#caller was wasting everybody's time generating useless data.
Therefore, the key feature I needed was the ability to get only the top N frames.
I implemented that in C, using Ruby's RUBY_EVENT hooks. I traced all the
(c-)call/return events to maintain a call stack for each Ruby thread. That's
fairly slow, but I anticipated it would be faster overall than Kernel#caller
if the call stack was deep enough, and I was right (rcov's tracing phase was
nearly 2X faster than before).
It took about ~500 lines of C, which corresponded broadly to 80 lines of Ruby:
class CallStackdef initialize(stack=[]);@stack=stackenddef <<(x);@stack<<xenddef pop;@stack.popenddef last(n=1);@stack[-[n,@stack.size].min..-1]end# bomb for n <= 0?def tos;@stack.lastenddef to_a;@stackendend