How to View a Sample of the Call Stack in Ruby

How do you view a sample of the call stack in ruby?

Just put

puts caller

anywhere in the code. If you don't like its format, it's an array of strings, so you can do some regex manipulation for a desired output.

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

Call stack around ruby code

It's not exactly like that, but it's very similar.

You can use method method. For example

2.method(:hours)
=> #<Method: Fixnum(Numeric)#hours>

or probably more detail

2.method(:hours).source_location
=> ["~/.rvm/gems/ruby-2.3.3/gems/activesupport-4.2.7.1/lib/active_support/core_ext/numeric/time.rb", 29]

and after that you can use debuger(if posible) or go deeper

Another detail in this answer How to find where a method is defined at runtime?

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 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

Pry: show me the stack

Use the pry-stack_explorer plugin, it allows you to move up and down the call-stack (with up and down), display the callstack (with show-stack), and so on:

see here:

Frame number: 0/64

From: /Users/johnmair/ruby/rails_projects/personal_site/app/controllers/posts_controller.rb @ line 7 PostsController#index:

5: def index
6: @posts = Post.all
=> 7: binding.pry
8: end

[1] pry(#<PostsController>)> show-stack

Showing all accessible frames in stack (65 in total):
--
=> #0 index <PostsController#index()>
#1 [method] send_action <ActionController::ImplicitRender#send_action(method, *args)>
#2 [method] process_action <AbstractController::Base#process_action(method_name, *args)>
#3 [method] process_action <ActionController::Rendering#process_action(*arg1)>
<... clipped ...>

[2] pry(#<PostsController>)> up

Frame number: 1/64
Frame type: method

From: /Users/johnmair/.rvm/gems/ruby-2.0.0-p0/gems/actionpack-3.2.8/lib/action_controller/metal/implicit_render.rb @ line 4 ActionController::ImplicitRender#send_action:

3: def send_action(method, *args)
=> 4: ret = super
5: default_render unless response_body
6: ret
7: end

[3] pry(#<PostsController>)>

How do I get a call stack of another thread?

There is Thread#backtrace method to get function backtrace for some thread:

https://ruby-doc.org/core-2.4.1/Thread.html#method-i-backtrace

backtrace → array click to toggle source

Returns the current backtrace of the target thread.

For usage example check this project: https://github.com/frsyuki/sigdump (it will show backtraces and some memory usage info for ruby and jruby)

sigdump - In short: SIGQUIT of Java VM for Ruby (Use signal to show stacktrace of a Ruby process without restarting it).

... Just sending SIGCONT signal will dump backtrace and memory profile to /tmp/sigdump-.log file.

sigdump dumps following information (see also Sample output):

Backtrace of all threads

https://github.com/frsyuki/sigdump/blob/master/lib/sigdump.rb

    dump_all_thread_backtrace(io)
...
Thread.list.each do |thread|
dump_backtrace(thread, io)
...
def self.dump_backtrace(thread, io)
status = thread.status
if status == nil
status = "finished"
elsif status == false
status = "error"
end

io.write " Thread #{thread} status=#{status} priority=#{thread.priority}\n"
if thread.backtrace
thread.backtrace.each {|bt|
io.write " #{bt}\n"
}
end

io.flush
nil
end


Related Topics



Leave a reply



Submit