类 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

所有调用时的事件钩子 (callb_callc_call)

:a_return

所有返回时的事件钩子 (returnb_returnc_return)

:thread_begin

线程开始时的事件钩子

:thread_end

线程结束时的事件钩子

:fiber_switch

纤程切换时的事件钩子

:script_compiled

新的 Ruby 代码编译 (使用 evalloadrequire)

公共类方法

allow_reentry { block } 点击切换源代码

一般来说,当 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 trace_point.rb, line 198
def self.allow_reentry
  Primitive.tracepoint_allow_reentry
end
new(*events) { |obj| block } → obj click to toggle source

返回一个新的 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 trace_point.rb, line 96
def self.new(*events)
  Primitive.tracepoint_new_s(events)
end
stat → obj click to toggle source

返回 TracePoint 的内部信息。

返回值的内容是特定于实现的。它可能会在将来更改。

此方法仅用于调试 TracePoint 本身。

# File trace_point.rb, line 118
def self.stat
  Primitive.tracepoint_stat_s
end
trace(*events) { |obj| block } → obj click to toggle source

用于 TracePoint.new 的便捷方法,它会自动激活跟踪。

trace = TracePoint.trace(:call) { |tp| [tp.lineno, tp.event] }
#=> #<TracePoint:enabled>

trace.enabled? #=> true
# File trace_point.rb, line 133
def self.trace(*events)
  Primitive.tracepoint_trace_s(events)
end

公共实例方法

binding() 点击切换源代码

返回从事件生成的绑定对象。

注意,对于 :c_call:c_return 事件,该方法将返回 nil,因为 C 方法本身没有绑定。

# File trace_point.rb, line 381
def binding
  Primitive.tracepoint_attr_binding
end
callee_id() 点击切换源代码

返回正在调用的方法的调用名称。

# File trace_point.rb, line 337
def callee_id
  Primitive.tracepoint_attr_callee_id
end
defined_class() 点击切换源代码

返回正在调用的方法的类或模块。

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 trace_point.rb, line 373
def defined_class
  Primitive.tracepoint_attr_defined_class
end
disable → true 或 false 点击切换源代码
disable { block } → obj

停用跟踪。

如果跟踪已启用,则返回 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 trace_point.rb, line 296
def disable
  Primitive.tracepoint_disable_m
end
enable(target: nil, target_line: nil, target_thread: nil) → true 或 false 点击切换源代码
enable(target: nil, target_line: nil, target_thread: :default) { block } → obj

激活跟踪。

如果跟踪已启用,则返回 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

targettarget_linetarget_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 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
enabled? → true 或 false 点击切换源代码

跟踪的当前状态

# File trace_point.rb, line 304
def enabled?
  Primitive.tracepoint_enabled_p
end
eval_script() 点击切换源代码

:script_compiled 事件上编译的源代码 (String),用于 *eval 方法。 如果从文件加载,它将返回 nil。

# File trace_point.rb, line 407
def eval_script
  Primitive.tracepoint_attr_eval_script
end
event() 点击切换源代码

事件类型

有关更多信息,请参见 TracePoint 上的事件

# File trace_point.rb, line 311
def event
  Primitive.tracepoint_attr_event
end
inspect → string 点击切换源代码

返回一个包含人类可读的 TracePoint 状态的字符串。

# File trace_point.rb, line 105
def inspect
  Primitive.tracepoint_inspect
end
instruction_sequence() 点击切换源代码

:script_compiled 事件上由 RubyVM::InstructionSequence 实例表示的编译指令序列。

请注意,此方法是 MRI 特定的。

# File trace_point.rb, line 415
def instruction_sequence
  Primitive.tracepoint_attr_instruction_sequence
end
lineno() 点击切换源代码

事件的行号

# File trace_point.rb, line 316
def lineno
  Primitive.tracepoint_attr_lineno
end
method_id() 点击切换源代码

返回正在调用的方法定义处的名称

# File trace_point.rb, line 332
def method_id
  Primitive.tracepoint_attr_method_id
end
parameters() 点击切换源代码

返回当前钩子所属的方法或块的参数定义。 格式与 Method#parameters 相同

# File trace_point.rb, line 327
def parameters
  Primitive.tracepoint_attr_parameters
end
path() 点击切换源代码

正在运行的文件的路径

# File trace_point.rb, line 321
def path
  Primitive.tracepoint_attr_path
end
raised_exception() 点击切换源代码

:raise 事件上引发的异常值,或在 :rescue 事件上被拯救的值。

# File trace_point.rb, line 401
def raised_exception
  Primitive.tracepoint_attr_raised_exception
end
return_value() 点击切换源代码

来自 :return:c_return:b_return 事件的返回值

# File trace_point.rb, line 396
def return_value
  Primitive.tracepoint_attr_return_value
end
self() 点击切换源代码

在事件期间返回跟踪对象

与以下相同,只是它为 :c_call:c_return 事件返回正确的对象(方法接收器)

trace.binding.eval('self')
# File trace_point.rb, line 391
def self
  Primitive.tracepoint_attr_self
end