From 9eb5e3ead4e0f3860097fcc93211aee1e06b9971 Mon Sep 17 00:00:00 2001 From: Koichi Sasada Date: Wed, 2 Nov 2022 02:55:44 +0900 Subject: [PATCH] Disable canceled step/next/finish When `next` command canceled with other breaks, for example, stop at a breakpoint while executing `next` command, the `next` command should be canceled and it should not stop on the next line. However, the current implementation left the TracePoint object for step commands, so it should be disabled. --- lib/debug/session.rb | 14 +++++-- lib/debug/thread_client.rb | 11 +++++- test/console/control_flow_commands_test.rb | 46 ++++++++++++++++++++++ 3 files changed, 66 insertions(+), 5 deletions(-) diff --git a/lib/debug/session.rb b/lib/debug/session.rb index a9358d7c0..d65e528b0 100644 --- a/lib/debug/session.rb +++ b/lib/debug/session.rb @@ -89,7 +89,7 @@ module DEBUGGER__ class PostmortemError < RuntimeError; end class Session - attr_reader :intercepted_sigint_cmd, :process_group + attr_reader :intercepted_sigint_cmd, :process_group, :subsession_id include Color @@ -116,6 +116,7 @@ def initialize @intercepted_sigint_cmd = 'DEFAULT' @process_group = ProcessGroup.new @subsession_stack = [] + @subsession_id = 0 @frame_map = {} # for DAP: {id => [threadId, frame_depth]} and CDP: {id => frame_depth} @var_map = {1 => [:globals], } # {id => ...} for DAP @@ -138,8 +139,14 @@ def active? !@q_evt.closed? end - def break_at? file, line - @bps.has_key? [file, line] + def stop_stepping? file, line, subsession_id + if @bps.has_key? [file, line] + true + elsif @subsession_id != subsession_id + true + else + false + end end def activate ui = nil, on_fork: false @@ -1571,6 +1578,7 @@ def get_thread_client th = Thread.current end private def enter_subsession + @subsession_id += 1 if !@subsession_stack.empty? DEBUGGER__.info "Enter subsession (nested #{@subsession_stack.size})" else diff --git a/lib/debug/thread_client.rb b/lib/debug/thread_client.rb index 206d32c1f..21885cd3d 100644 --- a/lib/debug/thread_client.rb +++ b/lib/debug/thread_client.rb @@ -328,10 +328,14 @@ def step_tp iter, events = [:line, :b_return, :return] @step_tp.disable if @step_tp thread = Thread.current + subsession_id = SESSION.subsession_id if SUPPORT_TARGET_THREAD @step_tp = TracePoint.new(*events){|tp| - next if SESSION.break_at? tp.path, tp.lineno + if SESSION.stop_stepping? tp.path, tp.lineno, subsession_id + tp.disable + next + end next if !yield(tp.event) next if tp.path.start_with?(__dir__) next if tp.path.start_with?('') @@ -347,7 +351,10 @@ def step_tp iter, events = [:line, :b_return, :return] else @step_tp = TracePoint.new(*events){|tp| next if thread != Thread.current - next if SESSION.break_at? tp.path, tp.lineno + if SESSION.stop_stepping? tp.path, tp.lineno, subsession_id + tp.disable + next + end next if !yield(tp.event) next if tp.path.start_with?(__dir__) next if tp.path.start_with?('') diff --git a/test/console/control_flow_commands_test.rb b/test/console/control_flow_commands_test.rb index 739d910ff..12a3ae3e9 100644 --- a/test/console/control_flow_commands_test.rb +++ b/test/console/control_flow_commands_test.rb @@ -358,4 +358,50 @@ def test_next_steps_over_rescue_when_raising_from_method end end end + + class CancelStepTest < ConsoleTestCase + def program + <<~RUBY + 1| def foo m + 2| __send__ m + 3| end + 4| def bar + 5| a = :bar1 + 6| b = :bar2 + 7| c = :bar3 + 8| end + 9| + 10| def baz + 11| :baz + 12| end + 13| foo :bar + 14| foo :baz + 15| foo :baz + RUBY + end + + def test_next_should_be_canceled + debug_code program do + type 'b 13' + type 'b Object#bar' + type 'c' + assert_line_num 13 + type 'n' + assert_line_num 5 + type 'c' + end + end + + def test_finish_should_be_canceled + debug_code program do + type 'b 5' + type 'b 6' + type 'c' + assert_line_num 5 + type 'finish' + assert_line_num 6 + type 'c' + end + end + end end