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
How to Return Something Early from a Block
Ruby on Rails Advanced JSON Serialization
What Is the Correct Way to Detect If Ruby Is Running on Windows
Uninitialized Constant Rake::Dsl in Ruby Gem
Install Any Version of Ruby with Rvm on Mavericks
How to Override a Column in Rails Model
Safely Assign Value to Nested Hash Using Hash#Dig or Lonely Operator(&.)
Using a Class Object in Case Statement
Cannot Execute "Rails Console" Due to an Error with Readline
Executing User-Supplied Ruby Code on a Web Server
Ruby Dropped in Netbeans 7,How to Use It in Netbeans7
How to Share Variables Across My .Rb Files
How to Switch to an Older Version of Rails