From 57d772df607a8f3b6c11cbba0917643fac3bb39f Mon Sep 17 00:00:00 2001 From: alpaca-tc Date: Thu, 23 May 2024 11:18:39 +0900 Subject: [PATCH] Support b_call/b_return Fixed a bug that caused a discrepancy between stack and caller when block is called. --- lib/diver_down/trace.rb | 6 +- lib/diver_down/trace/session.rb | 4 ++ lib/diver_down/trace/tracer.rb | 11 +++- spec/diver_down/trace/tracer_spec.rb | 87 ++++++++++++++++++++++++++++ spec/fixtures/tracer_with_block.rb | 15 +++++ spec/fixtures/tracer_with_block/b.rb | 7 +++ spec/fixtures/tracer_with_block/c.rb | 11 ++++ spec/fixtures/tracer_with_block/d.rb | 5 ++ 8 files changed, 140 insertions(+), 6 deletions(-) create mode 100644 spec/fixtures/tracer_with_block.rb create mode 100644 spec/fixtures/tracer_with_block/b.rb create mode 100644 spec/fixtures/tracer_with_block/c.rb create mode 100644 spec/fixtures/tracer_with_block/d.rb diff --git a/lib/diver_down/trace.rb b/lib/diver_down/trace.rb index 8a14378..f64ffa6 100644 --- a/lib/diver_down/trace.rb +++ b/lib/diver_down/trace.rb @@ -11,16 +11,12 @@ module Trace require 'diver_down/trace/redefine_ruby_methods' require 'diver_down/trace/ignored_method_ids' - @trace_events = %i[ - call c_call return c_return - ] - # Trace only Ruby-implemented methods because tracing C-implemented methods is very slow # Override Ruby only with the minimal set of methods needed to trace dependencies. # # @return [void] def self.trace_only_ruby_world!(map = DiverDown::Trace::RedefineRubyMethods::DEFAULT_METHODS) - DiverDown::Trace::Tracer.trace_events = %i[call return] + DiverDown::Trace::Tracer.trace_events = DiverDown::Trace::Tracer::DEFAULT_TRACE_EVENTS - %i[c_call c_return] DiverDown::Trace::RedefineRubyMethods.redefine_c_methods(map) end end diff --git a/lib/diver_down/trace/session.rb b/lib/diver_down/trace/session.rb index e03beae..03203c0 100644 --- a/lib/diver_down/trace/session.rb +++ b/lib/diver_down/trace/session.rb @@ -46,6 +46,10 @@ def build_trace_point next if TracePoint == tp.defined_class case tp.event + when :b_call + call_stack.push + when :b_return + call_stack.pop when :call, :c_call # puts "#{tp.method_id} #{tp.path}:#{tp.lineno}" if call_stack.ignored? diff --git a/lib/diver_down/trace/tracer.rb b/lib/diver_down/trace/tracer.rb index 0eb2fc4..10591ae 100644 --- a/lib/diver_down/trace/tracer.rb +++ b/lib/diver_down/trace/tracer.rb @@ -3,9 +3,18 @@ module DiverDown module Trace class Tracer + DEFAULT_TRACE_EVENTS = %i[ + call + return + c_call + c_return + b_call + b_return + ].freeze + # @return [Array] def self.trace_events - @trace_events || %i[call c_call return c_return] + @trace_events || DEFAULT_TRACE_EVENTS end # @param events [Array] diff --git a/spec/diver_down/trace/tracer_spec.rb b/spec/diver_down/trace/tracer_spec.rb index c1ab3fd..d4c7607 100644 --- a/spec/diver_down/trace/tracer_spec.rb +++ b/spec/diver_down/trace/tracer_spec.rb @@ -443,6 +443,93 @@ def trace_fixture(path, module_set: {}, caller_paths: nil, ignored_method_ids: [ )) end + it 'traces tracer_with_block.rb' do + wrapper = Class.new do + def self.wrap + yield + end + end + stub_const('Wrapper', wrapper) + + definition = wrapper.wrap do + trace_fixture( + 'tracer_with_block.rb', + caller_paths: [ + fixture_path('tracer_with_block.rb'), + ], + module_set: { + modules: [ + 'Wrapper', + 'AntipollutionModule::A', + 'AntipollutionModule::B', + 'AntipollutionModule::C', + 'AntipollutionModule::D', + ], + } + ) + end + + expect(definition.to_h).to match(fill_default( + title: 'title', + sources: [ + { + source_name: 'AntipollutionModule::A', + dependencies: [ + { + source_name: 'AntipollutionModule::B', + method_ids: [ + { + name: 'call_c', + context: 'class', + paths: [ + match(/tracer_with_block\.rb:\d+/), + ], + }, + ], + }, + ], + }, + { + source_name: 'AntipollutionModule::B', + dependencies: [ + { + source_name: 'AntipollutionModule::C', + method_ids: [ + { + name: 'call_d', + context: 'class', + paths: [ + match(/tracer_with_block\.rb:\d+/), + ], + }, + ], + }, + ], + }, + { + source_name: 'AntipollutionModule::C', + dependencies: [ + { + source_name: 'AntipollutionModule::D', + method_ids: [ + { + name: 'call_name', + context: 'class', + paths: [ + match(/tracer_with_block\.rb:\d+/), + ], + }, + ], + }, + ], + }, + { + source_name: 'AntipollutionModule::D', + }, + ] + )) + end + it 'traces tracer_subclass.rb' do definition = trace_fixture( 'tracer_subclass.rb', diff --git a/spec/fixtures/tracer_with_block.rb b/spec/fixtures/tracer_with_block.rb new file mode 100644 index 0000000..4648033 --- /dev/null +++ b/spec/fixtures/tracer_with_block.rb @@ -0,0 +1,15 @@ +load "#{__dir__}/tracer_with_block/b.rb" +load "#{__dir__}/tracer_with_block/c.rb" +load "#{__dir__}/tracer_with_block/d.rb" + +def run + A.call_b +end + +class A + def self.call_b + yield_self do + B.call_c + end + end +end diff --git a/spec/fixtures/tracer_with_block/b.rb b/spec/fixtures/tracer_with_block/b.rb new file mode 100644 index 0000000..2650af7 --- /dev/null +++ b/spec/fixtures/tracer_with_block/b.rb @@ -0,0 +1,7 @@ +class B + def self.call_c + yield_self do + C.call_d + end + end +end diff --git a/spec/fixtures/tracer_with_block/c.rb b/spec/fixtures/tracer_with_block/c.rb new file mode 100644 index 0000000..800c4a7 --- /dev/null +++ b/spec/fixtures/tracer_with_block/c.rb @@ -0,0 +1,11 @@ +class C + def self.call_d + yield_self do + yield_self do + yield_self do + D.call_name + end + end + end + end +end diff --git a/spec/fixtures/tracer_with_block/d.rb b/spec/fixtures/tracer_with_block/d.rb new file mode 100644 index 0000000..48f4113 --- /dev/null +++ b/spec/fixtures/tracer_with_block/d.rb @@ -0,0 +1,5 @@ +class D + def self.call_name + 'name' + end +end