Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow reentrant breakpoint on Ruby 3.1 #558

Merged
merged 1 commit into from
Mar 11, 2022
Merged
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
2 changes: 1 addition & 1 deletion lib/debug/breakpoint.rb
Original file line number Diff line number Diff line change
Expand Up @@ -327,6 +327,7 @@ def initialize expr, path

def setup
@tp = TracePoint.new(:line){|tp|
next if SESSION.in_subsession? # TODO: Ractor support
next if ThreadClient.current.management?
next if skip_path?(tp.path)

Expand Down Expand Up @@ -375,7 +376,6 @@ def watch_eval(tp)

def setup
@tp = TracePoint.new(:line, :return, :b_return){|tp|

watch_eval(tp)
}
end
Expand Down
38 changes: 25 additions & 13 deletions lib/debug/session.rb
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ def initialize ui
@intercept_trap_sigint = false
@intercepted_sigint_cmd = 'DEFAULT'
@process_group = ProcessGroup.new
@subsession = nil
@subsession_stack = []

@frame_map = {} # for DAP: {id => [threadId, frame_depth]} and CDP: {id => frame_depth}
@var_map = {1 => [:globals], } # {id => ...} for DAP
Expand Down Expand Up @@ -216,6 +216,7 @@ def process_event evt
q << true

when :init
enter_subsession
wait_command_loop tc

when :load
Expand Down Expand Up @@ -258,7 +259,7 @@ def process_event evt
end

when :result
raise "[BUG] not in subsession" unless @subsession
raise "[BUG] not in subsession" if @subsession_stack.empty?

case ev_args.first
when :try_display
Expand Down Expand Up @@ -1540,27 +1541,38 @@ def get_thread_client th = Thread.current
end

private def enter_subsession
raise "already in subsession" if @subsession
@subsession = true
stop_all_threads
@process_group.lock
DEBUGGER__.info "enter_subsession"
if !@subsession_stack.empty?
DEBUGGER__.info "Enter subsession (nested #{@subsession_stack.size})"
else
DEBUGGER__.info "Enter subsession"
stop_all_threads
@process_group.lock
end

@subsession_stack << true
end

private def leave_subsession type
DEBUGGER__.info "leave_subsession"
@process_group.unlock
restart_all_threads
raise '[BUG] leave_subsession: not entered' if @subsession_stack.empty?
@subsession_stack.pop

if @subsession_stack.empty?
DEBUGGER__.info "Leave subsession"
@process_group.unlock
restart_all_threads
else
DEBUGGER__.info "Leave subsession (nested #{@subsession_stack.size})"
end

@tc << type if type
@tc = nil
@subsession = false
rescue Exception => e
STDERR.puts [e, e.backtrace].inspect
STDERR.puts PP.pp([e, e.backtrace], ''.dup)
raise
end

def in_subsession?
@subsession
!@subsession_stack.empty?
end

## event
Expand Down
47 changes: 37 additions & 10 deletions lib/debug/thread_client.rb
Original file line number Diff line number Diff line change
Expand Up @@ -346,9 +346,39 @@ def step_tp iter, events = [:line, :b_return, :return]

## cmd helpers

# this method is extracted to hide frame_eval's local variables from C method eval's binding
def instance_eval_for_cmethod frame_self, src
frame_self.instance_eval(src)
if TracePoint.respond_to? :allow_reentry
def tp_allow_reentry
TracePoint.allow_reentry do
yield
end
rescue RuntimeError => e
# on the postmortem mode, it is not stopped in TracePoint
if e.message == 'No need to allow reentrance.'
yield
else
raise
end
end
else
def tp_allow_reentry
yield
end
end

def frame_eval_core src, b
if b
f, _l = b.source_location

tp_allow_reentry do
b.eval(src, "(rdbg)/#{f}")
end
else
frame_self = current_frame.self

tp_allow_reentry do
frame_self.instance_eval(src)
end
end
end

SPECIAL_LOCAL_VARS = [
Expand All @@ -365,16 +395,13 @@ def frame_eval src, re_raise: false
b.local_variable_set(name, var) if /\%/ !~ name
end

result = if b
f, _l = b.source_location
b.eval(src, "(rdbg)/#{f}")
else
frame_self = current_frame.self
instance_eval_for_cmethod(frame_self, src)
end
result = frame_eval_core(src, b)

@success_last_eval = true
result

rescue SystemExit
raise
rescue Exception => e
return yield(e) if block_given?

Expand Down