Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions lib/debug/server_cdp.rb
Original file line number Diff line number Diff line change
Expand Up @@ -781,7 +781,7 @@ def process_cdp args
exception = nil
result = {
reason: 'other',
callFrames: @target_frames.map.with_index{|frame, i|
callFrames: target_frames.map.with_index{|frame, i|
exception = frame.raised_exception if frame == current_frame && frame.has_raised_exception

path = frame.realpath || frame.path
Expand Down Expand Up @@ -842,7 +842,7 @@ def process_cdp args
when :evaluate
res = {}
fid, expr, group = args
frame = @target_frames[fid]
frame = target_frames[fid]
message = nil

if frame && (b = frame.binding)
Expand Down Expand Up @@ -933,7 +933,7 @@ def process_cdp args
event! :cdp_result, :evaluate, req, message: message, response: res, output: output
when :scope
fid = args.shift
frame = @target_frames[fid]
frame = target_frames[fid]
if b = frame.binding
vars = b.local_variables.map{|name|
v = b.local_variable_get(name)
Expand Down
10 changes: 5 additions & 5 deletions lib/debug/server_dap.rb
Original file line number Diff line number Diff line change
Expand Up @@ -593,7 +593,7 @@ def process_dap args
case type
when :backtrace
event! :dap_result, :backtrace, req, {
stackFrames: @target_frames.map{|frame|
stackFrames: target_frames.map{|frame|
path = frame.realpath || frame.path
ref = frame.file_lines unless path && File.exist?(path)

Expand All @@ -612,7 +612,7 @@ def process_dap args
}
when :scopes
fid = args.shift
frame = @target_frames[fid]
frame = target_frames[fid]

lnum =
if frame.binding
Expand Down Expand Up @@ -640,7 +640,7 @@ def process_dap args
}]
when :scope
fid = args.shift
frame = @target_frames[fid]
frame = target_frames[fid]
if b = frame.binding
vars = b.local_variables.map{|name|
v = b.local_variable_get(name)
Expand Down Expand Up @@ -712,7 +712,7 @@ def process_dap args

when :evaluate
fid, expr, context = args
frame = @target_frames[fid]
frame = target_frames[fid]
message = nil

if frame && (b = frame.binding)
Expand Down Expand Up @@ -774,7 +774,7 @@ def process_dap args

when :completions
fid, text = args
frame = @target_frames[fid]
frame = target_frames[fid]

if (b = frame&.binding) && word = text&.split(/[\s\{]/)&.last
words = IRB::InputCompletor::retrieve_completion_data(word, bind: b).compact
Expand Down
82 changes: 55 additions & 27 deletions lib/debug/thread_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,8 @@ def initialize id, q_evt, q_cmd, thr = Thread.current
@is_management = false
@id = id
@thread = thr
@target_frames = nil
@frame_index_stack = []
@frames_stack = []
@q_evt = q_evt
@q_cmd = q_cmd
@step_tp = nil
Expand All @@ -101,6 +102,30 @@ def initialize id, q_evt, q_cmd, thr = Thread.current
::DEBUGGER__.info("Thread \##{@id} is created.")
end

def target_frames
@frames_stack.last
end

def set_target_frames(frames)
@frames_stack << frames
end

def pop_target_frames
@frames_stack.pop
end

def current_frame_index
@frame_index_stack.last
end

def set_current_frame_index(i)
@frame_index_stack << i
end

def pop_current_frame_index
@frame_index_stack.pop
end

def deactivate
@step_tp.disable if @step_tp
end
Expand Down Expand Up @@ -233,19 +258,19 @@ def on_pause
def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_frames: nil, postmortem_exc: nil
return if management?

@current_frame_index = 0
set_current_frame_index(0)

case
when postmortem_frames
@target_frames = postmortem_frames
set_target_frames(postmortem_frames)
@postmortem = true
when replay_frames
@target_frames = replay_frames
set_target_frames(replay_frames)
else
@target_frames = DEBUGGER__.capture_frames(__dir__)
set_target_frames(DEBUGGER__.capture_frames(__dir__))
end

cf = @target_frames.first
cf = target_frames.first
if cf
case event
when :return, :b_return, :c_return
Expand Down Expand Up @@ -282,6 +307,9 @@ def suspend event, tp = nil, bp: nil, sig: nil, postmortem_frames: nil, replay_f
end

wait_next_action
ensure
pop_target_frames
pop_current_frame_index
end

def replay_suspend
Expand Down Expand Up @@ -407,13 +435,13 @@ def frame_eval src, re_raise: false
raise if re_raise
end

def show_src(frame_index: @current_frame_index,
def show_src(frame_index: current_frame_index,
update_line: false,
max_lines: CONFIG[:show_src_lines] || 10,
start_line: nil,
end_line: nil,
dir: +1)
if @target_frames && frame = @target_frames[frame_index]
if target_frames && frame = target_frames[frame_index]
if file_lines = frame.file_lines
frame_line = frame.location.lineno - 1

Expand Down Expand Up @@ -459,8 +487,8 @@ def show_src(frame_index: @current_frame_index,
end

def current_frame
if @target_frames
@target_frames[@current_frame_index]
if target_frames
target_frames[current_frame_index]
else
nil
end
Expand Down Expand Up @@ -600,9 +628,9 @@ def show_by_editor path = nil
### cmd: show frames

def show_frames max = nil, pattern = nil
if @target_frames && (max ||= @target_frames.size) > 0
if target_frames && (max ||= target_frames.size) > 0
frames = []
@target_frames.each_with_index{|f, i|
target_frames.each_with_index{|f, i|
next if pattern && !(f.name.match?(pattern) || f.location_str.match?(pattern))
next if CONFIG[:skip_path] && CONFIG[:skip_path].any?{|pat|
case pat
Expand Down Expand Up @@ -630,8 +658,8 @@ def show_frame i=0
puts frame_str(i)
end

def frame_str(i, frame: @target_frames[i])
cur_str = (@current_frame_index == i ? '=>' : ' ')
def frame_str(i, frame: target_frames[i])
cur_str = (current_frame_index == i ? '=>' : ' ')
prefix = "#{cur_str}##{i}"
frame_string = @frame_formatter.call(frame)
"#{prefix}\t#{frame_string}"
Expand Down Expand Up @@ -752,7 +780,7 @@ def wait_next_action_
end

when :next
frame = @target_frames.first
frame = target_frames.first
path = frame.location.absolute_path || "!eval:#{frame.path}"
line = frame.location.lineno

Expand All @@ -764,7 +792,7 @@ def wait_next_action_
end
end

depth = @target_frames.first.frame_depth
depth = target_frames.first.frame_depth

step_tp iter do
loc = caller_locations(2, 1).first
Expand All @@ -782,7 +810,7 @@ def wait_next_action_

when :finish
finish_frames = (iter || 1) - 1
goal_depth = @target_frames.first.frame_depth - finish_frames
goal_depth = target_frames.first.frame_depth - finish_frames

step_tp nil, [:return, :b_return] do
DEBUGGER__.frame_depth - 3 <= goal_depth ? true : false
Expand All @@ -792,7 +820,7 @@ def wait_next_action_
when :back
if @recorder&.can_step_back?
unless @recorder.backup_frames
@recorder.backup_frames = @target_frames
@recorder.backup_frames = target_frames
end
@recorder.step_back
raise SuspendReplay
Expand Down Expand Up @@ -859,28 +887,28 @@ def wait_next_action_
type, arg = *args
case type
when :up
if @current_frame_index + 1 < @target_frames.size
@current_frame_index += 1
if current_frame_index + 1 < target_frames.size
set_current_frame_index(current_frame_index + 1)
show_src max_lines: 1
show_frame(@current_frame_index)
show_frame(current_frame_index)
end
when :down
if @current_frame_index > 0
@current_frame_index -= 1
if current_frame_index > 0
set_current_frame_index(current_frame_index - 1)
show_src max_lines: 1
show_frame(@current_frame_index)
show_frame(current_frame_index)
end
when :set
if arg
index = arg.to_i
if index >= 0 && index < @target_frames.size
@current_frame_index = index
if index >= 0 && index < target_frames.size
set_current_frame_index(index)
else
puts "out of frame index: #{index}"
end
end
show_src max_lines: 1
show_frame(@current_frame_index)
show_frame(current_frame_index)
else
raise "unsupported frame operation: #{arg.inspect}"
end
Expand Down
61 changes: 61 additions & 0 deletions test/debug/break_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -722,4 +722,65 @@ def test_the_path_option_supersede_skip_path_config
end
end
end

class NestedBreakTest < TestCase
def program
<<~RUBY
1| class Foo
2| def self.bar(num)
3| num
4| end
5| end
6|
7| a = 10
8| binding.b
RUBY
end

if RUBY_VERSION >= "3.1.0"
def test_method_breakpoint_can_be_triggered_inside_another_breakpoint
debug_code(program) do
type "break Foo.bar"
type "c"

# stops at the first breakpoint
assert_line_num(8)

# enters the second breakpoint
type "Foo.bar(a)"
assert_line_num(3)
type "num + 10"
assert_line_text(/20/)
type "c"

# returns to the first breakpoint
assert_line_num(8)
type "a + 100"
assert_line_text(/110/)

type "c"
end
end
else
def test_nested_breakpoint_will_be_ignored
debug_code(program) do
type "break Foo.bar"
type "c"

# stops at the first breakpoint
assert_line_num(8)

# doesn't enter the another subsession
type "Foo.bar(a)"

# returns to the first breakpoint
assert_line_num(8)
type "a + 100"
assert_line_text(/110/)

type "c"
end
end
end
end
end