lib/debug/session.rb in debug-1.7.1 vs lib/debug/session.rb in debug-1.7.2
- old
+ new
@@ -126,20 +126,20 @@
@scr_id_map = {} # for CDP
@obj_map = {} # { object_id => ... } for CDP
@tp_thread_begin = nil
+ @tp_thread_end = nil
+
@commands = {}
@unsafe_context = false
- has_keep_script_lines = RubyVM.respond_to? :keep_script_lines
+ @has_keep_script_lines = RubyVM.respond_to? :keep_script_lines
@tp_load_script = TracePoint.new(:script_compiled){|tp|
- if !has_keep_script_lines || bps_pending_until_load?
- eval_script = tp.eval_script unless has_keep_script_lines
- ThreadClient.current.on_load tp.instruction_sequence, eval_script
- end
+ eval_script = tp.eval_script unless @has_keep_script_lines
+ ThreadClient.current.on_load tp.instruction_sequence, eval_script
}
@tp_load_script.enable
@thread_stopper = thread_stopper
self.postmortem = CONFIG[:postmortem]
@@ -167,16 +167,21 @@
def activate ui = nil, on_fork: false
@ui = ui if ui
@tp_thread_begin&.disable
+ @tp_thread_end&.disable
@tp_thread_begin = nil
-
+ @tp_thread_end = nil
@ui.activate self, on_fork: on_fork
q = Queue.new
+ first_q = Queue.new
@session_server = Thread.new do
+ # make sure `@session_server` is assigned
+ first_q.pop; first_q = nil
+
Thread.current.name = 'DEBUGGER__::SESSION@server'
Thread.current.abort_on_exception = true
# Thread management
setup_threads
@@ -190,23 +195,30 @@
@tp_thread_begin = TracePoint.new(:thread_begin) do |tp|
get_thread_client
end
@tp_thread_begin.enable
+ @tp_thread_end = TracePoint.new(:thread_end) do |tp|
+ @th_clients.delete(Thread.current)
+ end
+ @tp_thread_end.enable
+
# session start
q << true
session_server_main
end
+ first_q << :ok
q.pop
end
def deactivate
get_thread_client.deactivate
@thread_stopper.disable
@tp_load_script.disable
@tp_thread_begin.disable
+ @tp_thread_end.disable
@bps.each_value{|bp| bp.disable}
@th_clients.each_value{|thc| thc.close}
@tracers.values.each{|t| t.disable}
@q_evt.close
@ui&.deactivate
@@ -217,15 +229,17 @@
@ui.deactivate
@ui = ui
# activate new ui
@tp_thread_begin.disable
+ @tp_thread_end.disable
@ui.activate self
if @ui.respond_to?(:reader_thread) && thc = get_thread_client(@ui.reader_thread)
thc.mark_as_management
end
@tp_thread_begin.enable
+ @tp_thread_end.enable
end
def pop_event
@q_evt.pop
end
@@ -327,21 +341,18 @@
obj_id = ev_args[1]
obj_inspect = ev_args[2]
opt = ev_args[3]
add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
else
- # ignore
+ stop_all_threads
end
wait_command_loop
- when :dap_result
- dap_event ev_args # server.rb
+ when :protocol_result
+ process_protocol_result ev_args
wait_command_loop
- when :cdp_result
- cdp_event ev_args
- wait_command_loop
end
end
def add_preset_commands name, cmds, kick: true, continue: true
cs = cmds.map{|c|
@@ -478,13 +489,13 @@
end
# * `u[ntil]`
# * Similar to `next` command, but only stop later lines or the end of the current frame.
# * Similar to gdb's `advance` command.
- # * `u[ntil] <[file:]line>
+ # * `u[ntil] <[file:]line>`
# * Run til the program reaches given location or the end of the current frame.
- # * `u[ntil] <name>
+ # * `u[ntil] <name>`
# * Run til the program invokes a method `<name>`. `<name>` can be a regexp with `/name/`.
register_command 'u', 'until',
repeat: true,
cancel_auto_continue: true,
postmortem: false do |arg|
@@ -887,39 +898,39 @@
### Evaluate
# * `p <expr>`
# * Evaluate like `p <expr>` on the current frame.
register_command 'p' do |arg|
- request_tc [:eval, :p, arg.to_s]
+ request_eval :p, arg.to_s
end
# * `pp <expr>`
# * Evaluate like `pp <expr>` on the current frame.
register_command 'pp' do |arg|
- request_tc [:eval, :pp, arg.to_s]
+ request_eval :pp, arg.to_s
end
# * `eval <expr>`
# * Evaluate `<expr>` on the current frame.
register_command 'eval', 'call' do |arg|
if arg == nil || arg.empty?
show_help 'eval'
@ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
:retry
else
- request_tc [:eval, :call, arg]
+ request_eval :call, arg
end
end
# * `irb`
# * Invoke `irb` on the current frame.
register_command 'irb' do |arg|
if @ui.remote?
@ui.puts "not supported on the remote console."
:retry
end
- request_tc [:eval, :irb]
+ request_eval :irb, nil
end
### Trace
# * `trace`
# * Show available tracers list.
@@ -1135,11 +1146,11 @@
cmd.block.call(cmd_arg)
else
@repl_prev_line = nil
check_unsafe
- request_tc [:eval, :pp, line]
+ request_eval :pp, line
end
rescue Interrupt
return :retry
rescue SystemExit
@@ -1151,10 +1162,15 @@
@ui.puts "[REPL ERROR] #{e.inspect}"
@ui.puts e.backtrace.map{|e| ' ' + e}
return :retry
end
+ def request_eval type, src
+ restart_all_threads
+ request_tc [:eval, type, src]
+ end
+
def step_command type, arg
if type == :until
leave_subsession [:step, type, arg]
return
end
@@ -1319,14 +1335,10 @@
end
end
# breakpoint management
- def bps_pending_until_load?
- @bps.any?{|key, bp| bp.pending_until_load?}
- end
-
def iterate_bps
deleted_bps = []
i = 0
@bps.each{|key, bp|
if !bp.deleted?
@@ -1498,11 +1510,11 @@
end
def clear_line_breakpoints path
path = resolve_path(path)
clear_breakpoints do |k, bp|
- bp.is_a?(LineBreakpoint) && DEBUGGER__.compare_path(k.first, path)
+ bp.is_a?(LineBreakpoint) && bp.path_is?(path)
end
rescue Errno::ENOENT
# just ignore
end
@@ -1522,11 +1534,11 @@
end
# tracers
def add_tracer tracer
- if @tracers.has_key? tracer.key
+ if @tracers[tracer.key]&.enabled?
tracer.disable
@ui.puts "Duplicated tracer: #{tracer}"
else
@tracers[tracer.key] = tracer
@ui.puts "Enable #{tracer}"
@@ -1722,29 +1734,30 @@
DEBUGGER__.info "Load #{iseq.absolute_path || iseq.path}"
file_path, reloaded = @sr.add(iseq, src)
@ui.event :load, file_path, reloaded
- pending_line_breakpoints = @bps.find_all do |key, bp|
- LineBreakpoint === bp && !bp.iseq
- end
-
- pending_line_breakpoints.each do |_key, bp|
- if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
- bp.try_activate iseq
- end
- end
-
- if reloaded
- @bps.find_all do |key, bp|
- LineBreakpoint === bp && DEBUGGER__.compare_path(bp.path, file_path)
+ # check breakpoints
+ if file_path
+ @bps.find_all do |_key, bp|
+ LineBreakpoint === bp && bp.path_is?(file_path)
end.each do |_key, bp|
- @bps.delete bp.key # to allow duplicate
- if nbp = LineBreakpoint.copy(bp, iseq)
- add_bp nbp
+ if !bp.iseq
+ bp.try_activate iseq
+ elsif reloaded
+ @bps.delete bp.key # to allow duplicate
+ if nbp = LineBreakpoint.copy(bp, iseq)
+ add_bp nbp
+ end
end
end
+ else # !file_path => file_path is not existing
+ @bps.find_all do |_key, bp|
+ LineBreakpoint === bp && !bp.iseq && DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
+ end.each do |_key, bp|
+ bp.try_activate iseq
+ end
end
end
def resolve_path file
File.realpath(File.expand_path(file))
@@ -2436,10 +2449,10 @@
end
end
end
module DaemonInterceptor
- def daemon
+ def daemon(*args)
return super unless defined?(SESSION) && SESSION.active?
_, child_hook = __fork_setup_for_debugger(:child)
unless SESSION.remote?