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

2019-03-09 15:57发布

I'm investigating different optimization techniques, and I came across this post Analyzing Code for Efficiency? by someone who believes that sampling the call stack is more effective than using a profiler. The basic idea is that if you take a view of the call stack, you see where your application is most likely to be spending most of its time, and then optimize there.

It is certainly interesting, and he is obviously an expert on this, but I don't know how to view the call stack in ruby. In debugger I can say "info stack" but only seems to show one line.

EDIT: I saw this comment by Mike Dunlavey: "I would just like to point out that if you run under the debugger, interrupt it manually, and display the call stack..."

I'm just not sure how to interrupt it manually and dipslay the call stack.

3条回答
爷、活的狠高调
2楼-- · 2019-03-09 16:42

How about sending signal to the ruby process, and creating a handler for the signal which dumps all stacks?

From http://le-huy.blogspot.com/2012/04/dump-backtrace-of-all-threads-in-ruby.html we have this example :

require 'pp'

def backtrace_for_all_threads(signame)
  File.open("/tmp/ruby_backtrace_#{Process.pid}.txt","a") do |f|
      f.puts "--- got signal #{signame}, dump backtrace for all threads at #{Time.now}"
      if Thread.current.respond_to?(:backtrace)
        Thread.list.each do |t|
          f.puts t.inspect
          PP.pp(t.backtrace.delete_if {|frame| frame =~ /^#{File.expand_path(__FILE__)}/},
               f) # remove frames resulting from calling this method
        end
      else
          PP.pp(caller.delete_if {|frame| frame =~ /^#{File.expand_path(__FILE__)}/},
               f) # remove frames resulting from calling this method
      end
  end
end

Signal.trap(29) do
  backtrace_for_all_threads("INFO")
end

Then we need to send the signal to the appropriate process :

ps afxw | grep ruby
kill -29 <pid>
ls -l /tmp/ruby*
vi /tmp/ruby_backtrace_...

Repeat the signal at appropriate sampling time.

查看更多
劫难
3楼-- · 2019-03-09 16:48

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.

查看更多
别忘想泡老子
4楼-- · 2019-03-09 16:48

You can throw an exception at any time, and then look at the $@ predefined variable, which returns an array of backtrace data. E.g. put this in foo.rb:

begin                                                                        
  raise 'foo'                                                                
rescue                                                                       
  puts $@                                                                    
end  

Then run it:

$ ruby foo.rb 
foo.rb:2:in `<main>'
查看更多
登录 后发表回答