Ruby 对象抛出时如何查看调用栈

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

监狱长 gem 使用 throwcatch 函数来处理控制流。这使得调试身份验证系统变得非常困难,尤其是在 devise 中,其中 gem throw:warden 符号和 warden gem catch是的。

在我的程序中,我有一个函数(间接地)抛出一个 :warden 符号,但我不知道 throw 函数的确切调用位置是什么。有办法找到吗?

TracePoint class 好像只支持 raise 事件。

我能够使用 TracePointthrow 通过捕获 c_call:

是一个 c 语言例程这一事实
TracePoint.new(:c_call) do |trace|
  if trace.method_id == :throw
    p [trace.path, trace.lineno]
  end
end

这只会让您知道调用 throw 的实际位置,而不是调用到该点的所有内容的完整堆栈跟踪,尽管您也可以尝试捕捉 :call , 并将可捕获更多信息的内容放在一起。举个简单的例子:

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

完整示例:

# 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

显然,在这个例子中很难判断哪个方法实际抛出了符号。但是 运行 这个例子,我可以在输出中看到几次(运行 2 个例子):

# 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