debug 1.0.0.beta5 → 1.0.0.beta6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +193 -2
- data/README.md +20 -12
- data/bin/gentest +22 -0
- data/exe/rdbg +9 -13
- data/lib/debug.rb +3 -0
- data/lib/debug/breakpoint.rb +11 -0
- data/lib/debug/client.rb +5 -7
- data/lib/debug/color.rb +5 -3
- data/lib/debug/config.rb +25 -13
- data/lib/debug/console.rb +13 -0
- data/lib/debug/frame_info.rb +2 -1
- data/lib/debug/open.rb +1 -0
- data/lib/debug/run.rb +3 -2
- data/lib/debug/server.rb +25 -16
- data/lib/debug/server_dap.rb +4 -2
- data/lib/debug/session.rb +140 -64
- data/lib/debug/source_repository.rb +2 -0
- data/lib/debug/thread_client.rb +43 -31
- data/lib/debug/version.rb +3 -1
- data/misc/README.md.erb +13 -9
- metadata +3 -3
- data/lib/debug/test_console.rb +0 -0
    
        data/lib/debug/config.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 |  | 
| 2 3 | 
             
            module DEBUGGER__
         | 
| 3 4 | 
             
              def self.unix_domain_socket_dir
         | 
| @@ -34,15 +35,18 @@ module DEBUGGER__ | |
| 34 35 | 
             
                nonstop:     'RUBY_DEBUG_NONSTOP',     # Nonstop mode ('1' is nonstop)
         | 
| 35 36 | 
             
                init_script: 'RUBY_DEBUG_INIT_SCRIPT', # debug command script path loaded at first stop
         | 
| 36 37 | 
             
                commands:    'RUBY_DEBUG_COMMANDS',    # debug commands invoked at first stop. commands should be separated by ';;'
         | 
| 38 | 
            +
                no_rc:       'RUBY_DEBUG_NO_RC',       # ignore loading ~/.rdbgrc(.rb)
         | 
| 37 39 |  | 
| 38 40 | 
             
                # UI setting
         | 
| 39 41 | 
             
                show_src_lines: 'RUBY_DEBUG_SHOW_SRC_LINES', # Show n lines source code on breakpoint (default: 10 lines).
         | 
| 40 42 | 
             
                show_frames:    'RUBY_DEBUG_SHOW_FRAMES',    # Show n frames on breakpoint (default: 2 frames).
         | 
| 41 43 | 
             
                use_short_path: 'RUBY_DEBUG_USE_SHORT_PATH', # Show shoten PATH (like $(Gem)/foo.rb).
         | 
| 42 44 | 
             
                skip_nosrc:     'RUBY_DEBUG_SKIP_NOSRC',     # Skip on no source code lines (default: false).
         | 
| 43 | 
            -
                 | 
| 45 | 
            +
                no_color:       'RUBY_DEBUG_NO_COLOR',       # Do not use colorize
         | 
| 46 | 
            +
                no_sigint_hook: 'RUBY_DEBUG_NO_SIGINT_HOOK', # Do not suspend on SIGINT
         | 
| 47 | 
            +
                quiet:          'RUBY_DEBUG_QUIET',          # Do not show messages
         | 
| 44 48 |  | 
| 45 | 
            -
                # remote
         | 
| 49 | 
            +
                # remote setting
         | 
| 46 50 | 
             
                port:        'RUBY_DEBUG_PORT',        # TCP/IP remote debugging: port
         | 
| 47 51 | 
             
                host:        'RUBY_DEBUG_HOST',        # TCP/IP remote debugging: host (localhost if not given)
         | 
| 48 52 | 
             
                sock_path:   'RUBY_DEBUG_SOCK_PATH',   # UNIX Domain Socket remote debugging: socket path
         | 
| @@ -50,16 +54,15 @@ module DEBUGGER__ | |
| 50 54 | 
             
                cookie:      'RUBY_DEBUG_COOKIE',      # Cookie for negotiation
         | 
| 51 55 | 
             
              }.freeze
         | 
| 52 56 |  | 
| 53 | 
            -
              def self. | 
| 54 | 
            -
                CONFIG_MAP. | 
| 55 | 
            -
                   | 
| 57 | 
            +
              def self.config_to_env_hash config
         | 
| 58 | 
            +
                CONFIG_MAP.each_with_object({}){|(key, evname), env|
         | 
| 59 | 
            +
                  env[evname] = config[key].to_s if config[key]
         | 
| 56 60 | 
             
                }
         | 
| 57 61 | 
             
              end
         | 
| 58 62 |  | 
| 59 63 | 
             
              def self.parse_argv argv
         | 
| 60 64 | 
             
                config = {
         | 
| 61 65 | 
             
                  mode: :start,
         | 
| 62 | 
            -
                  use_colorize: true,
         | 
| 63 66 | 
             
                }
         | 
