Accessing the call stack

J

Jamis Buck

I've been wishing there were an easy to way to access Ruby's call
stack. I mean, you can use "caller", but that just gives you an array
of strings. Sometimes it would be nice to know the file, line, method
and object that invoked a particular method, without having to parse the
caller strings. The object and/or class cannot even be obtained that
way.

Is there an easy way? I wound up adding a trace function that
"listened" for call and return events, and then added them to a custom
stack. This worked very well, but is not very robust since if anyone
sets a new trace function, the "call stack" is no longer accurate.

Here's my solution:

#-------------------------------------------------
def invoker
Thread.current[:callstack] ||= []
( Thread.current[:callstack][-3] || Object )
end

set_trace_func proc { |event,file,line,obj,bind,klass|
if event == "call"
Thread.current[:callstack] ||= []
Thread.current[:callstack].push klass
elsif event == "return"
Thread.current[:callstack].pop
end
}
#-------------------------------------------------

All it tells you is the class of object that invoked the method, but it
could easily be extended. What I really want to know is: have I
reinvented the wheel? If not, perhaps something like this would be a
candidate for Rite...?

- Jamis
 
R

Robert Klemme

Jamis Buck said:
I've been wishing there were an easy to way to access Ruby's call
stack. I mean, you can use "caller", but that just gives you an array
of strings. Sometimes it would be nice to know the file, line, method
and object that invoked a particular method, without having to parse the
caller strings. The object and/or class cannot even be obtained that
way.

Is there an easy way? I wound up adding a trace function that
"listened" for call and return events, and then added them to a custom
stack. This worked very well, but is not very robust since if anyone
sets a new trace function, the "call stack" is no longer accurate.

Here's my solution:

#-------------------------------------------------
def invoker
Thread.current[:callstack] ||= []
( Thread.current[:callstack][-3] || Object )
end

set_trace_func proc { |event,file,line,obj,bind,klass|
if event == "call"
Thread.current[:callstack] ||= []
Thread.current[:callstack].push klass
elsif event == "return"
Thread.current[:callstack].pop
end
}
#-------------------------------------------------

All it tells you is the class of object that invoked the method, but it
could easily be extended. What I really want to know is: have I
reinvented the wheel? If not, perhaps something like this would be a
candidate for Rite...?

I don't think you have reinvented the wheel. I'd do just some little
changes:

module Kernel
def invoker(levels = -1)
st = Thread.current[:callstack]
st && st[levels - 2]
end
end

set_trace_func proc {|*args|
case args[0]
when /call$/
( Thread.current[:callstack] ||= [] ).push args
when /return$/
Thread.current[:callstack].pop
end
}

def rec(count)
p invoker
puts count
rec count - 1 if count > 0
end

rec 10

Regards

robert
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top