diff --git a/.document b/.document index 2116ca6d26..64c56be7ee 100644 --- a/.document +++ b/.document @@ -9,9 +9,10 @@ # prelude prelude.rb - rbconfig.rb +trace_point.rb + # the lib/ directory (which has its own .document file) lib diff --git a/common.mk b/common.mk index 42d3223e90..40cef70dda 100644 --- a/common.mk +++ b/common.mk @@ -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 diff --git a/inits.c b/inits.c index 11b5fa5a7e..8a9789d050 100644 --- a/inits.c +++ b/inits.c @@ -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 diff --git a/prelude.rb b/prelude.rb index 4e23aba36e..7cc2d260b6 100644 --- a/prelude.rb +++ b/prelude.rb @@ -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 # - # 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 diff --git a/test/ruby/test_settracefunc.rb b/test/ruby/test_settracefunc.rb index 11384273df..dba940b43e 100644 --- a/test/ruby/test_settracefunc.rb +++ b/test/ruby/test_settracefunc.rb @@ -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{} diff --git a/tool/test/testunit/test_parallel.rb b/tool/test/testunit/test_parallel.rb index ac4c924eba..24e1f89bcb 100644 --- a/tool/test/testunit/test_parallel.rb +++ b/tool/test/testunit/test_parallel.rb @@ -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]) diff --git a/trace_point.rb b/trace_point.rb new file mode 100644 index 0000000000..9be90506d3 --- /dev/null +++ b/trace_point.rb @@ -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 +# #=> # +# +# trace.enable +# #=> false +# +# 0 / 0 +# #=> [5, :raise, #] +# +# == 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 + # #=> # + # + # 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] } + # #=> # + # + # 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 # + # 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 + # + # Note: #defined_class returns singleton class. + # + # 6th block parameter of Kernel#set_trace_func passes original class + # of attached by singleton class. + # + # This is a difference between Kernel#set_trace_func and TracePoint. + # + # class C; def self.foo; end; end + # trace = TracePoint.new(:call) do |tp| + # p tp.defined_class #=> # + # 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 diff --git a/vm_trace.c b/vm_trace.c index eb1c2afa4e..1cb5fd0036 100644 --- a/vm_trace.c +++ b/vm_trace.c @@ -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 - * - * Note: #defined_class returns singleton class. - * - * 6th block parameter of Kernel#set_trace_func passes original class - * of attached by singleton class. - * - * This is a difference between Kernel#set_trace_func and TracePoint. - * - * class C; def self.foo; end; end - * trace = TracePoint.new(:call) do |tp| - * p tp.defined_class #=> # - * 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 - * #=> # - * - * 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 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 - * #=> # - * - * trace.enable - * #=> false - * - * 0 / 0 - * #=> [5, :raise, #] - * - * == 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] } - * #=> # - * - * 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 {