| 64 67 | 
             
                CONFIG_MAP.each{|key, evname|
         | 
| 65 68 | 
             
                  if val = ENV[evname]
         | 
| @@ -90,14 +93,26 @@ module DEBUGGER__ | |
| 90 93 | 
             
                    config[:nonstop] = '1'
         | 
| 91 94 | 
             
                  end
         | 
| 92 95 |  | 
| 93 | 
            -
                  o.on('-e COMMAND', ' | 
| 96 | 
            +
                  o.on('-e COMMAND', 'Execute debug command at the beginning of the script.') do |cmd|
         | 
| 94 97 | 
             
                    config[:commands] ||= ''
         | 
| 95 | 
            -
                    config[:commands]  | 
| 98 | 
            +
                    config[:commands] += cmd + ';;'
         | 
| 96 99 | 
             
                  end
         | 
| 97 100 |  | 
| 98 | 
            -
                  o.on('-x FILE', '--init-script=FILE', ' | 
| 101 | 
            +
                  o.on('-x FILE', '--init-script=FILE', 'Execute debug command in the FILE.') do |file|
         | 
| 99 102 | 
             
                    config[:init_script] = file
         | 
| 100 103 | 
             
                  end
         | 
| 104 | 
            +
                  o.on('--no-rc', 'Ignore ~/.rdbgrc') do
         | 
| 105 | 
            +
                    config[:no_rc] = true
         | 
| 106 | 
            +
                  end
         | 
| 107 | 
            +
                  o.on('--no-color', 'Disable colorize') do
         | 
| 108 | 
            +
                    config[:no_color] = true
         | 
| 109 | 
            +
                  end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                  o.on('-c', '--command', 'Enable command mode.',
         | 
| 112 | 
            +
                                          'The first argument should be a command name in $PATH.',
         | 
| 113 | 
            +
                                          'Example: \'rdbg -c bundle exec rake test\'') do
         | 
| 114 | 
            +
                    config[:command] = true
         | 
| 115 | 
            +
                  end
         | 
| 101 116 |  | 
| 102 117 | 
             
                  o.separator ''
         | 
| 103 118 |  | 
| @@ -154,11 +169,8 @@ module DEBUGGER__ | |
| 154 169 | 
             
                    exit
         | 
| 155 170 | 
             
                  end
         | 
| 156 171 |  | 
| 157 | 
            -
                  o.on('-c', '--command', 'Command mode (first argument is command name)') do
         | 
| 158 | 
            -
                    config[:command] = true
         | 
| 159 | 
            -
                  end
         | 
| 160 | 
            -
             | 
| 161 172 | 
             
                  o.on('--util=NAME', 'Utility mode (used by tools)') do |name|
         | 
| 173 | 
            +
                    require_relative 'client'
         | 
| 162 174 | 
             
                    Client.new(name)
         | 
| 163 175 | 
             
                    exit
         | 
| 164 176 | 
             
                  end
         | 
    
        data/lib/debug/console.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require_relative 'session'
         | 
| 2 4 | 
             
            return unless defined?(DEBUGGER__)
         | 
| 3 5 |  | 
| @@ -6,6 +8,17 @@ require 'io/console/size' | |
| 6 8 | 
             
            module DEBUGGER__
         | 
| 7 9 | 
             
              class UI_Console < UI_Base
         | 
| 8 10 | 
             
                def initialize
         | 
| 11 | 
            +
                  unless CONFIG[:no_sigint_hook]
         | 
| 12 | 
            +
                    @prev_handler = trap(:SIGINT){
         | 
| 13 | 
            +
                      ThreadClient.current.on_trap :SIGINT
         | 
| 14 | 
            +
                    }
         | 
| 15 | 
            +
                  end
         | 
| 16 | 
            +
                end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                def close
         | 
| 19 | 
            +
                  if @prev_handler
         | 
| 20 | 
            +
                    trap(:SIGINT, @prev_handler)
         | 
| 21 | 
            +
                  end
         | 
| 9 22 | 
             
                end
         | 
| 10 23 |  | 
| 11 24 | 
             
                def remote?
         | 
    
        data/lib/debug/frame_info.rb
    CHANGED
    
    | @@ -1,3 +1,4 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 1 2 |  | 
| 2 3 | 
             
            module DEBUGGER__
         | 
| 3 4 | 
             
              FrameInfo = Struct.new(:location, :self, :binding, :iseq, :class, :frame_depth,
         | 
| @@ -9,7 +10,7 @@ module DEBUGGER__ | |
| 9 10 | 
             
              if File.exist? File.join(__dir__, 'debug.so')
         | 
| 10 11 | 
             
                require_relative 'debug.so'
         | 
| 11 12 | 
             
              else
         | 
| 12 | 
            -
                 | 
| 13 | 
            +
                require_relative 'debug'
         | 
| 13 14 | 
             
              end
         | 
| 14 15 |  | 
| 15 16 | 
             
              class FrameInfo
         | 
    
        data/lib/debug/open.rb
    CHANGED
    
    
    
        data/lib/debug/run.rb
    CHANGED
    
    
    
        data/lib/debug/server.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'socket'
         | 
| 2 4 |  | 
| 3 5 | 
             
            require_relative 'session'
         | 
| @@ -13,8 +15,8 @@ module DEBUGGER__ | |
| 13 15 | 
             
                  @accept_m = Mutex.new
         | 
| 14 16 | 
             
                  @accept_cv = ConditionVariable.new
         | 
| 15 17 | 
             
                  @client_addr = nil
         | 
| 16 | 
            -
                  @q_msg =  | 
| 17 | 
            -
                  @q_ans =  | 
| 18 | 
            +
                  @q_msg = nil
         | 
| 19 | 
            +
                  @q_ans = nil
         | 
| 18 20 | 
             
                  @unsent_messages = []
         | 
| 19 21 | 
             
                  @width = 80
         | 
| 20 22 |  | 
| @@ -23,7 +25,7 @@ module DEBUGGER__ | |
| 23 25 | 
             
                    Thread.current.abort_on_exception = true
         | 
| 24 26 |  | 
| 25 27 | 
             
                    accept do |server|
         | 
| 26 | 
            -
                      DEBUGGER__. | 
| 28 | 
            +
                      DEBUGGER__.warn "Connected."
         | 
| 27 29 |  | 
| 28 30 | 
             
                      @accept_m.synchronize{
         | 
| 29 31 | 
             
                        @sock = server
         | 
| @@ -36,6 +38,9 @@ module DEBUGGER__ | |
| 36 38 | 
             
                          @sock.puts m
         | 
| 37 39 | 
             
                        }
         | 
| 38 40 | 
             
                        @unsent_messages.clear
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                        @q_msg = Queue.new
         | 
| 43 | 
            +
                        @q_ans = Queue.new
         | 
| 39 44 | 
             
                      }
         | 
| 40 45 |  | 
| 41 46 | 
             
                      setup_interrupt do
         | 
| @@ -43,12 +48,14 @@ module DEBUGGER__ | |
| 43 48 | 
             
                      end
         | 
| 44 49 |  | 
| 45 50 | 
             
                    rescue => e
         | 
| 46 | 
            -
                      DEBUGGER__. | 
| 51 | 
            +
                      DEBUGGER__.warn "ReaderThreadError: #{e}", :error
         | 
| 47 52 | 
             
                    ensure
         | 
| 48 | 
            -
                      DEBUGGER__. | 
| 53 | 
            +
                      DEBUGGER__.warn "Disconnected."
         | 
| 49 54 | 
             
                      @sock = nil
         | 
| 50 55 | 
             
                      @q_msg.close
         | 
| 56 | 
            +
                      @q_msg = nil
         | 
| 51 57 | 
             
                      @q_ans.close
         | 
| 58 | 
            +
                      @q_ans = nil
         | 
| 52 59 | 
             
                    end
         | 
| 53 60 | 
             
                  end
         | 
| 54 61 | 
             
                end
         | 
| @@ -81,9 +88,6 @@ module DEBUGGER__ | |
| 81 88 | 
             
                end
         | 
| 82 89 |  | 
| 83 90 | 
             
                def process
         | 
| 84 | 
            -
                  @q_msg = Queue.new
         | 
| 85 | 
            -
                  @q_ans = Queue.new
         | 
| 86 | 
            -
             | 
| 87 91 | 
             
                  pause
         | 
| 88 92 |  | 
| 89 93 | 
             
                  while line = @sock.gets
         | 
| @@ -141,12 +145,20 @@ module DEBUGGER__ | |
| 141 145 | 
             
                  if s = @sock         # already connection
         | 
| 142 146 | 
             
                    # ok
         | 
| 143 147 | 
             
                  elsif skip == true   # skip process
         | 
| 144 | 
            -
                     | 
| 148 | 
            +
                    no_sock = true
         | 
| 149 | 
            +
                    r = @accept_m.synchronize do
         | 
| 150 | 
            +
                      if @sock
         | 
| 151 | 
            +
                        no_sock = false
         | 
| 152 | 
            +
                      else
         | 
| 153 | 
            +
                        yield nil
         | 
| 154 | 
            +
                      end
         | 
| 155 | 
            +
                    end
         | 
| 156 | 
            +
                    return r if no_sock
         | 
| 145 157 | 
             
                  else                 # wait for connection
         | 
| 146 158 | 
             
                    until s = @sock
         | 
| 147 159 | 
             
                      @accept_m.synchronize{
         | 
| 148 160 | 
             
                        unless @sock
         | 
| 149 | 
            -
                          DEBUGGER__. | 
| 161 | 
            +
                          DEBUGGER__.warn "wait for debuger connection..."
         | 
| 150 162 | 
             
                          @accept_cv.wait(@accept_m)
         | 
| 151 163 | 
             
                        end
         | 
| 152 164 | 
             
                      }
         | 
| @@ -190,6 +202,7 @@ module DEBUGGER__ | |
| 190 202 | 
             
                def readline
         | 
| 191 203 | 
             
                  (sock do |s|
         | 
| 192 204 | 
             
                    s.puts "input"
         | 
| 205 | 
            +
                    sleep 0.01 until @q_msg
         | 
| 193 206 | 
             
                    @q_msg.pop
         | 
| 194 207 | 
             
                  end || 'continue').strip
         | 
| 195 208 | 
             
                end
         | 
| @@ -224,7 +237,7 @@ module DEBUGGER__ | |
| 224 237 |  | 
| 225 238 | 
             
                def accept
         | 
| 226 239 | 
             
                  Socket.tcp_server_sockets @host, @port do |socks|
         | 
| 227 | 
            -
                    ::DEBUGGER__. | 
| 240 | 
            +
                    ::DEBUGGER__.warn "Debugger can attach via TCP/IP (#{socks.map{|e| e.local_address.inspect}})"
         | 
| 228 241 | 
             
                    Socket.accept_loop(socks) do |sock, client|
         | 
| 229 242 | 
             
                      @client_addr = client
         | 
| 230 243 | 
             
                      yield sock
         | 
| @@ -254,7 +267,7 @@ module DEBUGGER__ | |
| 254 267 | 
             
                    @sock_path = DEBUGGER__.create_unix_domain_socket_name(@sock_dir)
         | 
| 255 268 | 
             
                  end
         | 
| 256 269 |  | 
| 257 | 
            -
                  ::DEBUGGER__. | 
| 270 | 
            +
                  ::DEBUGGER__.warn "Debugger can attach via UNIX domain socket (#{@sock_path})"
         | 
| 258 271 | 
             
                  Socket.unix_server_loop @sock_path do |sock, client|
         | 
| 259 272 | 
             
                    @client_addr = client
         | 
| 260 273 | 
             
                    yield sock
         | 
| @@ -263,8 +276,4 @@ module DEBUGGER__ | |
| 263 276 | 
             
                  end
         | 
| 264 277 | 
             
                end
         | 
| 265 278 | 
             
              end
         | 
| 266 | 
            -
             | 
| 267 | 
            -
              def self.message msg
         | 
| 268 | 
            -
                $stderr.puts "DEBUGGER: #{msg}"
         | 
| 269 | 
            -
              end
         | 
| 270 279 | 
             
            end
         | 
    
        data/lib/debug/server_dap.rb
    CHANGED
    
    | @@ -1,3 +1,5 @@ | |
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 1 3 | 
             
            require 'json'
         | 
| 2 4 |  | 
| 3 5 | 
             
            module DEBUGGER__
         | 
| @@ -5,7 +7,7 @@ module DEBUGGER__ | |
| 5 7 | 
             
                SHOW_PROTOCOL = ENV['RUBY_DEBUG_DAP_SHOW_PROTOCOL'] == '1'
         | 
| 6 8 |  | 
| 7 9 | 
             
                def dap_setup bytes
         | 
| 8 | 
            -
                  DEBUGGER__.set_config( | 
| 10 | 
            +
                  DEBUGGER__.set_config(no_color: true)
         | 
| 9 11 | 
             
                  @seq = 0
         | 
| 10 12 |  | 
| 11 13 | 
             
                  $stderr.puts '[>]' + bytes if SHOW_PROTOCOL
         | 
| @@ -436,7 +438,7 @@ module DEBUGGER__ | |
| 436 438 | 
             
                  when :backtrace
         | 
| 437 439 | 
             
                    event! :dap_result, :backtrace, req, {
         | 
| 438 440 | 
             
                      stackFrames: @target_frames.map.with_index{|frame, i|
         | 
| 439 | 
            -
                        path = frame. | 
| 441 | 
            +
                        path = frame.realpath
         | 
| 440 442 | 
             
                        ref = frame.file_lines unless File.exist?(path)
         | 
| 441 443 |  | 
| 442 444 | 
             
                        {
         | 
    
        data/lib/debug/session.rb
    CHANGED
    
    | @@ -1,4 +1,5 @@ | |
| 1 | 
            -
            
         | 
| 1 | 
            +
            # frozen_string_literal: true
         | 
| 2 | 
            +
             | 
| 2 3 | 
             
            # skip to load debugger for bundle exec
         | 
| 3 4 | 
             
            return if $0.end_with?('bin/bundle') && ARGV.first == 'exec'
         | 
| 4 5 |  | 
| @@ -52,6 +53,8 @@ class RubyVM::InstructionSequence | |
| 52 53 | 
             
            end
         | 
| 53 54 |  | 
| 54 55 | 
             
            module DEBUGGER__
         | 
| 56 | 
            +
              PresetCommand = Struct.new(:commands, :source, :auto_continue)
         | 
| 57 | 
            +
             | 
| 55 58 | 
             
              class Session
         | 
| 56 59 | 
             
                def initialize ui
         | 
| 57 60 | 
             
                  @ui = ui
         | 
| @@ -67,7 +70,7 @@ module DEBUGGER__ | |
| 67 70 | 
             
                  @displays = []
         | 
| 68 71 | 
             
                  @tc = nil
         | 
| 69 72 | 
             
                  @tc_id = 0
         | 
| 70 | 
            -
                  @ | 
| 73 | 
            +
                  @preset_command = nil
         | 
| 71 74 |  | 
| 72 75 | 
             
                  @frame_map = {} # {id => [threadId, frame_depth]} for DAP
         | 
| 73 76 | 
             
                  @var_map   = {1 => [:globals], } # {id => ...} for DAP
         | 
| @@ -98,6 +101,12 @@ module DEBUGGER__ | |
| 98 101 | 
             
                  @tp_thread_begin.enable
         | 
| 99 102 | 
             
                end
         | 
| 100 103 |  | 
| 104 | 
            +
                def reset_ui ui
         | 
| 105 | 
            +
                  @ui.close
         | 
| 106 | 
            +
                  @ui = ui
         | 
| 107 | 
            +
                  @management_threads << @ui.reader_thread if @ui.respond_to? :reader_thread
         | 
| 108 | 
            +
                end
         | 
| 109 | 
            +
             | 
| 101 110 | 
             
                def session_server_main
         | 
| 102 111 | 
             
                  while evt = @q_evt.pop
         | 
| 103 112 | 
             
                    # varible `@internal_info` is only used for test
         | 
| @@ -105,6 +114,8 @@ module DEBUGGER__ | |
| 105 114 | 
             
                    output.each{|str| @ui.puts str}
         | 
| 106 115 |  | 
| 107 116 | 
             
                    case ev
         | 
| 117 | 
            +
                    when :init
         | 
| 118 | 
            +
                      wait_command_loop tc
         | 
| 108 119 | 
             
                    when :load
         | 
| 109 120 | 
             
                      iseq, src = ev_args
         | 
| 110 121 | 
             
                      on_load iseq, src
         | 
| @@ -133,10 +144,6 @@ module DEBUGGER__ | |
| 133 144 | 
             
                      end
         | 
| 134 145 | 
             
                    when :result
         | 
| 135 146 | 
             
                      case ev_args.first
         | 
| 136 | 
            -
                      when :watch
         | 
| 137 | 
            -
                        bp = ev_args[1]
         | 
| 138 | 
            -
                        @bps[bp.key] = bp
         | 
| 139 | 
            -
                        show_bps bp
         | 
| 140 147 | 
             
                      when :try_display
         | 
| 141 148 | 
             
                        failed_results = ev_args[1]
         | 
| 142 149 | 
             
                        if failed_results.size > 0
         | 
| @@ -145,10 +152,10 @@ module DEBUGGER__ | |
| 145 152 | 
             
                            @ui.puts "canceled: #{@displays.pop}"
         | 
| 146 153 | 
             
                          end
         | 
| 147 154 | 
             
                        end
         | 
| 148 | 
            -
                      when :method_breakpoint
         | 
| 155 | 
            +
                      when :method_breakpoint, :watch_breakpoint
         | 
| 149 156 | 
             
                        bp = ev_args[1]
         | 
| 150 157 | 
             
                        if bp
         | 
| 151 | 
            -
                           | 
| 158 | 
            +
                          add_breakpoint(bp)
         | 
| 152 159 | 
             
                          show_bps bp
         | 
| 153 160 | 
             
                        else
         | 
| 154 161 | 
             
                          # can't make a bp
         | 
| @@ -169,15 +176,24 @@ module DEBUGGER__ | |
| 169 176 | 
             
                  @th_clients.each{|th, thc| thc.close}
         | 
| 170 177 | 
             
                end
         | 
| 171 178 |  | 
| 172 | 
            -
                def  | 
| 173 | 
            -
                  cmds. | 
| 174 | 
            -
                    c.gsub( | 
| 175 | 
            -
                     | 
| 176 | 
            -
                  }
         | 
| 179 | 
            +
                def add_preset_commands name, cmds, kick: true, continue: true
         | 
| 180 | 
            +
                  cs = cmds.map{|c|
         | 
| 181 | 
            +
                    c = c.strip.gsub(/\A\s*\#.*/, '').strip
         | 
| 182 | 
            +
                    c unless c.empty?
         | 
| 183 | 
            +
                  }.compact
         | 
| 184 | 
            +
             | 
| 185 | 
            +
                  unless cs.empty?
         | 
| 186 | 
            +
                    if @preset_command
         | 
| 187 | 
            +
                      @preset_command.commands += cs
         | 
| 188 | 
            +
                    else
         | 
| 189 | 
            +
                      @preset_command = PresetCommand.new(cs, name, continue)
         | 
| 190 | 
            +
                    end
         | 
| 191 | 
            +
                    ThreadClient.current.on_init name if kick
         | 
| 192 | 
            +
                  end
         | 
| 177 193 | 
             
                end
         | 
| 178 194 |  | 
| 179 195 | 
             
                def source iseq
         | 
| 180 | 
            -
                  if CONFIG[: | 
| 196 | 
            +
                  if !CONFIG[:no_color]
         | 
| 181 197 | 
             
                    @sr.get_colored(iseq)
         | 
| 182 198 | 
             
                  else
         | 
| 183 199 | 
             
                    @sr.get(iseq)
         | 
| @@ -207,12 +223,23 @@ module DEBUGGER__ | |
| 207 223 | 
             
                end
         | 
| 208 224 |  | 
| 209 225 | 
             
                def wait_command
         | 
| 210 | 
            -
                  if @ | 
| 226 | 
            +
                  if @preset_command
         | 
| 227 | 
            +
                    if @preset_command.commands.empty?
         | 
| 228 | 
            +
                      if @preset_command.auto_continue
         | 
| 229 | 
            +
                        @preset_command = nil
         | 
| 230 | 
            +
                        @tc << :continue
         | 
| 231 | 
            +
                        return
         | 
| 232 | 
            +
                      else
         | 
| 233 | 
            +
                        @preset_command = nil
         | 
| 234 | 
            +
                        return :retry
         | 
| 235 | 
            +
                      end
         | 
| 236 | 
            +
                    else
         | 
| 237 | 
            +
                      line = @preset_command.commands.shift
         | 
| 238 | 
            +
                      @ui.puts "(rdbg:#{@preset_command.source}) #{line}"
         | 
| 239 | 
            +
                    end
         | 
| 240 | 
            +
                  else
         | 
| 211 241 | 
             
                    @ui.puts "INTERNAL_INFO: #{JSON.generate(@internal_info)}" if ENV['RUBY_DEBUG_TEST_MODE']
         | 
| 212 242 | 
             
                    line = @ui.readline
         | 
| 213 | 
            -
                  else
         | 
| 214 | 
            -
                    line = @initial_commands.shift.strip
         | 
| 215 | 
            -
                    @ui.puts "(rdbg:init) #{line}"
         | 
| 216 243 | 
             
                  end
         | 
| 217 244 |  | 
| 218 245 | 
             
                  case line
         | 
| @@ -247,21 +274,25 @@ module DEBUGGER__ | |
| 247 274 | 
             
                  # * `s[tep]`
         | 
| 248 275 | 
             
                  #   * Step in. Resume the program until next breakable point.
         | 
| 249 276 | 
             
                  when 's', 'step'
         | 
| 277 | 
            +
                    cancel_auto_continue
         | 
| 250 278 | 
             
                    @tc << [:step, :in]
         | 
| 251 279 |  | 
| 252 280 | 
             
                  # * `n[ext]`
         | 
| 253 281 | 
             
                  #   * Step over. Resume the program until next line.
         | 
| 254 282 | 
             
                  when 'n', 'next'
         | 
| 283 | 
            +
                    cancel_auto_continue
         | 
| 255 284 | 
             
                    @tc << [:step, :next]
         | 
| 256 285 |  | 
| 257 286 | 
             
                  # * `fin[ish]`
         | 
| 258 287 | 
             
                  #   * Finish this frame. Resume the program until the current frame is finished.
         | 
| 259 288 | 
             
                  when 'fin', 'finish'
         | 
| 289 | 
            +
                    cancel_auto_continue
         | 
| 260 290 | 
             
                    @tc << [:step, :finish]
         | 
| 261 291 |  | 
| 262 292 | 
             
                  # * `c[ontinue]`
         | 
| 263 293 | 
             
                  #   * Resume the program.
         | 
| 264 294 | 
             
                  when 'c', 'continue'
         | 
| 295 | 
            +
                    cancel_auto_continue
         | 
| 265 296 | 
             
                    @tc << :continue
         | 
| 266 297 |  | 
| 267 298 | 
             
                  # * `q[uit]` or `Ctrl-D`
         | 
| @@ -366,8 +397,8 @@ module DEBUGGER__ | |
| 366 397 | 
             
                  #   * Stop the execution when the result of current scope's `@ivar` is changed.
         | 
| 367 398 | 
             
                  #   * Note that this feature is super slow.
         | 
| 368 399 | 
             
                  when 'wat', 'watch'
         | 
| 369 | 
            -
                    if arg
         | 
| 370 | 
            -
                      @tc << [: | 
| 400 | 
            +
                    if arg && arg.match?(/\A@\w+/)
         | 
| 401 | 
            +
                      @tc << [:breakpoint, :watch, arg]
         | 
| 371 402 | 
             
                    else
         | 
| 372 403 | 
             
                      show_bps
         | 
| 373 404 | 
             
                      return :retry
         | 
| @@ -481,9 +512,7 @@ module DEBUGGER__ | |
| 481 512 | 
             
                    case arg
         | 
| 482 513 | 
             
                    when /(\d+)/
         | 
| 483 514 | 
             
                      if @displays[n = $1.to_i]
         | 
| 484 | 
            -
                         | 
| 485 | 
            -
                          @displays.delete_at n
         | 
| 486 | 
            -
                        end
         | 
| 515 | 
            +
                        @displays.delete_at n
         | 
| 487 516 | 
             
                      end
         | 
| 488 517 | 
             
                      @tc << [:eval, :display, @displays]
         | 
| 489 518 | 
             
                    when nil
         | 
| @@ -560,6 +589,9 @@ module DEBUGGER__ | |
| 560 589 | 
             
                    end
         | 
| 561 590 | 
             
                    @tc << [:eval, :call, 'binding.irb']
         | 
| 562 591 |  | 
| 592 | 
            +
                    # don't repeat irb command
         | 
| 593 | 
            +
                    @repl_prev_line = nil
         | 
| 594 | 
            +
             | 
| 563 595 | 
             
                  ### Thread control
         | 
| 564 596 |  | 
| 565 597 | 
             
                  # * `th[read]`
         | 
| @@ -608,6 +640,12 @@ module DEBUGGER__ | |
| 608 640 | 
             
                  return :retry
         | 
| 609 641 | 
             
                end
         | 
| 610 642 |  | 
| 643 | 
            +
                def cancel_auto_continue
         | 
| 644 | 
            +
                  if @preset_command&.auto_continue
         | 
| 645 | 
            +
                    @preset_command.auto_continue = false
         | 
| 646 | 
            +
                  end
         | 
| 647 | 
            +
                end
         | 
| 648 | 
            +
             | 
| 611 649 | 
             
                def show_help arg
         | 
| 612 650 | 
             
                  DEBUGGER__.helps.each{|cat, cs|
         | 
| 613 651 | 
             
                    cs.each{|ws, desc|
         | 
| @@ -883,10 +921,16 @@ module DEBUGGER__ | |
| 883 921 |  | 
| 884 922 | 
             
                def add_breakpoint bp
         | 
| 885 923 | 
             
                  if @bps.has_key? bp.key
         | 
| 886 | 
            -
                     | 
| 924 | 
            +
                    unless bp.duplicable?
         | 
| 925 | 
            +
                      @ui.puts "duplicated breakpoint: #{bp}"
         | 
| 926 | 
            +
                      bp.disable
         | 
| 927 | 
            +
                    end
         | 
| 887 928 | 
             
                  else
         | 
| 888 929 | 
             
                    @bps[bp.key] = bp
         | 
| 889 930 | 
             
                  end
         | 
| 931 | 
            +
             | 
| 932 | 
            +
                  # don't repeat commands that add breakpoints
         | 
| 933 | 
            +
                  @repl_prev_line = nil
         | 
| 890 934 | 
             
                end
         | 
| 891 935 |  | 
| 892 936 | 
             
                def rehash_bps
         | 
| @@ -932,6 +976,7 @@ module DEBUGGER__ | |
| 932 976 | 
             
                def add_line_breakpoint file, line, **kw
         | 
| 933 977 | 
             
                  file = resolve_path(file)
         | 
| 934 978 | 
             
                  bp = LineBreakpoint.new(file, line, **kw)
         | 
| 979 | 
            +
             | 
| 935 980 | 
             
                  add_breakpoint bp
         | 
| 936 981 | 
             
                rescue Errno::ENOENT => e
         | 
| 937 982 | 
             
                  @ui.puts e.message
         | 
| @@ -1022,44 +1067,71 @@ module DEBUGGER__ | |
| 1022 1067 |  | 
| 1023 1068 | 
             
              # start methods
         | 
| 1024 1069 |  | 
| 1025 | 
            -
              def self. | 
| 1070 | 
            +
              def self.start nonstop: false, **kw
         | 
| 1026 1071 | 
             
                set_config(kw)
         | 
| 1027 1072 |  | 
| 1028 | 
            -
                 | 
| 1029 | 
            -
             | 
| 1030 | 
            -
             | 
| 1073 | 
            +
                unless defined? SESSION
         | 
| 1074 | 
            +
                  require_relative 'console'
         | 
| 1075 | 
            +
                  initialize_session UI_Console.new
         | 
| 1076 | 
            +
                end
         | 
| 1031 1077 |  | 
| 1032 | 
            -
                 | 
| 1033 | 
            -
                  ThreadClient.current.on_trap :SIGINT
         | 
| 1034 | 
            -
                }
         | 
| 1078 | 
            +
                setup_initial_suspend unless nonstop
         | 
| 1035 1079 | 
             
              end
         | 
| 1036 1080 |  | 
| 1037 | 
            -
              def self.open host: nil, port: ::DEBUGGER__::CONFIG[:port], sock_path: nil, sock_dir: nil, **kw
         | 
| 1081 | 
            +
              def self.open host: nil, port: ::DEBUGGER__::CONFIG[:port], sock_path: nil, sock_dir: nil, nonstop: false, **kw
         | 
| 1038 1082 | 
             
                set_config(kw)
         | 
| 1039 1083 |  | 
| 1040 1084 | 
             
                if port
         | 
| 1041 | 
            -
                  open_tcp host: host, port: port
         | 
| 1085 | 
            +
                  open_tcp host: host, port: port, nonstop: nonstop
         | 
| 1042 1086 | 
             
                else
         | 
| 1043 | 
            -
                  open_unix sock_path: sock_path, sock_dir: sock_dir
         | 
| 1087 | 
            +
                  open_unix sock_path: sock_path, sock_dir: sock_dir, nonstop: nonstop
         | 
| 1044 1088 | 
             
                end
         | 
| 1045 1089 | 
             
              end
         | 
| 1046 1090 |  | 
| 1047 | 
            -
              def self.open_tcp host: nil, port:, **kw
         | 
| 1091 | 
            +
              def self.open_tcp host: nil, port:, nonstop: false, **kw
         | 
| 1048 1092 | 
             
                set_config(kw)
         | 
| 1049 1093 | 
             
                require_relative 'server'
         | 
| 1050 | 
            -
             | 
| 1094 | 
            +
             | 
| 1095 | 
            +
                if defined? SESSION
         | 
| 1096 | 
            +
                  SESSION.reset_ui UI_TcpServer.new(host: host, port: port)
         | 
| 1097 | 
            +
                else
         | 
| 1098 | 
            +
                  initialize_session UI_TcpServer.new(host: host, port: port)
         | 
| 1099 | 
            +
                end
         | 
| 1100 | 
            +
             | 
| 1101 | 
            +
                setup_initial_suspend unless nonstop
         | 
| 1051 1102 | 
             
              end
         | 
| 1052 1103 |  | 
| 1053 | 
            -
              def self.open_unix sock_path: nil, sock_dir: nil, **kw
         | 
| 1104 | 
            +
              def self.open_unix sock_path: nil, sock_dir: nil, nonstop: false, **kw
         | 
| 1054 1105 | 
             
                set_config(kw)
         | 
| 1055 1106 | 
             
                require_relative 'server'
         | 
| 1056 | 
            -
             | 
| 1107 | 
            +
             | 
| 1108 | 
            +
                if defined? SESSION
         | 
| 1109 | 
            +
                  SESSION.reset_ui UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
         | 
| 1110 | 
            +
                else
         | 
| 1111 | 
            +
                  initialize_session UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
         | 
| 1112 | 
            +
                end
         | 
| 1113 | 
            +
             | 
| 1114 | 
            +
                setup_initial_suspend unless nonstop
         | 
| 1057 1115 | 
             
              end
         | 
| 1058 1116 |  | 
| 1059 1117 | 
             
              # boot utilities
         | 
| 1060 1118 |  | 
| 1119 | 
            +
              def self.setup_initial_suspend
         | 
| 1120 | 
            +
                if !::DEBUGGER__::CONFIG[:nonstop]
         | 
| 1121 | 
            +
                  if loc = ::DEBUGGER__.require_location
         | 
| 1122 | 
            +
                    # require 'debug/console' or 'debug'
         | 
| 1123 | 
            +
                    add_line_breakpoint loc.absolute_path, loc.lineno + 1, oneshot: true, hook_call: false
         | 
| 1124 | 
            +
                  else
         | 
| 1125 | 
            +
                    # -r
         | 
| 1126 | 
            +
                    add_line_breakpoint $0, 1, oneshot: true, hook_call: false
         | 
| 1127 | 
            +
                  end
         | 
| 1128 | 
            +
                end
         | 
| 1129 | 
            +
              end
         | 
| 1130 | 
            +
             | 
| 1061 1131 | 
             
              class << self
         | 
| 1062 1132 | 
             
                define_method :initialize_session do |ui|
         | 
| 1133 | 
            +
                  DEBUGGER__.warn "Session start (pid: #{Process.pid})"
         | 
| 1134 | 
            +
             | 
| 1063 1135 | 
             
                  ::DEBUGGER__.const_set(:SESSION, Session.new(ui))
         | 
| 1064 1136 |  | 
| 1065 1137 | 
             
                  # default breakpoints
         | 
| @@ -1067,10 +1139,15 @@ module DEBUGGER__ | |
| 1067 1139 | 
             
                  # ::DEBUGGER__.add_catch_breakpoint 'RuntimeError'
         | 
| 1068 1140 |  | 
| 1069 1141 | 
             
                  Binding.module_eval do
         | 
| 1070 | 
            -
                    def bp command: nil
         | 
| 1142 | 
            +
                    def bp command: nil, nonstop: nil
         | 
| 1071 1143 | 
             
                      if command
         | 
| 1072 1144 | 
             
                        cmds = command.split(";;")
         | 
| 1073 | 
            -
                         | 
| 1145 | 
            +
                        # nonstop
         | 
| 1146 | 
            +
                        #  nil, true -> auto_continue
         | 
| 1147 | 
            +
                        #  false     -> stop
         | 
| 1148 | 
            +
                        SESSION.add_preset_commands 'binding.bp(command:)', cmds,
         | 
| 1149 | 
            +
                                                    kick: false,
         | 
| 1150 | 
            +
                                                    continue: nonstop == false ? false : true
         | 
| 1074 1151 | 
             
                      end
         | 
| 1075 1152 |  | 
| 1076 1153 | 
             
                      ::DEBUGGER__.add_line_breakpoint __FILE__, __LINE__ + 1, oneshot: true
         | 
| @@ -1078,36 +1155,26 @@ module DEBUGGER__ | |
| 1078 1155 | 
             
                    end
         | 
| 1079 1156 | 
             
                  end
         | 
| 1080 1157 |  | 
| 1081 | 
            -
                  if !::DEBUGGER__::CONFIG[:nonstop]
         | 
| 1082 | 
            -
                    if loc = ::DEBUGGER__.require_location
         | 
| 1083 | 
            -
                      # require 'debug/console' or 'debug'
         | 
| 1084 | 
            -
                      add_line_breakpoint loc.absolute_path, loc.lineno + 1, oneshot: true, hook_call: false
         | 
| 1085 | 
            -
                    else
         | 
| 1086 | 
            -
                      # -r
         | 
| 1087 | 
            -
                      add_line_breakpoint $0, 1, oneshot: true, hook_call: false
         | 
| 1088 | 
            -
                    end
         | 
| 1089 | 
            -
                  end
         | 
| 1090 | 
            -
             | 
| 1091 1158 | 
             
                  load_rc
         | 
| 1092 1159 | 
             
                end
         | 
| 1093 1160 | 
             
              end
         | 
| 1094 1161 |  | 
| 1095 1162 | 
             
              def self.load_rc
         | 
| 1096 | 
            -
                [ | 
| 1097 | 
            -
             | 
| 1098 | 
            -
             | 
| 1099 | 
            -
             | 
| 1100 | 
            -
             | 
| 1101 | 
            -
             | 
| 1102 | 
            -
             | 
| 1103 | 
            -
                [init_script = ::DEBUGGER__::CONFIG[:init_script],
         | 
| 1104 | 
            -
                 './.rdbgrc',
         | 
| 1105 | 
            -
                 File.expand_path('~/.rdbgrc')].each{|path|
         | 
| 1106 | 
            -
                   next unless path
         | 
| 1163 | 
            +
                [[File.expand_path('~/.rdbgrc'), true],
         | 
| 1164 | 
            +
                 [File.expand_path('~/.rdbgrc.rb'), true],
         | 
| 1165 | 
            +
                 # ['./.rdbgrc', true], # disable because of security concern
         | 
| 1166 | 
            +
                 [::DEBUGGER__::CONFIG[:init_script], false],
         | 
| 1167 | 
            +
                 ].each{|(path, rc)|
         | 
| 1168 | 
            +
                  next unless path
         | 
| 1169 | 
            +
                  next if rc && ::DEBUGGER__::CONFIG[:no_rc] # ignore rc
         | 
| 1107 1170 |  | 
| 1108 1171 | 
             
                  if File.file? path
         | 
| 1109 | 
            -
                     | 
| 1110 | 
            -
             | 
| 1172 | 
            +
                    if path.end_with?('.rb')
         | 
| 1173 | 
            +
                      load path
         | 
| 1174 | 
            +
                    else
         | 
| 1175 | 
            +
                      ::DEBUGGER__::SESSION.add_preset_commands path, File.readlines(path)
         | 
| 1176 | 
            +
                    end
         | 
| 1177 | 
            +
                  elsif !rc
         | 
| 1111 1178 | 
             
                    warn "Not found: #{path}"
         | 
| 1112 1179 | 
             
                  end
         | 
| 1113 1180 | 
             
                }
         | 
| @@ -1115,7 +1182,7 @@ module DEBUGGER__ | |
| 1115 1182 | 
             
                # given debug commands
         | 
| 1116 1183 | 
             
                if ::DEBUGGER__::CONFIG[:commands]
         | 
| 1117 1184 | 
             
                  cmds = ::DEBUGGER__::CONFIG[:commands].split(';;')
         | 
| 1118 | 
            -
                  ::DEBUGGER__::SESSION. | 
| 1185 | 
            +
                  ::DEBUGGER__::SESSION.add_preset_commands "commands", cmds, kick: false, continue: false
         | 
| 1119 1186 | 
             
                end
         | 
| 1120 1187 | 
             
              end
         | 
| 1121 1188 |  | 
| @@ -1194,4 +1261,13 @@ module DEBUGGER__ | |
| 1194 1261 | 
             
                  str
         | 
| 1195 1262 | 
             
                end
         | 
| 1196 1263 | 
             
              end
         | 
| 1264 | 
            +
             | 
| 1265 | 
            +
              def self.warn msg, level = :warn
         | 
| 1266 | 
            +
                case level
         | 
| 1267 | 
            +
                when :warn
         | 
| 1268 | 
            +
                  STDERR.puts "DEBUGGER: #{msg}" unless CONFIG[:quiet]
         | 
| 1269 | 
            +
                when :error
         | 
| 1270 | 
            +
                  STDERR.puts "DEBUGGER: #{msg}"
         | 
| 1271 | 
            +
                end
         | 
| 1272 | 
            +
              end
         | 
| 1197 1273 | 
             
            end
         |