1
0
Fork 0
mirror of https://github.com/ruby/ruby.git synced 2022-11-09 12:17:21 -05:00
ruby--ruby/test/lib/tracepointchecker.rb
ko1 23e452b17b cleanup hook cleanup code.
* vm_trace.c: before this patch, deleted hooks are remvoed at
  *the beggining* of hooks (exec_hooks_precheck).
  This patch cleanup deleted hooks at
  (1) just after hook is deleted (TracePoint#disable and so on)
  (2) just after executing hooks (exec_hooks_postcheck)
  Most of time (1) is enough, but if some threads running hooks,
  we need to wait cleaning up deleted hooks until threads finish
  running the hooks. This is why (2) is introduced (and this is
  why current impl cleanup deleted hooks at the beggining of hooks).

* test/lib/tracepointchecker.rb: check also the number of delete
  waiting hooks.

* cont.c (cont_restore_thread): fix VM->trace_running count.


git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@60782 b2dd03c8-39d4-4d8f-98ff-823fe69b080e
2017-11-16 02:47:58 +00:00

119 lines
2.7 KiB
Ruby

# frozen_string_literal: true
module TracePointChecker
STATE = {
count: 0,
running: false,
}
module ZombieTraceHunter
def before_setup
@tracepoint_captured_stat = TracePoint.stat.map{|k, (activated, deleted)| [k, activated, deleted]}
super
end
def after_teardown
super
# detect zombie traces.
assert_equal(
@tracepoint_captured_stat,
TracePoint.stat.map{|k, (activated, deleted)| [k, activated, deleted]},
"The number of active/deleted trace events was changed"
)
# puts "TracePoint - deleted: #{deleted}" if deleted > 0
TracePointChecker.check if STATE[:running]
end
end
MAIN_THREAD = Thread.current
TRACES = []
def self.prefix event
case event
when :call, :return
:n
when :c_call, :c_return
:c
when :b_call, :b_return
:b
end
end
def self.clear_call_stack
Thread.current[:call_stack] = []
end
def self.call_stack
stack = Thread.current[:call_stack]
stack = clear_call_stack unless stack
stack
end
def self.verbose_out label, method
puts label => call_stack, :count => STATE[:count], :method => method
end
def self.method_label tp
"#{prefix(tp.event)}##{tp.method_id}"
end
def self.start verbose: false, stop_at_failure: false
call_events = %i(a_call)
return_events = %i(a_return)
clear_call_stack
STATE[:running] = true
TRACES << TracePoint.new(*call_events){|tp|
next if Thread.current != MAIN_THREAD
method = method_label(tp)
call_stack.push method
STATE[:count] += 1
verbose_out :psuh, method if verbose
}
TRACES << TracePoint.new(*return_events){|tp|
next if Thread.current != MAIN_THREAD
STATE[:count] += 1
method = "#{prefix(tp.event)}##{tp.method_id}"
verbose_out :pop1, method if verbose
stored_method = call_stack.pop
next if stored_method.nil?
verbose_out :pop2, method if verbose
if stored_method != method
stop if stop_at_failure
RubyVM::SDR() if defined? RubyVM::SDR()
call_stack.clear
raise "#{stored_method} is expected, but #{method} (count: #{STATE[:count]})"
end
}
TRACES.each{|trace| trace.enable}
end
def self.stop
STATE[:running] = true
TRACES.each{|trace| trace.disable}
TRACES.clear
end
def self.check
TRACES.each{|trace|
raise "trace #{trace} should not be deactivated" unless trace.enabled?
}
end
end if defined?(TracePoint.stat)
class ::Test::Unit::TestCase
include TracePointChecker::ZombieTraceHunter
end if defined?(TracePointChecker)
# TracePointChecker.start verbose: false