debug 1.5.0 → 1.6.0
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 +79 -11
- data/README.md +39 -16
- data/Rakefile +25 -7
- data/debug.gemspec +1 -1
- data/exe/rdbg +7 -3
- data/ext/debug/debug.c +0 -22
- data/ext/debug/extconf.rb +18 -7
- data/lib/debug/breakpoint.rb +68 -33
- data/lib/debug/client.rb +12 -2
- data/lib/debug/config.rb +55 -24
- data/lib/debug/console.rb +9 -3
- data/lib/debug/frame_info.rb +31 -24
- data/lib/debug/server.rb +44 -17
- data/lib/debug/server_cdp.rb +5 -5
- data/lib/debug/server_dap.rb +221 -115
- data/lib/debug/session.rb +227 -129
- data/lib/debug/source_repository.rb +13 -0
- data/lib/debug/thread_client.rb +161 -64
- data/lib/debug/tracer.rb +4 -3
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +28 -8
- metadata +5 -6
- data/lib/debug/bp.vim +0 -68
    
        data/lib/debug/config.rb
    CHANGED
    
    | @@ -7,46 +7,50 @@ module DEBUGGER__ | |
| 7 7 | 
             
                ERROR:   2,
         | 
| 8 8 | 
             
                WARN:    3,
         | 
| 9 9 | 
             
                INFO:    4,
         | 
| 10 | 
            +
                DEBUG:   5
         | 
| 10 11 | 
             
              }.freeze
         | 
