TracePoint 类
一个提供 Kernel#set_trace_func
功能的类,以面向对象的 API 方式实现。
示例¶ ↑
我们可以使用 TracePoint
来收集专门针对异常的信息
trace = TracePoint.new(:raise) do |tp| p [tp.lineno, tp.event, tp.raised_exception] end #=> #<TracePoint:disabled> trace.enable #=> false 0 / 0 #=> [5, :raise, #<ZeroDivisionError: divided by 0>]
事件¶ ↑
如果您没有指定要监听的事件类型,TracePoint
将包含所有可用的事件。
注意 不要依赖当前事件集,因为此列表可能会发生变化。建议您指定要使用的事件类型。
要过滤跟踪的内容,您可以将以下任何内容作为 events
传递
:line
-
在新行上执行表达式或语句
:class
-
开始类或模块定义
:end
-
完成类或模块定义
:call
-
调用 Ruby 方法
:return
-
从 Ruby 方法返回
:c_call
-
调用 C 语言例程
:c_return
-
从 C 语言例程返回
:raise
-
引发异常
:rescue
-
捕获异常
:b_call
-
块进入时的事件钩子
:b_return
-
块结束时的事件钩子
:a_call
-
所有调用时的事件钩子 (
call
、b_call
和c_call
) :a_return
-
所有返回时的事件钩子 (
return
、b_return
和c_return
) :thread_begin
-
线程开始时的事件钩子
:thread_end
-
线程结束时的事件钩子
:fiber_switch
-
纤程切换时的事件钩子
:script_compiled
-
新的 Ruby 代码编译 (使用
eval
、load
或require
)
公共类方法
一般来说,当 TracePoint
回调正在运行时,其他注册的回调不会被调用,以避免由于重入而导致的混淆。此方法允许在给定块中进行重入。此方法应谨慎使用,否则回调很容易被无限调用。
如果在已经允许重入的情况下调用此方法,它会引发 RuntimeError
。
示例
# Without reentry # --------------- line_handler = TracePoint.new(:line) do |tp| next if tp.path != __FILE__ # only work in this file puts "Line handler" binding.eval("class C; end") end.enable class_handler = TracePoint.new(:class) do |tp| puts "Class handler" end.enable class B end # This script will print "Class handler" only once: when inside :line # handler, all other handlers are ignored # With reentry # ------------ line_handler = TracePoint.new(:line) do |tp| next if tp.path != __FILE__ # only work in this file next if (__LINE__..__LINE__+3).cover?(tp.lineno) # don't be invoked from itself puts "Line handler" TracePoint.allow_reentry { binding.eval("class C; end") } end.enable class_handler = TracePoint.new(:class) do |tp| puts "Class handler" end.enable class B end # This wil print "Class handler" twice: inside allow_reentry block in :line # handler, other handlers are enabled.
请注意,此示例展示了该方法的主要效果,但其实际用途是用于调试有时需要其他库钩子不受调试器在跟踪点处理中影响的库。在这种情况下,应注意防止无限递归(请注意,我们需要从 :line 处理程序中过滤掉自身的调用,否则它将无限调用自身)。
# File ruby_3_3_0/trace_point.rb, line 198 def self.allow_reentry Primitive.tracepoint_allow_reentry end
返回一个新的 TracePoint
对象,默认情况下未启用。
接下来,为了激活跟踪,您必须使用 TracePoint#enable
trace = TracePoint.new(:call) do |tp| p [tp.lineno, tp.defined_class, tp.method_id, tp.event] end #=> #<TracePoint:disabled> trace.enable #=> false puts "Hello, TracePoint!" # ... # [48, IRB::Notifier::AbstractNotifier, :printf, :call] # ...
当您想要停用跟踪时,您必须使用 TracePoint#disable
trace.disable
有关可能的事件和更多信息,请参阅 TracePoint
中的事件。
必须提供一个块,否则会引发 ArgumentError
。
如果跟踪方法未包含在给定的事件过滤器中,则会引发 RuntimeError
。
TracePoint.trace(:line) do |tp| p tp.raised_exception end #=> RuntimeError: 'raised_exception' not supported by this event
如果跟踪方法在块外部调用,则会引发 RuntimeError
。
TracePoint.trace(:line) do |tp| $tp = tp end $tp.lineno #=> access from outside (RuntimeError)
也禁止从其他线程访问。
# File ruby_3_3_0/trace_point.rb, line 96 def self.new(*events) Primitive.tracepoint_new_s(events) end
返回 TracePoint
的内部信息。
返回值的内容是特定于实现的。它可能会在将来发生变化。
此方法仅用于调试 TracePoint
本身。
# File ruby_3_3_0/trace_point.rb, line 118 def self.stat Primitive.tracepoint_stat_s end
一个用于 TracePoint.new
的便捷方法,它会自动激活跟踪。
trace = TracePoint.trace(:call) { |tp| [tp.lineno, tp.event] } #=> #<TracePoint:enabled> trace.enabled? #=> true
# File ruby_3_3_0/trace_point.rb, line 133 def self.trace(*events) Primitive.tracepoint_trace_s(events) end
公共实例方法
返回事件生成的绑定对象。
注意,对于 :c_call
和 :c_return
事件,该方法将返回 nil
,因为 C 方法本身没有绑定。
# File ruby_3_3_0/trace_point.rb, line 381 def binding Primitive.tracepoint_attr_binding end
返回正在调用的方法的调用名称。
# File ruby_3_3_0/trace_point.rb, line 337 def callee_id Primitive.tracepoint_attr_callee_id end
返回正在调用的方法的类或模块。
class C; def foo; end; end trace = TracePoint.new(:call) do |tp| p tp.defined_class #=> C end.enable do C.new.foo end
如果方法由模块定义,则返回该模块。
module M; def foo; end; end class C; include M; end; trace = TracePoint.new(:call) do |tp| p tp.defined_class #=> M end.enable do C.new.foo end
注意: defined_class
返回单例类。
Kernel#set_trace_func
的第 6 个块参数传递由单例类附加的原始类。
这是 Kernel#set_trace_func 和 TracePoint 之间的区别。
class C; def self.foo; end; end trace = TracePoint.new(:call) do |tp| p tp.defined_class #=> #<Class:C> end.enable do C.foo end
# File ruby_3_3_0/trace_point.rb, line 373 def defined_class Primitive.tracepoint_attr_defined_class end
停用跟踪。
如果跟踪已启用,则返回 true。如果跟踪已停用,则返回 false。
trace.enabled? #=> true trace.disable #=> true (previous status) trace.enabled? #=> false trace.disable #=> false
如果提供了块,则跟踪仅在块范围内停用。
trace.enabled? #=> true trace.disable do trace.enabled? # only disabled for this block end trace.enabled? #=> true
注意:您无法在块中访问事件钩子。
trace.disable { p tp.lineno } #=> RuntimeError: access from outside
# File ruby_3_3_0/trace_point.rb, line 296 def disable Primitive.tracepoint_disable_m end
激活跟踪。
如果跟踪已启用,则返回 true
。如果跟踪已停用,则返回 false
。
trace.enabled? #=> false trace.enable #=> false (previous state) # trace is enabled trace.enabled? #=> true trace.enable #=> true (previous state) # trace is still enabled
如果提供了块,则跟踪仅在块调用期间启用。如果 target 和 target_line 都为 nil,则如果提供了块,target_thread 将默认为当前线程。
trace.enabled? #=> false trace.enable do trace.enabled? # only enabled for this block and thread end trace.enabled? #=> false
target
、target_line
和 target_thread
参数用于将跟踪限制在指定的代码对象上。target
应该是一个代码对象,对于该对象,RubyVM::InstructionSequence.of
将返回指令序列。
t = TracePoint.new(:line) { |tp| p tp } def m1 p 1 end def m2 p 2 end t.enable(target: method(:m1)) m1 # prints #<TracePoint:line test.rb:4 in `m1'> m2 # prints nothing
注意:您无法在 enable
块中访问事件钩子。
trace.enable { p tp.lineno } #=> RuntimeError: access from outside
# File ruby_3_3_0/trace_point.rb, line 260 def enable(target: nil, target_line: nil, target_thread: :default) Primitive.tracepoint_enable_m(target, target_line, target_thread) end
跟踪的当前状态
# File ruby_3_3_0/trace_point.rb, line 304 def enabled? Primitive.tracepoint_enabled_p end
在 :script_compiled
事件上编译的源代码 (String
),用于 *eval 方法。如果从文件加载,它将返回 nil。
# File ruby_3_3_0/trace_point.rb, line 407 def eval_script Primitive.tracepoint_attr_eval_script end
事件类型
有关更多信息,请参阅 TracePoint
上的事件。
# File ruby_3_3_0/trace_point.rb, line 311 def event Primitive.tracepoint_attr_event end
返回一个包含人类可读的 TracePoint
状态的字符串。
# File ruby_3_3_0/trace_point.rb, line 105 def inspect Primitive.tracepoint_inspect end
在 :script_compiled
事件上,由 RubyVM::InstructionSequence
实例表示的已编译指令序列。
请注意,此方法是 MRI 特定的。
# File ruby_3_3_0/trace_point.rb, line 415 def instruction_sequence Primitive.tracepoint_attr_instruction_sequence end
事件的行号
# File ruby_3_3_0/trace_point.rb, line 316 def lineno Primitive.tracepoint_attr_lineno end
返回正在调用的方法定义处的名称
# File ruby_3_3_0/trace_point.rb, line 332 def method_id Primitive.tracepoint_attr_method_id end
返回当前钩子所属的方法或块的参数定义。格式与 Method#parameters
相同
# File ruby_3_3_0/trace_point.rb, line 327 def parameters Primitive.tracepoint_attr_parameters end
正在运行的文件的路径
# File ruby_3_3_0/trace_point.rb, line 321 def path Primitive.tracepoint_attr_path end
在 :raise
事件上引发的异常的值,或在 :rescue
事件上被拯救的值。
# File ruby_3_3_0/trace_point.rb, line 401 def raised_exception Primitive.tracepoint_attr_raised_exception end
来自 :return
、:c_return
和 :b_return
事件的返回值
# File ruby_3_3_0/trace_point.rb, line 396 def return_value Primitive.tracepoint_attr_return_value end
在事件期间返回跟踪对象
与以下相同,只是它为 :c_call
和 :c_return
事件返回正确的对象(方法接收器)
trace.binding.eval('self')
# File ruby_3_3_0/trace_point.rb, line 391 def self Primitive.tracepoint_attr_self end