How to Get a Stack Trace Object in Ruby

How to get a stack trace object in Ruby?

You can use Kernel.caller for this. The same method is used when generating stack traces for exceptions.

From the docs:

def a(skip)
caller(skip)
end
def b(skip)
a(skip)
end
def c(skip)
b(skip)
end
c(0) #=> ["prog:2:in `a'", "prog:5:in `b'", "prog:8:in `c'", "prog:10"]
c(1) #=> ["prog:5:in `b'", "prog:8:in `c'", "prog:11"]
c(2) #=> ["prog:8:in `c'", "prog:12"]
c(3) #=> ["prog:13"]

Get current stack trace in Ruby without raising an exception

You can use Kernel#caller:

# /tmp/caller.rb

def foo
puts caller # Kernel#caller returns an array of strings
end

def bar
foo
end

def baz
bar
end

baz

Output:

caller.rb:8:in `bar'
caller.rb:12:in `baz'
caller.rb:15:in `<main>'

How to view the call stack when an object is thrown in Ruby

I was able to get something working using TracePoint and the fact that throw is a c language routine by trapping c_call:

TracePoint.new(:c_call) do |trace|
if trace.method_id == :throw
p [trace.path, trace.lineno]
end
end

This will only get you where the call to throw was actually made, not a full stack trace of everything called up to that point, though you could play around with catching :call as well, and put something together that captures more information. As a quick example:

TracePoint.new(:call, :c_call) do |trace|
if trace.event == :call || trace.method_id == :throw
p [trace.method_id, trace.path, trace.lineno]
end

trace.disable if trace.method_id == :throw
end

Full example:

# might_throw_cont.rb
def might_throw_second
throw :warden if rand(100) < 10

might_throw_third
end

def might_throw_third
throw :warden if rand(100) < 10

might_throw_final
end

# example.rb
require './might_throw_cont'

def might_throw_first
throw :warden if rand(100) < 10

might_throw_second
end

def might_throw_final
throw :warden if rand(100) < 10

will_throw
end

def will_throw
throw :warden
end

TracePoint.new(:call, :c_call) do |trace|
if trace.event == :call || trace.method_id == :throw
p [trace.method_id, trace.path, trace.lineno]
end

trace.disable if trace.method_id == :throw
end.enable do
catch :warden do
might_throw_first
end

puts "done"
end

Obviously, going to be hard to tell which method actually threw the symbol in this example. But running the example, a couple times I'll be able to see in the output (2 example runs):

# run 1
[:might_throw_first, "example.rb", 3]
[:might_throw_second, "/Users/simplelime/Documents/Ruby/might_throw_cont.rb", 1]
[:might_throw_third, "/Users/simplelime/Documents/Ruby/might_throw_cont.rb", 7]
[:might_throw_final, "example.rb", 9]
[:will_throw, "example.rb", 15]
[:throw, "example.rb", 16] # < only line you'll see if you trace only :c_call
done

# run 2
[:might_throw_first, "example.rb", 3]
[:throw, "example.rb", 4] # < only line you'll see if you trace only :c_call
done

How do I get ruby to print a full backtrace instead of a truncated one?

Exception#backtrace has the entire stack in it:

def do_division_by_zero; 5 / 0; end
begin
do_division_by_zero
rescue => exception
puts exception.backtrace
raise # always reraise
end

(Inspired by Peter Cooper's Ruby Inside blog)

How to get stack trace from a Test::Unit::TestCase

Looking through the code of Test::Unit in Ruby 1.8, it seems all the errors go through the Test::Unit::Error object which filters the backtrace in its #long_display method. There is no configuration and all runners will use the same filtered trace.

Brute force monkey patch to get the whole trace: (I put this in my single test case file; perhaps you could put it in a test helper)

require 'test/unit/util/backtracefilter'

module Test::Unit::Util::BacktraceFilter
def filter_backtrace(backtrace, prefix=nil)
backtrace
end
end

And monkey patch for Ruby 1.9 (which uses minitest)

def MiniTest.filter_backtrace(bt)
bt
end

Create full Exception object in Ruby

The current stack trace can be obtained from Kernel#caller, so you could do

exception = Exception.new("my message")
exception.set_backtrace(caller)

I guess creating an Exception object without raising it immediately basically only makes sense when you are planning to pass it around. Therefore it makes sense that the backtrace must be set manually.

how to get/traceback method calls stack

Ruby has one stdlib called Tracer. I would demonstrate it with a small example s below :

require 'tracer'

Tracer.on
def a;end
def b; a ; end
def c; b ; end
c
Tracer.off

Let me now run the code :

(arup~>test)$ ruby -v c.rb
ruby 2.0.0p0 (2013-02-24 revision 39474) [i686-linux]
#0:c.rb:4::-: def a;end
#0:c.rb:5::-: def b; a ; end
#0:c.rb:6::-: def c; b ; end
#0:c.rb:7::-: c
#0:c.rb:6:Object:>: def c; b ; end
#0:c.rb:6:Object:-: def c; b ; end
#0:c.rb:5:Object:>: def b; a ; end
#0:c.rb:5:Object:-: def b; a ; end
#0:c.rb:4:Object:>: def a;end
#0:c.rb:4:Object:<: def a;end
#0:c.rb:5:Object:<: def b; a ; end
#0:c.rb:6:Object:<: def c; b ; end
#0:c.rb:8::-: Tracer.off
(arup~>test)$

Description of some symbolic notations here came in the output:

  • +>+ - call a Ruby method
  • - - execute code on a new line
  • +<+ - return from a Ruby method

Is there a way to get a stack trace from rspec when a method is unexpectedly called more times than specified?

Try something like this:

status.should_receive(:nil?).twice { puts caller; false }

This tells rspec to allow two invocations and call the associated block each time. Thecaller method generates a full backtrace which you should be able to analyze onstdout. We also returnfalse to stay on the code-path we're testing.

If the two backtraces are hard to distinguish and you're only interested in the second (unexpected) invocation, then set up two successive expectations:

status.should_receive(:nil?).and_return(false)
status.should_receive(:nil?) { puts caller; false }

Here the double will return false on the first invocation and call the block on the second.

Reference for setting responses on expectations:

https://github.com/rspec/rspec-mocks#setting-responses



Related Topics



Leave a reply



Submit