use builtin for TracePoint.

Define TracePoint in trace_point.rb and use __builtin_ syntax.
This commit is contained in:
Koichi Sasada 2019-11-07 18:22:08 +09:00
parent 46acd0075d
commit e2a45cb984
Notes: git 2019-11-08 09:10:08 +09:00
8 changed files with 527 additions and 438 deletions

View File

@ -9,9 +9,10 @@
# prelude
prelude.rb
rbconfig.rb
trace_point.rb
# the lib/ directory (which has its own .document file)
lib

View File

@ -1094,11 +1094,14 @@ preludes: {$(VPATH)}prelude.c
preludes: {$(VPATH)}miniprelude.c
preludes: {$(srcdir)}golf_prelude.c
BUILTIN_RB_SRCS =
BUILTIN_RB_SRCS = $(srcdir)/trace_point.rb
builtin_binary.inc: $(PREP) $(BUILTIN_RB_SRCS) $(srcdir)/tool/mk_builtin_binary.rb
$(Q) $(MINIRUBY) $(srcdir)/tool/mk_builtin_binary.rb
load_trace_point.inc: $(srcdir)/trace_point.rb $(srcdir)/tool/mk_builtin_loader.rb
$(Q) $(BASERUBY) $(srcdir)/tool/mk_builtin_loader.rb $(srcdir)/trace_point.rb
$(srcdir)/revision.h:
$(Q)$(gnumake:yes=#) $(RM) $(@F)
$(Q)$(gnumake:yes=#) exit > $@ || exit > $(@F)
@ -3389,6 +3392,7 @@ vm_trace.$(OBJEXT): $(CCAN_DIR)/str/str.h
vm_trace.$(OBJEXT): $(hdrdir)/ruby.h
vm_trace.$(OBJEXT): $(hdrdir)/ruby/ruby.h
vm_trace.$(OBJEXT): {$(VPATH)}assert.h
vm_trace.$(OBJEXT): {$(VPATH)}builtin.h
vm_trace.$(OBJEXT): {$(VPATH)}config.h
vm_trace.$(OBJEXT): {$(VPATH)}debug.h
vm_trace.$(OBJEXT): {$(VPATH)}debug_counter.h
@ -3400,6 +3404,7 @@ vm_trace.$(OBJEXT): {$(VPATH)}intern.h
vm_trace.$(OBJEXT): {$(VPATH)}internal.h
vm_trace.$(OBJEXT): {$(VPATH)}io.h
vm_trace.$(OBJEXT): {$(VPATH)}iseq.h
vm_trace.$(OBJEXT): {$(VPATH)}load_trace_point.inc
vm_trace.$(OBJEXT): {$(VPATH)}method.h
vm_trace.$(OBJEXT): {$(VPATH)}missing.h
vm_trace.$(OBJEXT): {$(VPATH)}mjit.h

View File

@ -65,11 +65,12 @@ rb_call_inits(void)
CALL(Rational);
CALL(Complex);
CALL(version);
CALL(vm_trace);
CALL(vm_stack_canary);
CALL(ast);
CALL(gc_stress);
CALL(builtin);
CALL(vm_trace);
}
#undef CALL

View File

@ -136,69 +136,6 @@ class IO
end
end
class TracePoint
# call-seq:
# trace.enable(target: nil, target_line: nil, target_thread: nil) -> true or false
# trace.enable(target: nil, target_line: nil, target_thread: nil) { block } -> obj
#
# Activates the trace.
#
# Returns +true+ if trace was enabled.
# Returns +false+ if trace was disabled.
#
# trace.enabled? #=> false
# trace.enable #=> false (previous state)
# # trace is enabled
# trace.enabled? #=> true
# trace.enable #=> true (previous state)
# # trace is still enabled
#
# If a block is given, the trace will only be enabled within the scope of the
# block.
#
# trace.enabled?
# #=> false
#
# trace.enable do
# trace.enabled?
# # only enabled for this block
# end
#
# trace.enabled?
# #=> false
#
# +target+, +target_line+ and +target_thread+ parameters are used to
# limit tracing only to specified code objects. +target+ should be a
# code object for which RubyVM::InstructionSequence.of will return
# an instruction sequence.
#
# 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:5 in `m1'>
# m2
# # prints nothing
#
# Note: You cannot access event hooks within the +enable+ block.
#
# trace.enable { p tp.lineno }
# #=> RuntimeError: access from outside
#
def enable target: nil, target_line: nil, target_thread: nil, &blk
self.__enable target, target_line, target_thread, &blk
end
end
class Binding
# :nodoc:
def irb

View File

@ -466,6 +466,130 @@ class TestSetTraceFunc < Test::Unit::TestCase
trace.disable if trace&.enabled?
end
answer_events = [
#
[:line, 4, 'xyzzy', self.class, method, self, :outer, :nothing],
[:c_call, 4, 'xyzzy', Integer, :times, 1, :outer, :nothing],
[:line, 4, 'xyzzy', self.class, method, self, nil, :nothing],
[:line, 5, 'xyzzy', self.class, method, self, :inner, :nothing],
[:c_call, 5, 'xyzzy', Kernel, :tap, self, :inner, :nothing],
[:c_return, 5, "xyzzy", Kernel, :tap, self, :inner, self],
[:c_return, 4, "xyzzy", Integer, :times, 1, :outer, 1],
[:line, 7, 'xyzzy', self.class, method, self, :outer, :nothing],
[:c_call, 7, "xyzzy", Class, :inherited, Object, :outer, :nothing],
[:c_return, 7, "xyzzy", Class, :inherited, Object, :outer, nil],
[:class, 7, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
[:line, 8, "xyzzy", nil, nil, xyzzy.class, nil, :nothing],
[:line, 9, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
[:c_call, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
[:c_return, 9, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
[:line, 13, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
[:c_call, 13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, :nothing],
[:c_return,13, "xyzzy", Module, :method_added, xyzzy.class, :XYZZY_outer, nil],
[:end, 17, "xyzzy", nil, nil, xyzzy.class, :XYZZY_outer, :nothing],
[:line, 18, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
[:c_call, 18, "xyzzy", Class, :new, xyzzy.class, :outer, :nothing],
[:c_call, 18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, :nothing],
[:c_return,18, "xyzzy", BasicObject, :initialize, xyzzy, :outer, nil],
[:c_return,18, "xyzzy", Class, :new, xyzzy.class, :outer, xyzzy],
[:line, 19, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
[:call, 9, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
[:line, 10, "xyzzy", xyzzy.class, :foo, xyzzy, nil, :nothing],
[:line, 11, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, :nothing],
[:call, 13, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
[:line, 14, "xyzzy", xyzzy.class, :bar, xyzzy, nil, :nothing],
[:line, 15, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, :nothing],
[:c_call, 15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, :nothing],
[:c_return,15, "xyzzy", Kernel, :tap, xyzzy, :XYZZY_bar, xyzzy],
[:return, 16, "xyzzy", xyzzy.class, :bar, xyzzy, :XYZZY_bar, xyzzy],
[:return, 12, "xyzzy", xyzzy.class, :foo, xyzzy, :XYZZY_foo, xyzzy],
[:line, 20, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
[:c_call, 20, "xyzzy", Kernel, :raise, self, :outer, :nothing],
[:c_call, 20, "xyzzy", Exception, :exception, RuntimeError, :outer, :nothing],
[:c_call, 20, "xyzzy", Exception, :initialize, raised_exc, :outer, :nothing],
[:c_return,20, "xyzzy", Exception, :initialize, raised_exc, :outer, raised_exc],
[:c_return,20, "xyzzy", Exception, :exception, RuntimeError, :outer, raised_exc],
[:c_return,20, "xyzzy", Kernel, :raise, self, :outer, nil],
[:c_call, 20, "xyzzy", Exception, :backtrace, raised_exc, :outer, :nothing],
[:c_return,20, "xyzzy", Exception, :backtrace, raised_exc, :outer, nil],
[:raise, 20, "xyzzy", TestSetTraceFunc, :trace_by_tracepoint, self, :outer, raised_exc],
[:c_call, 20, "xyzzy", Module, :===, RuntimeError,:outer, :nothing],
[:c_return,20, "xyzzy", Module, :===, RuntimeError,:outer, true],
[:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
]
return events, answer_events
end
def test_tracepoint
events1, answer_events = *trace_by_tracepoint(:line, :class, :end, :call, :return, :c_call, :c_return, :raise)
ms = [events1, answer_events].map{|evs|
evs.map{|e|
"#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}"
}
}
if false # show all events
printf(" %-60s | %-60s\n", "actual", "expected")
ms[0].zip(ms[1]){|a, b|
printf("%s%-60s | %-60s\n", a==b ? ' ' : '!', a, b)
}
end
mesg = ms[0].zip(ms[1]).map{|a, b|
if a != b
"actual: #{a} <-> expected: #{b}"
end
}.compact.join("\n")
answer_events.zip(events1){|answer, event|
assert_equal answer, event, mesg
}
[:line, :class, :end, :call, :return, :c_call, :c_return, :raise].each{|event|
events1, answer_events = *trace_by_tracepoint(event)
answer_events.find_all{|e| e[0] == event}.zip(events1){|answer_line, event_line|
assert_equal answer_line, event_line
}
}
end
def trace_by_set_trace_func
events = []
trace = nil
trace = trace
xyzzy = nil
xyzzy = xyzzy
_local_var = :outer
method = :trace_by_set_trace_func
raised_exc = nil
eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
1: set_trace_func(lambda{|event, file, line, id, binding, klass|
2: events << [event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")] if file == 'xyzzy'
3: })
4: 1.times{|;_local_var| _local_var = :inner
5: tap{}
6: }
7: class XYZZY
8: _local_var = :XYZZY_outer
9: def foo
10: _local_var = :XYZZY_foo
11: bar
12: end
13: def bar
14: _local_var = :XYZZY_bar
15: tap{}
16: end
17: end
18: xyzzy = XYZZY.new
19: xyzzy.foo
20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
21: set_trace_func(nil)
EOF
self.class.class_eval{remove_const(:XYZZY)}
answer_events = [
#
[:c_return, 1, "xyzzy", TracePoint, :trace, TracePoint, :outer, trace],
@ -519,79 +643,19 @@ class TestSetTraceFunc < Test::Unit::TestCase
[:line, 21, "xyzzy", TestSetTraceFunc, method, self, :outer, :nothing],
[:c_call, 21, "xyzzy", TracePoint, :disable, trace, :outer, :nothing],
]
return events, answer_events
end
def trace_by_set_trace_func
events = []
trace = nil
trace = trace
xyzzy = nil
xyzzy = xyzzy
_local_var = :outer
eval <<-EOF.gsub(/^.*?: /, ""), nil, 'xyzzy'
1: set_trace_func(lambda{|event, file, line, id, binding, klass|
2: events << [event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")] if file == 'xyzzy'
3: })
4: 1.times{|;_local_var| _local_var = :inner
5: tap{}
6: }
7: class XYZZY
8: _local_var = :XYZZY_outer
9: def foo
10: _local_var = :XYZZY_foo
11: bar
12: end
13: def bar
14: _local_var = :XYZZY_bar
15: tap{}
16: end
17: end
18: xyzzy = XYZZY.new
19: xyzzy.foo
20: begin; raise RuntimeError; rescue RuntimeError => raised_exc; end
21: set_trace_func(nil)
EOF
self.class.class_eval{remove_const(:XYZZY)}
return events
end
def test_tracepoint
events1, answer_events = *trace_by_tracepoint(:line, :class, :end, :call, :return, :c_call, :c_return, :raise)
ms = [events1, answer_events].map{|evs|
evs.map{|e|
"#{e[0]} - #{e[2]}:#{e[1]} id: #{e[4]}"
}
}
mesg = ms[0].zip(ms[1]).map{|a, b|
if a != b
"#{a} <-> #{b}"
end
}.compact.join("\n")
answer_events.zip(events1){|answer, event|
assert_equal answer, event, mesg
}
events2 = trace_by_set_trace_func
events1.zip(events2){|ev1, ev2|
ev2[0] = ev2[0].sub('-', '_').to_sym
assert_equal ev1[0..2], ev2[0..2], ev1.inspect
def test_set_trace_func
actual_events, expected_events = trace_by_set_trace_func
expected_events.zip(actual_events){|e, a|
a[0] = a[0].to_s.sub('-', '_').to_sym
assert_equal e[0..2], a[0..2], a.inspect
# event, line, file, klass, id, binding.eval('self'), binding.eval("_local_var")
assert_equal ev1[3].nil?, ev2[3].nil? # klass
assert_equal ev1[4].nil?, ev2[4].nil? # id
assert_equal ev1[6], ev2[6] # _local_var
}
[:line, :class, :end, :call, :return, :c_call, :c_return, :raise].each{|event|
events1, answer_events = *trace_by_tracepoint(event)
answer_events.find_all{|e| e[0] == event}.zip(events1){|answer_line, event_line|
assert_equal answer_line, event_line
}
assert_equal e[3].nil?, a[3].nil? # klass
assert_equal e[4].nil?, a[4].nil? # id
assert_equal e[6], a[6] # _local_var
}
end
@ -676,7 +740,7 @@ class TestSetTraceFunc < Test::Unit::TestCase
}
foo
trace.disable
assert_equal([:foo, :foo], ary)
assert_equal([:foo, :disable, :foo, :disable], ary)
assert_equal([], args)
trace = TracePoint.new{}

View File

@ -110,9 +110,8 @@ module TestParallel
end
result = Marshal.load($1.chomp.unpack("m")[0])
assert_equal(5, result[0])
assert_equal(17, result[1])
assert_equal(12, result[1])
assert_kind_of(Array,result[2])
assert_kind_of(Array,result[3])
assert_kind_of(Array,result[4])

348
trace_point.rb Normal file
View File

@ -0,0 +1,348 @@
# loaded from vm_trace.c
# Document-class: TracePoint
#
# A class that provides the functionality of Kernel#set_trace_func in a
# nice Object-Oriented API.
#
# == Example
#
# We can use TracePoint to gather information specifically for exceptions:
#
# 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>]
#
# == Events
#
# If you don't specify the type of events you want to listen for,
# TracePoint will include all available events.
#
# *Note* do not depend on current event set, as this list is subject to
# change. Instead, it is recommended you specify the type of events you
# want to use.
#
# To filter what is traced, you can pass any of the following as +events+:
#
# +:line+:: execute code on a new line
# +:class+:: start a class or module definition
# +:end+:: finish a class or module definition
# +:call+:: call a Ruby method
# +:return+:: return from a Ruby method
# +:c_call+:: call a C-language routine
# +:c_return+:: return from a C-language routine
# +:raise+:: raise an exception
# +:b_call+:: event hook at block entry
# +:b_return+:: event hook at block ending
# +:thread_begin+:: event hook at thread beginning
# +:thread_end+:: event hook at thread ending
# +:fiber_switch+:: event hook at fiber switch
# +:script_compiled+:: new Ruby code compiled (with +eval+, +load+ or +require+)
#
class TracePoint
# call-seq:
# TracePoint.new(*events) { |obj| block } -> obj
#
# Returns a new TracePoint object, not enabled by default.
#
# Next, in order to activate the trace, you must use 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]
# # ...
#
# When you want to deactivate the trace, you must use TracePoint#disable
#
# trace.disable
#
# See TracePoint@Events for possible events and more information.
#
# A block must be given, otherwise an ArgumentError is raised.
#
# If the trace method isn't included in the given events filter, a
# RuntimeError is raised.
#
# TracePoint.trace(:line) do |tp|
# p tp.raised_exception
# end
# #=> RuntimeError: 'raised_exception' not supported by this event
#
# If the trace method is called outside block, a RuntimeError is raised.
#
# TracePoint.trace(:line) do |tp|
# $tp = tp
# end
# $tp.lineno #=> access from outside (RuntimeError)
#
# Access from other threads is also forbidden.
#
def self.new(*events)
__builtin_tracepoint_new_s(events)
end
# call-seq:
# trace.inspect -> string
#
# Return a string containing a human-readable TracePoint
# status.
def inspect
__builtin_tracepoint_inspect
end
# call-seq:
# TracePoint.stat -> obj
#
# Returns internal information of TracePoint.
#
# The contents of the returned value are implementation specific.
# It may be changed in future.
#
# This method is only for debugging TracePoint itself.
def stat
__builtin_tracepoint_stat_s
end
# Document-method: trace
#
# call-seq:
# TracePoint.trace(*events) { |obj| block } -> obj
#
# A convenience method for TracePoint.new, that activates the trace
# automatically.
#
# trace = TracePoint.trace(:call) { |tp| [tp.lineno, tp.event] }
# #=> #<TracePoint:enabled>
#
# trace.enabled? #=> true
#
def self.trace(*events)
__builtin_tracepoint_trace_s(events)
end
# call-seq:
# trace.enable(target: nil, target_line: nil, target_thread: nil) -> true or false
# trace.enable(target: nil, target_line: nil, target_thread: nil) { block } -> obj
#
# Activates the trace.
#
# Returns +true+ if trace was enabled.
# Returns +false+ if trace was disabled.
#
# trace.enabled? #=> false
# trace.enable #=> false (previous state)
# # trace is enabled
# trace.enabled? #=> true
# trace.enable #=> true (previous state)
# # trace is still enabled
#
# If a block is given, the trace will only be enabled within the scope of the
# block.
#
# trace.enabled?
# #=> false
#
# trace.enable do
# trace.enabled?
# # only enabled for this block
# end
#
# trace.enabled?
# #=> false
#
# +target+, +target_line+ and +target_thread+ parameters are used to
# limit tracing only to specified code objects. +target+ should be a
# code object for which RubyVM::InstructionSequence.of will return
# an instruction sequence.
#
# 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:5 in `m1'>
# m2
# # prints nothing
#
# Note: You cannot access event hooks within the +enable+ block.
#
# trace.enable { p tp.lineno }
# #=> RuntimeError: access from outside
#
def enable(target: nil, target_line: nil, target_thread: nil)
__builtin_tracepoint_enable_m(target, target_line, target_thread)
end
# call-seq:
# trace.disable -> true or false
# trace.disable { block } -> obj
#
# Deactivates the trace
#
# Return true if trace was enabled.
# Return false if trace was disabled.
#
# trace.enabled? #=> true
# trace.disable #=> true (previous status)
# trace.enabled? #=> false
# trace.disable #=> false
#
# If a block is given, the trace will only be disable within the scope of the
# block.
#
# trace.enabled?
# #=> true
#
# trace.disable do
# trace.enabled?
# # only disabled for this block
# end
#
# trace.enabled?
# #=> true
#
# Note: You cannot access event hooks within the block.
#
# trace.disable { p tp.lineno }
# #=> RuntimeError: access from outside
def disable
__builtin_tracepoint_disable_m
end
# call-seq:
# trace.enabled? -> true or false
#
# The current status of the trace
def enabled?
__builtin_tracepoint_enabled_p
end
# Type of event
#
# See TracePoint@Events for more information.
def event
__builtin_tracepoint_attr_event
end
# Line number of the event
def lineno
__builtin_tracepoint_attr_lineno
end
# Path of the file being run
def path
__builtin_tracepoint_attr_path
end
# Return the parameters definition of the method or block that the
# current hook belongs to. Format is the same as for Method#parameters
def parameters
__builtin_tracepoint_attr_parameters
end
# Return the name at the definition of the method being called
def method_id
__builtin_tracepoint_attr_method_id
end
# Return the called name of the method being called
def callee_id
__builtin_tracepoint_attr_callee_id
end
# Return class or module of the method being called.
#
# class C; def foo; end; end
# trace = TracePoint.new(:call) do |tp|
# p tp.defined_class #=> C
# end.enable do
# C.new.foo
# end
#
# If method is defined by a module, then that module is returned.
#
# 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
#
# <b>Note:</b> #defined_class returns singleton class.
#
# 6th block parameter of Kernel#set_trace_func passes original class
# of attached by singleton class.
#
# <b>This is a difference between Kernel#set_trace_func and TracePoint.</b>
#
# 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
def defined_class
__builtin_tracepoint_attr_defined_class
end
# Return the generated binding object from event
def binding
__builtin_tracepoint_attr_binding
end
# Return the trace object during event
#
# Same as TracePoint#binding:
# trace.binding.eval('self')
def self
__builtin_tracepoint_attr_self
end
# Return value from +:return+, +c_return+, and +b_return+ event
def return_value
__builtin_tracepoint_attr_return_value
end
# Value from exception raised on the +:raise+ event
def raised_exception
__builtin_tracepoint_attr_raised_exception
end
# Compiled source code (String) on *eval methods on the +:script_compiled+ event.
# If loaded from a file, it will return nil.
def eval_script
__builtin_tracepoint_attr_eval_script
end
# Compiled instruction sequence represented by a RubyVM::InstructionSequence instance
# on the +:script_compiled+ event.
#
# Note that this method is MRI specific.
def instruction_sequence
__builtin_tracepoint_attr_instruction_sequence
end
end

View File

@ -28,6 +28,7 @@
#include "mjit.h"
#include "iseq.h"
#include "eval_intern.h"
#include "builtin.h"
/* (1) trace mechanisms */
@ -1018,160 +1019,79 @@ rb_tracearg_object(rb_trace_arg_t *trace_arg)
return trace_arg->data;
}
/*
* Type of event
*
* See TracePoint@Events for more information.
*/
static VALUE
tracepoint_attr_event(VALUE tpval)
tracepoint_attr_event(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_event(get_trace_arg());
}
/*
* Line number of the event
*/
static VALUE
tracepoint_attr_lineno(VALUE tpval)
tracepoint_attr_lineno(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_lineno(get_trace_arg());
}
/*
* Path of the file being run
*/
static VALUE
tracepoint_attr_path(VALUE tpval)
tracepoint_attr_path(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_path(get_trace_arg());
}
/*
* Return the parameters definition of the method or block that the
* current hook belongs to. Format is the same as for Method#parameters
*/
static VALUE
tracepoint_attr_parameters(VALUE tpval)
tracepoint_attr_parameters(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_parameters(get_trace_arg());
}
/*
* Return the name at the definition of the method being called
*/
static VALUE
tracepoint_attr_method_id(VALUE tpval)
tracepoint_attr_method_id(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_method_id(get_trace_arg());
}
/*
* Return the called name of the method being called
*/
static VALUE
tracepoint_attr_callee_id(VALUE tpval)
tracepoint_attr_callee_id(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_callee_id(get_trace_arg());
}
/*
* Return class or module of the method being called.
*
* class C; def foo; end; end
* trace = TracePoint.new(:call) do |tp|
* p tp.defined_class #=> C
* end.enable do
* C.new.foo
* end
*
* If method is defined by a module, then that module is returned.
*
* 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
*
* <b>Note:</b> #defined_class returns singleton class.
*
* 6th block parameter of Kernel#set_trace_func passes original class
* of attached by singleton class.
*
* <b>This is a difference between Kernel#set_trace_func and TracePoint.</b>
*
* 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
*/
static VALUE
tracepoint_attr_defined_class(VALUE tpval)
tracepoint_attr_defined_class(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_defined_class(get_trace_arg());
}
/*
* Return the generated binding object from event
*/
static VALUE
tracepoint_attr_binding(VALUE tpval)
tracepoint_attr_binding(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_binding(get_trace_arg());
}
/*
* Return the trace object during event
*
* Same as TracePoint#binding:
* trace.binding.eval('self')
*/
static VALUE
tracepoint_attr_self(VALUE tpval)
tracepoint_attr_self(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_self(get_trace_arg());
}
/*
* Return value from +:return+, +c_return+, and +b_return+ event
*/
static VALUE
tracepoint_attr_return_value(VALUE tpval)
tracepoint_attr_return_value(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_return_value(get_trace_arg());
}
/*
* Value from exception raised on the +:raise+ event
*/
static VALUE
tracepoint_attr_raised_exception(VALUE tpval)
tracepoint_attr_raised_exception(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_raised_exception(get_trace_arg());
}
/*
* Compiled source code (String) on *eval methods on the +:script_compiled+ event.
* If loaded from a file, it will return nil.
*/
static VALUE
tracepoint_attr_eval_script(VALUE tpval)
tracepoint_attr_eval_script(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_eval_script(get_trace_arg());
}
/*
* Compiled instruction sequence represented by a RubyVM::InstructionSequence instance
* on the +:script_compiled+ event.
*
* Note that this method is MRI specific.
*/
static VALUE
tracepoint_attr_instruction_sequence(VALUE tpval)
tracepoint_attr_instruction_sequence(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracearg_instruction_sequence(get_trace_arg());
}
@ -1352,11 +1272,8 @@ rb_hook_list_remove_tracepoint(rb_hook_list_t *list, VALUE tpval)
list->events = events;
}
/* :nodoc:
* Docs for the TracePointe#enable are in prelude.rb
*/
static VALUE
tracepoint_enable_m(VALUE tpval, VALUE target, VALUE target_line, VALUE target_thread)
tracepoint_enable_m(rb_execution_context_t *ec, VALUE tpval, VALUE target, VALUE target_line, VALUE target_thread)
{
rb_tp_t *tp = tpptr(tpval);
int previous_tracing = tp->tracing;
@ -1392,43 +1309,8 @@ tracepoint_enable_m(VALUE tpval, VALUE target, VALUE target_line, VALUE target_t
}
}
/*
* call-seq:
* trace.disable -> true or false
* trace.disable { block } -> obj
*
* Deactivates the trace
*
* Return true if trace was enabled.
* Return false if trace was disabled.
*
* trace.enabled? #=> true
* trace.disable #=> true (previous status)
* trace.enabled? #=> false
* trace.disable #=> false
*
* If a block is given, the trace will only be disable within the scope of the
* block.
*
* trace.enabled?
* #=> true
*
* trace.disable do
* trace.enabled?
* # only disabled for this block
* end
*
* trace.enabled?
* #=> true
*
* Note: You cannot access event hooks within the block.
*
* trace.disable { p tp.lineno }
* #=> RuntimeError: access from outside
*/
static VALUE
tracepoint_disable_m(VALUE tpval)
tracepoint_disable_m(rb_execution_context_t *ec, VALUE tpval)
{
rb_tp_t *tp = tpptr(tpval);
int previous_tracing = tp->tracing;
@ -1449,12 +1331,6 @@ tracepoint_disable_m(VALUE tpval)
}
}
/*
* call-seq:
* trace.enabled? -> true or false
*
* The current status of the trace
*/
VALUE
rb_tracepoint_enabled_p(VALUE tpval)
{
@ -1462,6 +1338,12 @@ rb_tracepoint_enabled_p(VALUE tpval)
return tp->tracing ? Qtrue : Qfalse;
}
static VALUE
tracepoint_enabled_p(rb_execution_context_t *ec, VALUE tpval)
{
return rb_tracepoint_enabled_p(tpval);
}
static VALUE
tracepoint_new(VALUE klass, rb_thread_t *target_th, rb_event_flag_t events, void (func)(VALUE, void*), void *data, VALUE proc)
{
@ -1522,63 +1404,17 @@ rb_tracepoint_new(VALUE target_thval, rb_event_flag_t events, void (*func)(VALUE
return tracepoint_new(rb_cTracePoint, target_th, events, func, data, Qundef);
}
/*
* call-seq:
* TracePoint.new(*events) { |obj| block } -> obj
*
* Returns a new TracePoint object, not enabled by default.
*
* Next, in order to activate the trace, you must use 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]
* # ...
*
* When you want to deactivate the trace, you must use TracePoint#disable
*
* trace.disable
*
* See TracePoint@Events for possible events and more information.
*
* A block must be given, otherwise an ArgumentError is raised.
*
* If the trace method isn't included in the given events filter, a
* RuntimeError is raised.
*
* TracePoint.trace(:line) do |tp|
* p tp.raised_exception
* end
* #=> RuntimeError: 'raised_exception' not supported by this event
*
* If the trace method is called outside block, a RuntimeError is raised.
*
* TracePoint.trace(:line) do |tp|
* $tp = tp
* end
* $tp.lineno #=> access from outside (RuntimeError)
*
* Access from other threads is also forbidden.
*
*/
static VALUE
tracepoint_new_s(int argc, VALUE *argv, VALUE self)
tracepoint_new_s(rb_execution_context_t *ec, VALUE self, VALUE args)
{
rb_event_flag_t events = 0;
int i;
long i;
long argc = RARRAY_LEN(args);
if (argc > 0) {
for (i=0; i<argc; i++) {
events |= symbol2event_flag(argv[i]);
}
for (i=0; i<argc; i++) {
events |= symbol2event_flag(RARRAY_AREF(args, i));
}
}
else {
events = RUBY_EVENT_TRACEPOINT_ALL;
@ -1592,23 +1428,15 @@ tracepoint_new_s(int argc, VALUE *argv, VALUE self)
}
static VALUE
tracepoint_trace_s(int argc, VALUE *argv, VALUE self)
tracepoint_trace_s(rb_execution_context_t *ec, VALUE self, VALUE args)
{
VALUE trace = tracepoint_new_s(argc, argv, self);
VALUE trace = tracepoint_new_s(ec, self, args);
rb_tracepoint_enable(trace);
return trace;
}
/*
* call-seq:
* trace.inspect -> string
*
* Return a string containing a human-readable TracePoint
* status.
*/
static VALUE
tracepoint_inspect(VALUE self)
tracepoint_inspect(rb_execution_context_t *ec, VALUE self)
{
rb_tp_t *tp = tpptr(self);
rb_trace_arg_t *trace_arg = GET_EC()->trace_arg;
@ -1671,20 +1499,8 @@ tracepoint_stat_event_hooks(VALUE hash, VALUE key, rb_event_hook_t *hook)
rb_hash_aset(hash, key, rb_ary_new3(2, INT2FIX(active), INT2FIX(deleted)));
}
/*
* call-seq:
* TracePoint.stat -> obj
*
* Returns internal information of TracePoint.
*
* The contents of the returned value are implementation specific.
* It may be changed in future.
*
* This method is only for debugging TracePoint itself.
*/
static VALUE
tracepoint_stat_s(VALUE self)
tracepoint_stat_s(rb_execution_context_t *ec, VALUE self)
{
rb_vm_t *vm = GET_VM();
VALUE stat = rb_hash_new();
@ -1695,6 +1511,8 @@ tracepoint_stat_s(VALUE self)
return stat;
}
#include "load_trace_point.inc"
/* This function is called from inits.c */
void
Init_vm_trace(void)
@ -1704,94 +1522,10 @@ Init_vm_trace(void)
rb_define_method(rb_cThread, "set_trace_func", thread_set_trace_func_m, 1);
rb_define_method(rb_cThread, "add_trace_func", thread_add_trace_func_m, 1);
/*
* Document-class: TracePoint
*
* A class that provides the functionality of Kernel#set_trace_func in a
* nice Object-Oriented API.
*
* == Example
*
* We can use TracePoint to gather information specifically for exceptions:
*
* 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>]
*
* == Events
*
* If you don't specify the type of events you want to listen for,
* TracePoint will include all available events.
*
* *Note* do not depend on current event set, as this list is subject to
* change. Instead, it is recommended you specify the type of events you
* want to use.
*
* To filter what is traced, you can pass any of the following as +events+:
*
* +:line+:: execute code on a new line
* +:class+:: start a class or module definition
* +:end+:: finish a class or module definition
* +:call+:: call a Ruby method
* +:return+:: return from a Ruby method
* +:c_call+:: call a C-language routine
* +:c_return+:: return from a C-language routine
* +:raise+:: raise an exception
* +:b_call+:: event hook at block entry
* +:b_return+:: event hook at block ending
* +:thread_begin+:: event hook at thread beginning
* +:thread_end+:: event hook at thread ending
* +:fiber_switch+:: event hook at fiber switch
* +:script_compiled+:: new Ruby code compiled (with +eval+, +load+ or +require+)
*
*/
rb_cTracePoint = rb_define_class("TracePoint", rb_cObject);
rb_undef_alloc_func(rb_cTracePoint);
rb_define_singleton_method(rb_cTracePoint, "new", tracepoint_new_s, -1);
/*
* Document-method: trace
*
* call-seq:
* TracePoint.trace(*events) { |obj| block } -> obj
*
* A convenience method for TracePoint.new, that activates the trace
* automatically.
*
* trace = TracePoint.trace(:call) { |tp| [tp.lineno, tp.event] }
* #=> #<TracePoint:enabled>
*
* trace.enabled? #=> true
*/
rb_define_singleton_method(rb_cTracePoint, "trace", tracepoint_trace_s, -1);
rb_define_method(rb_cTracePoint, "__enable", tracepoint_enable_m, 3);
rb_define_method(rb_cTracePoint, "disable", tracepoint_disable_m, 0);
rb_define_method(rb_cTracePoint, "enabled?", rb_tracepoint_enabled_p, 0);
rb_define_method(rb_cTracePoint, "inspect", tracepoint_inspect, 0);
rb_define_method(rb_cTracePoint, "event", tracepoint_attr_event, 0);
rb_define_method(rb_cTracePoint, "lineno", tracepoint_attr_lineno, 0);
rb_define_method(rb_cTracePoint, "path", tracepoint_attr_path, 0);
rb_define_method(rb_cTracePoint, "parameters", tracepoint_attr_parameters, 0);
rb_define_method(rb_cTracePoint, "method_id", tracepoint_attr_method_id, 0);
rb_define_method(rb_cTracePoint, "callee_id", tracepoint_attr_callee_id, 0);
rb_define_method(rb_cTracePoint, "defined_class", tracepoint_attr_defined_class, 0);
rb_define_method(rb_cTracePoint, "binding", tracepoint_attr_binding, 0);
rb_define_method(rb_cTracePoint, "self", tracepoint_attr_self, 0);
rb_define_method(rb_cTracePoint, "return_value", tracepoint_attr_return_value, 0);
rb_define_method(rb_cTracePoint, "raised_exception", tracepoint_attr_raised_exception, 0);
rb_define_method(rb_cTracePoint, "eval_script", tracepoint_attr_eval_script, 0);
rb_define_method(rb_cTracePoint, "instruction_sequence", tracepoint_attr_instruction_sequence, 0);
rb_define_singleton_method(rb_cTracePoint, "stat", tracepoint_stat_s, 0);
load_trace_point();
}
typedef struct rb_postponed_job_struct {