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?