mirror of
https://github.com/ruby/ruby.git
synced 2022-11-09 12:17:21 -05:00

* 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
119 lines
2.7 KiB
Ruby
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
|