| 11 12 |  | 
| 12 13 | 
             
              CONFIG_SET = {
         | 
| 13 14 | 
             
                # UI setting
         | 
| 14 | 
            -
                log_level:      ['RUBY_DEBUG_LOG_LEVEL',      "UI: Log level same as Logger | 
| 15 | 
            -
                show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint  | 
| 16 | 
            -
                show_frames:    ['RUBY_DEBUG_SHOW_FRAMES',    "UI: Show n frames on breakpoint | 
| 17 | 
            -
                use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", | 
| 18 | 
            -
                no_color:       ['RUBY_DEBUG_NO_COLOR',       "UI: Do not use colorize | 
| 19 | 
            -
                no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT | 
| 20 | 
            -
                no_reline:      ['RUBY_DEBUG_NO_RELINE',      "UI: Do not use Reline library | 
| 15 | 
            +
                log_level:      ['RUBY_DEBUG_LOG_LEVEL',      "UI: Log level same as Logger",               :loglevel, "WARN"],
         | 
| 16 | 
            +
                show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"],
         | 
| 17 | 
            +
                show_frames:    ['RUBY_DEBUG_SHOW_FRAMES',    "UI: Show n frames on breakpoint",            :int, "2"],
         | 
| 18 | 
            +
                use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"],
         | 
| 19 | 
            +
                no_color:       ['RUBY_DEBUG_NO_COLOR',       "UI: Do not use colorize",                    :bool, "false"],
         | 
| 20 | 
            +
                no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT",               :bool, "false"],
         | 
| 21 | 
            +
                no_reline:      ['RUBY_DEBUG_NO_RELINE',      "UI: Do not use Reline library",              :bool, "false"],
         | 
| 22 | 
            +
                no_hint:        ['RUBY_DEBUG_NO_HINT',        "UI: Do not show the hint on the REPL",       :bool, "false"],
         | 
| 21 23 |  | 
| 22 24 | 
             
                # control setting
         | 
| 23 | 
            -
                skip_path:      ['RUBY_DEBUG_SKIP_PATH',      "CONTROL: Skip showing/entering frames for given paths | 
| 24 | 
            -
                skip_nosrc:     ['RUBY_DEBUG_SKIP_NOSRC',     "CONTROL: Skip on no source code lines | 
| 25 | 
            -
                keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it | 
| 26 | 
            -
                postmortem:     ['RUBY_DEBUG_POSTMORTEM',     "CONTROL: Enable postmortem debug | 
| 27 | 
            -
                fork_mode:      ['RUBY_DEBUG_FORK_MODE',      "CONTROL: Control which process activates a debugger after fork (both/parent/child)  | 
| 28 | 
            -
                sigdump_sig:    ['RUBY_DEBUG_SIGDUMP_SIG',    "CONTROL: Sigdump signal  | 
| 25 | 
            +
                skip_path:      ['RUBY_DEBUG_SKIP_PATH',      "CONTROL: Skip showing/entering frames for given paths", :path],
         | 
| 26 | 
            +
                skip_nosrc:     ['RUBY_DEBUG_SKIP_NOSRC',     "CONTROL: Skip on no source code lines",              :bool, "false"],
         | 
| 27 | 
            +
                keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it",   :bool, "false"],
         | 
| 28 | 
            +
                postmortem:     ['RUBY_DEBUG_POSTMORTEM',     "CONTROL: Enable postmortem debug",                   :bool, "false"],
         | 
| 29 | 
            +
                fork_mode:      ['RUBY_DEBUG_FORK_MODE',      "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"],
         | 
| 30 | 
            +
                sigdump_sig:    ['RUBY_DEBUG_SIGDUMP_SIG',    "CONTROL: Sigdump signal", :bool, "false"],
         | 
| 29 31 |  | 
| 30 32 | 
             
                # boot setting
         | 
| 31 | 
            -
                nonstop:        ['RUBY_DEBUG_NONSTOP',     "BOOT: Nonstop mode",                                                :bool],
         | 
| 32 | 
            -
                stop_at_load:   ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location",                               :bool],
         | 
| 33 | 
            +
                nonstop:        ['RUBY_DEBUG_NONSTOP',     "BOOT: Nonstop mode",                                                :bool, "false"],
         | 
| 34 | 
            +
                stop_at_load:   ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location",                               :bool, "false"],
         | 
| 33 35 | 
             
                init_script:    ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
         | 
| 34 36 | 
             
                commands:       ['RUBY_DEBUG_COMMANDS',    "BOOT: debug commands invoked at first stop. commands should be separated by ';;'"],
         | 
| 35 | 
            -
                no_rc:          ['RUBY_DEBUG_NO_RC',       "BOOT: ignore loading ~/.rdbgrc(.rb)",                               :bool],
         | 
| 36 | 
            -
                history_file:   ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file | 
| 37 | 
            -
                save_history:   ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines  | 
| 37 | 
            +
                no_rc:          ['RUBY_DEBUG_NO_RC',       "BOOT: ignore loading ~/.rdbgrc(.rb)",                               :bool, "false"],
         | 
| 38 | 
            +
                history_file:   ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file",               :string, "~/.rdbg_history"],
         | 
| 39 | 
            +
                save_history:   ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"],
         | 
| 38 40 |  | 
| 39 41 | 
             
                # remote setting
         | 
| 40 42 | 
             
                port:           ['RUBY_DEBUG_PORT',         "REMOTE: TCP/IP remote debugging: port"],
         | 
| 41 | 
            -
                host:           ['RUBY_DEBUG_HOST',         "REMOTE: TCP/IP remote debugging: host  | 
| 43 | 
            +
                host:           ['RUBY_DEBUG_HOST',         "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"],
         | 
| 42 44 | 
             
                sock_path:      ['RUBY_DEBUG_SOCK_PATH',    "REMOTE: UNIX Domain Socket remote debugging: socket path"],
         | 
| 43 45 | 
             
                sock_dir:       ['RUBY_DEBUG_SOCK_DIR',     "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
         | 
| 46 | 
            +
                local_fs_map:   ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map],
         | 
| 47 | 
            +
                skip_bp:        ['RUBY_DEBUG_SKIP_BP',      "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'],
         | 
| 44 48 | 
             
                cookie:         ['RUBY_DEBUG_COOKIE',       "REMOTE: Cookie for negotiation"],
         | 
| 45 49 | 
             
                open_frontend:  ['RUBY_DEBUG_OPEN_FRONTEND',"REMOTE: frontend used by open command (vscode, chrome, default: rdbg)."],
         | 
| 46 50 | 
             
                chrome_path:    ['RUBY_DEBUG_CHROME_PATH',  "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"],
         | 
| 47 51 |  | 
| 48 52 | 
             
                # obsolete
         | 
| 49 | 
            -
                parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork | 
| 53 | 
            +
                parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork",     :bool, "false"],
         | 
| 50 54 | 
             
              }.freeze
         | 
| 51 55 |  | 
| 52 56 | 
             
              CONFIG_MAP = CONFIG_SET.map{|k, (ev, _)| [k, ev]}.to_h.freeze
         | 
| @@ -59,11 +63,23 @@ module DEBUGGER__ | |
| 59 63 | 
             
                end
         | 
| 60 64 |  | 
| 61 65 | 
             
                def initialize argv
         | 
| 66 | 
            +
                  @skip_all = false
         | 
| 67 | 
            +
             | 
| 62 68 | 
             
                  if self.class.config
         | 
| 63 69 | 
             
                    raise 'Can not make multiple configurations in one process'
         | 
| 64 70 | 
             
                  end
         | 
| 65 71 |  | 
| 66 | 
            -
                   | 
| 72 | 
            +
                  config = self.class.parse_argv(argv)
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                  # apply defaults
         | 
| 75 | 
            +
                  CONFIG_SET.each do |k, config_detail|
         | 
| 76 | 
            +
                    unless config.key?(k)
         | 
| 77 | 
            +
                      default_value = config_detail[3]
         | 
| 78 | 
            +
                      config[k] = parse_config_value(k, default_value)
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                  end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                  update config
         | 
| 67 83 | 
             
                end
         | 
| 68 84 |  | 
| 69 85 | 
             
                def inspect
         | 
| @@ -78,6 +94,14 @@ module DEBUGGER__ | |
| 78 94 | 
             
                  set_config(key => val)
         | 
| 79 95 | 
             
                end
         | 
| 80 96 |  | 
| 97 | 
            +
                def skip_all
         | 
| 98 | 
            +
                  @skip_all = true
         | 
| 99 | 
            +
                end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                def skip?
         | 
| 102 | 
            +
                  @skip_all
         | 
| 103 | 
            +
                end
         | 
| 104 | 
            +
             | 
| 81 105 | 
             
                def set_config(**kw)
         | 
| 82 106 | 
             
                  conf = config.dup
         | 
| 83 107 | 
             
                  kw.each{|k, v|
         | 
| @@ -114,11 +138,13 @@ module DEBUGGER__ | |
| 114 138 | 
             
                  self.class.instance_variable_set(:@config, conf.freeze)
         | 
| 115 139 |  | 
| 116 140 | 
             
                  # Post process
         | 
| 117 | 
            -
                  if_updated old_conf, conf, :keep_alloc_site do | | 
| 141 | 
            +
                  if_updated old_conf, conf, :keep_alloc_site do |old, new|
         | 
| 118 142 | 
             
                    if new
         | 
| 119 143 | 
             
                      require 'objspace'
         | 
| 120 144 | 
             
                      ObjectSpace.trace_object_allocations_start
         | 
| 121 | 
            -
                     | 
| 145 | 
            +
                    end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
                    if old && !new
         | 
| 122 148 | 
             
                      ObjectSpace.trace_object_allocations_stop
         | 
| 123 149 | 
             
                    end
         | 
| 124 150 | 
             
                  end
         | 
| @@ -215,6 +241,8 @@ module DEBUGGER__ | |
| 215 241 | 
             
                        e
         | 
| 216 242 | 
             
                      end
         | 
| 217 243 | 
             
                    }
         | 
| 244 | 
            +
                  when :path_map
         | 
| 245 | 
            +
                    valstr.split(',').map{|e| e.split(':')}
         | 
| 218 246 | 
             
                  else
         | 
| 219 247 | 
             
                    valstr
         | 
| 220 248 | 
             
                  end
         | 
| @@ -223,6 +251,7 @@ module DEBUGGER__ | |
| 223 251 | 
             
                def self.parse_argv argv
         | 
| 224 252 | 
             
                  config = {
         | 
| 225 253 | 
             
                    mode: :start,
         | 
| 254 | 
            +
                    no_color: (nc = ENV['NO_COLOR']) && !nc.empty?,
         | 
| 226 255 | 
             
                  }
         | 
| 227 256 | 
             
                  CONFIG_MAP.each{|key, evname|
         | 
| 228 257 | 
             
                    if val = ENV[evname]
         | 
| @@ -361,6 +390,8 @@ module DEBUGGER__ | |
| 361 390 | 
             
                      case CONFIG_SET[key][2]
         | 
| 362 391 | 
             
                      when :path
         | 
| 363 392 | 
             
                        valstr = config[key].map{|e| e.kind_of?(Regexp) ? e.inspect : e}.join(':')
         | 
| 393 | 
            +
                      when :path_map
         | 
| 394 | 
            +
                        valstr = config[key].map{|e| e.join(':')}.join(',')
         | 
| 364 395 | 
             
                      else
         | 
| 365 396 | 
             
                        valstr = config[key].to_s
         | 
| 366 397 | 
             
                      end
         | 
| @@ -428,7 +459,7 @@ module DEBUGGER__ | |
| 428 459 | 
             
              end
         | 
| 429 460 |  | 
| 430 461 | 
             
              def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir)
         | 
| 431 | 
            -
                user = ENV['USER'] || ' | 
| 462 | 
            +
                user = ENV['USER'] || 'UnknownUser'
         | 
| 432 463 | 
             
                File.join(base_dir, "ruby-debug-#{user}")
         | 
| 433 464 | 
             
              end
         | 
| 434 465 |  | 
    
        data/lib/debug/console.rb
    CHANGED
    
    | @@ -98,7 +98,7 @@ module DEBUGGER__ | |
| 98 98 | 
             
                      when :ruby
         | 
| 99 99 | 
             
                        colorize_code(buff.chomp)
         | 
| 100 100 | 
             
                      end
         | 
| 101 | 
            -
                    end
         | 
| 101 | 
            +
                    end unless CONFIG[:no_hint]
         | 
| 102 102 |  | 
| 103 103 | 
             
                    yield
         | 
| 104 104 |  | 
| @@ -174,7 +174,13 @@ module DEBUGGER__ | |
| 174 174 | 
             
                end
         | 
| 175 175 |  | 
| 176 176 | 
             
                def history_file
         | 
| 177 | 
            -
                  CONFIG[:history_file] | 
| 177 | 
            +
                  history_file = CONFIG[:history_file]
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                  if !history_file.empty?
         | 
| 180 | 
            +
                    File.expand_path(history_file)
         | 
| 181 | 
            +
                  else
         | 
| 182 | 
            +
                    history_file
         | 
| 183 | 
            +
                  end
         | 
| 178 184 | 
             
                end
         | 
| 179 185 |  | 
| 180 186 | 
             
                FH = "# Today's OMIKUJI: "
         | 
| @@ -202,7 +208,7 @@ module DEBUGGER__ | |
| 202 208 | 
             
                  if history && @init_history_lines
         | 
| 203 209 | 
             
                    added_records = history.to_a[@init_history_lines .. -1]
         | 
| 204 210 | 
             
                    path = history_file
         | 
| 205 | 
            -
                    max = CONFIG[:save_history] | 
| 211 | 
            +
                    max = CONFIG[:save_history]
         | 
| 206 212 |  | 
| 207 213 | 
             
                    if !added_records.empty? && !path.empty?
         | 
| 208 214 | 
             
                      orig_records = read_history_file
         | 
    
        data/lib/debug/frame_info.rb
    CHANGED
    
    | @@ -27,8 +27,8 @@ module DEBUGGER__ | |
| 27 27 | 
             
                  location.absolute_path
         | 
| 28 28 | 
             
                end
         | 
| 29 29 |  | 
| 30 | 
            -
                def pretty_path
         | 
| 31 | 
            -
                  return '#<none>' unless path | 
| 30 | 
            +
                def self.pretty_path path
         | 
| 31 | 
            +
                  return '#<none>' unless path
         | 
| 32 32 | 
             
                  use_short_path = CONFIG[:use_short_path]
         | 
| 33 33 |  | 
| 34 34 | 
             
                  case
         | 
| @@ -45,15 +45,18 @@ module DEBUGGER__ | |
| 45 45 | 
             
                  end
         | 
| 46 46 | 
             
                end
         | 
| 47 47 |  | 
| 48 | 
            +
                def pretty_path
         | 
| 49 | 
            +
                  FrameInfo.pretty_path path
         | 
| 50 | 
            +
                end
         | 
| 51 | 
            +
             | 
| 48 52 | 
             
                def name
         | 
| 49 53 | 
             
                  # p frame_type: frame_type, self: self
         | 
| 50 54 | 
             
                  case frame_type
         | 
| 51 55 | 
             
                  when :block
         | 
| 52 | 
            -
                    level, block_loc | 
| 56 | 
            +
                    level, block_loc = block_identifier
         | 
| 53 57 | 
             
                    "block in #{block_loc}#{level}"
         | 
| 54 58 | 
             
                  when :method
         | 
| 55 | 
            -
                     | 
| 56 | 
            -
                    "#{ci}"
         | 
| 59 | 
            +
                    method_identifier
         | 
| 57 60 | 
             
                  when :c
         | 
| 58 61 | 
             
                    c_identifier
         | 
| 59 62 | 
             
                  when :other
         | 
| @@ -83,16 +86,13 @@ module DEBUGGER__ | |
| 83 86 |  | 
| 84 87 | 
             
                def block_identifier
         | 
| 85 88 | 
             
                  return unless frame_type == :block
         | 
| 86 | 
            -
                  args = parameters_info
         | 
| 87 89 | 
             
                  _, level, block_loc = location.label.match(BLOCK_LABL_REGEXP).to_a
         | 
| 88 | 
            -
                  [level || "", block_loc | 
| 90 | 
            +
                  [level || "", block_loc]
         | 
| 89 91 | 
             
                end
         | 
| 90 92 |  | 
| 91 93 | 
             
                def method_identifier
         | 
| 92 94 | 
             
                  return unless frame_type == :method
         | 
| 93 | 
            -
                   | 
| 94 | 
            -
                  ci = "#{klass_sig}#{callee}"
         | 
| 95 | 
            -
                  [ci, args]
         | 
| 95 | 
            +
                  "#{klass_sig}#{callee}"
         | 
| 96 96 | 
             
                end
         | 
| 97 97 |  | 
| 98 98 | 
             
                def c_identifier
         | 
| @@ -106,7 +106,11 @@ module DEBUGGER__ | |
| 106 106 | 
             
                end
         | 
| 107 107 |  | 
| 108 108 | 
             
                def callee
         | 
| 109 | 
            -
                  self._callee ||=  | 
| 109 | 
            +
                  self._callee ||= begin
         | 
| 110 | 
            +
                                     self.binding&.eval('__callee__')
         | 
| 111 | 
            +
                                   rescue NameError # BasicObject
         | 
| 112 | 
            +
                                     nil
         | 
| 113 | 
            +
                                   end
         | 
| 110 114 | 
             
                end
         | 
| 111 115 |  | 
| 112 116 | 
             
                def return_str
         | 
| @@ -115,6 +119,11 @@ module DEBUGGER__ | |
| 115 119 | 
             
                  end
         | 
| 116 120 | 
             
                end
         | 
| 117 121 |  | 
| 122 | 
            +
                def matchable_location
         | 
| 123 | 
            +
                  # realpath can sometimes be nil so we can't use it here
         | 
| 124 | 
            +
                  "#{path}:#{location.lineno}"
         | 
| 125 | 
            +
                end
         | 
| 126 | 
            +
             | 
| 118 127 | 
             
                def location_str
         | 
| 119 128 | 
             
                  "#{pretty_path}:#{location.lineno}"
         | 
| 120 129 | 
             
                end
         | 
| @@ -138,18 +147,6 @@ module DEBUGGER__ | |
| 138 147 | 
             
                  end
         | 
| 139 148 | 
             
                end
         | 
| 140 149 |  | 
| 141 | 
            -
                private
         | 
| 142 | 
            -
             | 
| 143 | 
            -
                def get_singleton_class obj
         | 
| 144 | 
            -
                  obj.singleton_class # TODO: don't use it
         | 
| 145 | 
            -
                rescue TypeError
         | 
| 146 | 
            -
                  nil
         | 
| 147 | 
            -
                end
         | 
| 148 | 
            -
             | 
| 149 | 
            -
                private def local_variable_get var
         | 
| 150 | 
            -
                  local_variables[var]
         | 
| 151 | 
            -
                end
         | 
| 152 | 
            -
             | 
| 153 150 | 
             
                def parameters_info
         | 
| 154 151 | 
             
                  vars = iseq.parameters_symbols
         | 
| 155 152 | 
             
                  vars.map{|var|
         | 
| @@ -161,7 +158,17 @@ module DEBUGGER__ | |
| 161 158 | 
             
                  }.compact
         | 
| 162 159 | 
             
                end
         | 
| 163 160 |  | 
| 164 | 
            -
                def  | 
| 161 | 
            +
                private def get_singleton_class obj
         | 
| 162 | 
            +
                  obj.singleton_class # TODO: don't use it
         | 
| 163 | 
            +
                rescue TypeError
         | 
| 164 | 
            +
                  nil
         | 
| 165 | 
            +
                end
         | 
| 166 | 
            +
             | 
| 167 | 
            +
                private def local_variable_get var
         | 
| 168 | 
            +
                  local_variables[var]
         | 
| 169 | 
            +
                end
         | 
| 170 | 
            +
             | 
| 171 | 
            +
                private def klass_sig
         | 
| 165 172 | 
             
                  if self.class == get_singleton_class(self.self)
         | 
| 166 173 | 
             
                    "#{self.self}."
         | 
| 167 174 | 
             
                  else
         | 
    
        data/lib/debug/server.rb
    CHANGED
    
    | @@ -49,6 +49,7 @@ module DEBUGGER__ | |
| 49 49 | 
             
                    accept do |server, already_connected: false|
         | 
| 50 50 | 
             
                      DEBUGGER__.warn "Connected."
         | 
| 51 51 | 
             
                      greeting_done = false
         | 
| 52 | 
            +
                      @need_pause_at_first = true
         | 
| 52 53 |  | 
| 53 54 | 
             
                      @accept_m.synchronize{
         | 
| 54 55 | 
             
                        @sock = server
         | 
| @@ -68,7 +69,7 @@ module DEBUGGER__ | |
| 68 69 | 
             
                      } unless already_connected
         | 
| 69 70 |  | 
| 70 71 | 
             
                      setup_interrupt do
         | 
| 71 | 
            -
                        pause  | 
| 72 | 
            +
                        pause if !already_connected && @need_pause_at_first
         | 
| 72 73 | 
             
                        process
         | 
| 73 74 | 
             
                      end
         | 
| 74 75 |  | 
| @@ -106,6 +107,24 @@ module DEBUGGER__ | |
| 106 107 | 
             
                  end
         | 
| 107 108 | 
             
                end
         | 
| 108 109 |  | 
| 110 | 
            +
                def parse_option params
         | 
| 111 | 
            +
                  case params.strip
         | 
| 112 | 
            +
                  when /width:\s+(\d+)/
         | 
| 113 | 
            +
                    @width = $1.to_i
         | 
| 114 | 
            +
                    parse_option $~.post_match
         | 
| 115 | 
            +
                  when /cookie:\s+(\S+)/
         | 
| 116 | 
            +
                    check_cookie $1 if $1 != '-'
         | 
| 117 | 
            +
                    parse_option $~.post_match
         | 
| 118 | 
            +
                  when /nonstop: (true|false)/
         | 
| 119 | 
            +
                    @need_pause_at_first = false if $1 == 'true'
         | 
| 120 | 
            +
                    parse_option $~.post_match
         | 
| 121 | 
            +
                  when /(.+):(.+)/
         | 
| 122 | 
            +
                    raise GreetingError, "Unkown option: #{params}"
         | 
| 123 | 
            +
                  else
         | 
| 124 | 
            +
                    # OK
         | 
| 125 | 
            +
                  end
         | 
| 126 | 
            +
                end
         | 
| 127 | 
            +
             | 
| 109 128 | 
             
                def greeting
         | 
| 110 129 | 
             
                  case g = @sock.gets
         | 
| 111 130 | 
             
                  when /^info cookie:\s+(.*)$/
         | 
| @@ -116,16 +135,18 @@ module DEBUGGER__ | |
| 116 135 | 
             
                    @sock.close
         | 
| 117 136 | 
             
                    raise GreetingError, "HEAD request"
         | 
| 118 137 |  | 
| 119 | 
            -
                  when /^version:\s+( | 
| 120 | 
            -
                    v,  | 
| 138 | 
            +
                  when /^version:\s+(\S+)\s+(.+)$/
         | 
| 139 | 
            +
                    v, params = $1, $2
         | 
| 140 | 
            +
             | 
| 121 141 | 
             
                    # TODO: protocol version
         | 
| 122 142 | 
             
                    if v != VERSION
         | 
| 123 143 | 
             
                      raise GreetingError, "Incompatible version (server:#{VERSION} and client:#{$1})"
         | 
| 124 144 | 
             
                    end
         | 
| 145 | 
            +
                    parse_option(params)
         | 
| 125 146 |  | 
| 126 | 
            -
                     | 
| 127 | 
            -
             | 
| 128 | 
            -
                     | 
| 147 | 
            +
                    puts "DEBUGGER (client): Connected. PID:#{Process.pid}, $0:#{$0}"
         | 
| 148 | 
            +
                    puts "DEBUGGER (client): Type `Ctrl-C` to enter the debug console." unless @need_pause_at_first
         | 
| 149 | 
            +
                    puts
         | 
| 129 150 |  | 
| 130 151 | 
             
                  when /^Content-Length: (\d+)/
         | 
| 131 152 | 
             
                    require_relative 'server_dap'
         | 
| @@ -133,12 +154,15 @@ module DEBUGGER__ | |
| 133 154 | 
             
                    raise unless @sock.read(2) == "\r\n"
         | 
| 134 155 | 
             
                    self.extend(UI_DAP)
         | 
| 135 156 | 
             
                    @repl = false
         | 
| 157 | 
            +
                    @need_pause_at_first = false
         | 
| 136 158 | 
             
                    dap_setup @sock.read($1.to_i)
         | 
| 159 | 
            +
             | 
| 137 160 | 
             
                  when /^GET \/.* HTTP\/1.1/
         | 
| 138 161 | 
             
                    require_relative 'server_cdp'
         | 
| 139 162 |  | 
| 140 163 | 
             
                    self.extend(UI_CDP)
         | 
| 141 164 | 
             
                    @repl = false
         | 
| 165 | 
            +
                    @need_pause_at_first = false
         | 
| 142 166 | 
             
                    CONFIG.set_config no_color: true
         | 
| 143 167 |  | 
| 144 168 | 
             
                    @ws_server = UI_CDP::WebSocketServer.new(@sock)
         | 
| @@ -164,6 +188,7 @@ module DEBUGGER__ | |
| 164 188 | 
             
                      }
         | 
| 165 189 | 
             
                    end
         | 
| 166 190 |  | 
| 191 | 
            +
                    return unless line
         | 
| 167 192 | 
             
                    next if line == :can_not_read
         | 
| 168 193 |  | 
| 169 194 | 
             
                    case line
         | 
| @@ -187,7 +212,7 @@ module DEBUGGER__ | |
| 187 212 | 
             
                        raise "pid:#{Process.pid} but get #{line}"
         | 
| 188 213 | 
             
                      end
         | 
| 189 214 | 
             
                    else
         | 
| 190 | 
            -
                      STDERR.puts "unsupported: #{line}"
         | 
| 215 | 
            +
                      STDERR.puts "unsupported: #{line.inspect}"
         | 
| 191 216 | 
             
                      exit!
         | 
| 192 217 | 
             
                    end
         | 
| 193 218 | 
             
                  end
         | 
| @@ -309,7 +334,9 @@ module DEBUGGER__ | |
| 309 334 | 
             
                end
         | 
| 310 335 |  | 
| 311 336 | 
             
                def readline prompt
         | 
| 312 | 
            -
                  input = (sock do |s|
         | 
| 337 | 
            +
                  input = (sock(skip: CONFIG[:skip_bp]) do |s|
         | 
| 338 | 
            +
                    next unless s
         | 
| 339 | 
            +
             | 
| 313 340 | 
             
                    if @repl
         | 
| 314 341 | 
             
                      raise "not in subsession, but received: #{line.inspect}" unless @session.in_subsession?
         | 
| 315 342 | 
             
                      line = "input #{Process.pid}"
         | 
| @@ -353,8 +380,8 @@ module DEBUGGER__ | |
| 353 380 |  | 
| 354 381 | 
             
              class UI_TcpServer < UI_ServerBase
         | 
| 355 382 | 
             
                def initialize host: nil, port: nil
         | 
| 356 | 
            -
                  @ | 
| 357 | 
            -
                  @host = host || CONFIG[:host] | 
| 383 | 
            +
                  @local_addr = nil
         | 
| 384 | 
            +
                  @host = host || CONFIG[:host]
         | 
| 358 385 | 
             
                  @port_save_file = nil
         | 
| 359 386 | 
             
                  @port = begin
         | 
| 360 387 | 
             
                    port_str = (port && port.to_s) || CONFIG[:port] || raise("Specify listening port by RUBY_DEBUG_PORT environment variable.")
         | 
| @@ -375,12 +402,12 @@ module DEBUGGER__ | |
| 375 402 | 
             
                def chrome_setup
         | 
| 376 403 | 
             
                  require_relative 'server_cdp'
         | 
| 377 404 |  | 
| 378 | 
            -
                  unless @chrome_pid = UI_CDP.setup_chrome(@ | 
| 405 | 
            +
                  unless @chrome_pid = UI_CDP.setup_chrome(@local_addr.inspect_sockaddr)
         | 
| 379 406 | 
             
                    DEBUGGER__.warn <<~EOS
         | 
| 380 407 | 
             
                      With Chrome browser, type the following URL in the address-bar:
         | 
| 381 408 |  | 
| 382 | 
            -
                         devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@ | 
| 383 | 
            -
             | 
| 409 | 
            +
                         devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@local_addr.inspect_sockaddr}/#{SecureRandom.uuid}
         | 
| 410 | 
            +
             | 
| 384 411 | 
             
                      EOS
         | 
| 385 412 | 
             
                  end
         | 
| 386 413 | 
             
                end
         | 
| @@ -391,9 +418,9 @@ module DEBUGGER__ | |
| 391 418 |  | 
| 392 419 | 
             
                  begin
         | 
| 393 420 | 
             
                    Socket.tcp_server_sockets @host, @port do |socks|
         | 
| 394 | 
            -
                      @ | 
| 421 | 
            +
                      @local_addr = socks.first.local_address # Change this part if `socks` are multiple.
         | 
| 395 422 | 
             
                      rdbg = File.expand_path('../../exe/rdbg', __dir__)
         | 
| 396 | 
            -
                      DEBUGGER__.warn "Debugger can attach via TCP/IP (#{@ | 
| 423 | 
            +
                      DEBUGGER__.warn "Debugger can attach via TCP/IP (#{@local_addr.inspect_sockaddr})"
         | 
| 397 424 |  | 
| 398 425 | 
             
                      if @port_save_file
         | 
| 399 426 | 
             
                        File.write(@port_save_file, "#{socks[0].local_address.ip_port.to_s}\n")
         | 
| @@ -403,7 +430,7 @@ module DEBUGGER__ | |
| 403 430 | 
             
                      DEBUGGER__.info <<~EOS
         | 
| 404 431 | 
             
                      With rdbg, use the following command line:
         | 
| 405 432 | 
             
                      #
         | 
| 406 | 
            -
                      #   #{rdbg} --attach #{@ | 
| 433 | 
            +
                      #   #{rdbg} --attach #{@local_addr.ip_address} #{@local_addr.ip_port}
         | 
| 407 434 | 
             
                      #
         | 
| 408 435 | 
             
                      EOS
         | 
| 409 436 |  | 
| @@ -411,7 +438,7 @@ module DEBUGGER__ | |
| 411 438 | 
             
                      when 'chrome'
         | 
| 412 439 | 
             
                        chrome_setup
         | 
| 413 440 | 
             
                      when 'vscode'
         | 
| 414 | 
            -
                        vscode_setup @ | 
| 441 | 
            +
                        vscode_setup @local_addr.inspect_sockaddr
         | 
| 415 442 | 
             
                      end
         | 
| 416 443 |  | 
| 417 444 | 
             
                      Socket.accept_loop(socks) do |sock, client|
         | 
    
        data/lib/debug/server_cdp.rb
    CHANGED
    
    | @@ -346,7 +346,7 @@ module DEBUGGER__ | |
| 346 346 | 
             
                      send_event 'Runtime.executionContextCreated',
         | 
| 347 347 | 
             
                                  context: {
         | 
| 348 348 | 
             
                                    id: SecureRandom.hex(16),
         | 
| 349 | 
            -
                                    origin: "http://#{@ | 
| 349 | 
            +
                                    origin: "http://#{@local_addr.inspect_sockaddr}",
         | 
| 350 350 | 
             
                                    name: ''
         | 
| 351 351 | 
             
                                  }
         | 
| 352 352 | 
             
                    when 'Runtime.getIsolateId'
         | 
| @@ -572,13 +572,13 @@ module DEBUGGER__ | |
| 572 572 | 
             
                def process_protocol_request req
         | 
| 573 573 | 
             
                  case req['method']
         | 
| 574 574 | 
             
                  when 'Debugger.stepOver', 'Debugger.stepInto', 'Debugger.stepOut', 'Debugger.resume', 'Debugger.enable'
         | 
| 575 | 
            -
                     | 
| 575 | 
            +
                    request_tc [:cdp, :backtrace, req]
         | 
| 576 576 | 
             
                  when 'Debugger.evaluateOnCallFrame'
         | 
| 577 577 | 
             
                    frame_id = req.dig('params', 'callFrameId')
         | 
| 578 578 | 
             
                    group = req.dig('params', 'objectGroup')
         | 
| 579 579 | 
             
                    if fid = @frame_map[frame_id]
         | 
| 580 580 | 
             
                      expr = req.dig('params', 'expression')
         | 
| 581 | 
            -
                       | 
| 581 | 
            +
                      request_tc [:cdp, :evaluate, req, fid, expr, group]
         | 
| 582 582 | 
             
                    else
         | 
| 583 583 | 
             
                      fail_response req,
         | 
| 584 584 | 
             
                                    code: INVALID_PARAMS,
         | 
| @@ -591,9 +591,9 @@ module DEBUGGER__ | |
| 591 591 | 
             
                      when 'local'
         | 
| 592 592 | 
             
                        frame_id = ref[1]
         | 
| 593 593 | 
             
                        fid = @frame_map[frame_id]
         | 
| 594 | 
            -
                         | 
| 594 | 
            +
                        request_tc [:cdp, :scope, req, fid]
         | 
| 595 595 | 
             
                      when 'properties'
         | 
| 596 | 
            -
                         | 
| 596 | 
            +
                        request_tc [:cdp, :properties, req, oid]
         | 
| 597 597 | 
             
                      when 'script', 'global'
         | 
| 598 598 | 
             
                        # TODO: Support script and global types
         | 
| 599 599 | 
             
                        @ui.respond req, result: []
         |