class DEBUGGER__::Config

Experimental RBS support (using type sampling data from the type_fusion project).

# sig/debug/config.rbs

class DEBUGGER__::Config
  def []: (Symbol key) -> Symbol
end

def self.config

def self.config
  @config
end

def self.config_to_env_hash config

def self.config_to_env_hash config
  CONFIG_MAP.each_with_object({}){|(key, evname), env|
    unless config[key].nil?
      case CONFIG_SET[key][2]
      when :path
        valstr = config[key].map{|e| e.kind_of?(Regexp) ? e.inspect : e}.join(':')
      when :path_map
        valstr = config[key].map{|e| e.join(':')}.join(',')
      else
        valstr = config[key].to_s
      end
      env[evname] = valstr
    end
  }
end

def self.parse_argv argv

def self.parse_argv argv
  config = {
    mode: :start,
    no_color: (nc = ENV['NO_COLOR']) && !nc.empty?,
  }
  CONFIG_MAP.each{|key, evname|
    if val = ENV[evname]
      config[key] = parse_config_value(key, val)
    end
  }
  return config if !argv || argv.empty?
  if argv.kind_of? String
    require 'shellwords'
    argv = Shellwords.split(argv)
  end
  require 'optparse'
  require_relative 'version'
  have_shown_version = false
  opt = OptionParser.new do |o|
    o.banner = "#{$0} [options] -- [debuggee options]"
    o.separator ''
    o.version = ::DEBUGGER__::VERSION
    o.separator 'Debug console mode:'
    o.on('-n', '--nonstop', 'Do not stop at the beginning of the script.') do
      config[:nonstop] = '1'
    end
    o.on('-e DEBUG_COMMAND', 'Execute debug command at the beginning of the script.') do |cmd|
      config[:commands] ||= ''
      config[:commands] += cmd + ';;'
    end
    o.on('-x FILE', '--init-script=FILE', 'Execute debug command in the FILE.') do |file|
      config[:init_script] = file
    end
    o.on('--no-rc', 'Ignore ~/.rdbgrc') do
      config[:no_rc] = true
    end
    o.on('--no-color', 'Disable colorize') do
      config[:no_color] = true
    end
    o.on('--no-sigint-hook', 'Disable to trap SIGINT') do
      config[:no_sigint_hook] = true
    end
    o.on('-c', '--command', 'Enable command mode.',
                            'The first argument should be a command name in $PATH.',
                            'Example: \'rdbg -c bundle exec rake test\'') do
      config[:command] = true
    end
    o.separator ''
    o.on('-O', '--open=[FRONTEND]', 'Start remote debugging with opening the network port.',
                                    'If TCP/IP options are not given, a UNIX domain socket will be used.',
                                    'If FRONTEND is given, prepare for the FRONTEND.',
                                    'Now rdbg, vscode and chrome is supported.') do |f|
      case f # some format patterns are not documented yet
      when nil
        config[:open] = true
      when /\A\d\z/
        config[:open] = true
        config[:port] = f.to_i
      when /\A(\S+):(\d+)\z/
        config[:open] = true
        config[:host] = $1
        config[:port] = $2.to_i
      when 'tcp'
        config[:open] = true
        config[:port] ||= 0
      when 'vscode', 'chrome', 'cdp'
        config[:open] = f&.downcase
      else
        raise "Unknown option for --open: #{f}"
      end
    end
    o.on('--sock-path=SOCK_PATH', 'UNIX Domain socket path') do |path|
      config[:sock_path] = path
    end
    o.on('--port=PORT', 'Listening TCP/IP port') do |port|
      config[:port] = port
    end
    o.on('--host=HOST', 'Listening TCP/IP host') do |host|
      config[:host] = host
    end
    o.on('--cookie=COOKIE', 'Set a cookie for connection') do |c|
      config[:cookie] = c
    end
    o.on('--session-name=NAME', 'Session name') do |name|
      config[:session_name] = name
    end
    rdbg = 'rdbg'
    o.separator ''
    o.separator '  Debug console mode runs Ruby program with the debug console.'
    o.separator ''
    o.separator "  '#{rdbg} target.rb foo bar'                starts like 'ruby target.rb foo bar'."
    o.separator "  '#{rdbg} -- -r foo -e bar'                 starts like 'ruby -r foo -e bar'."
    o.separator "  '#{rdbg} -c rake test'                     starts like 'rake test'."
    o.separator "  '#{rdbg} -c -- rake test -t'               starts like 'rake test -t'."
    o.separator "  '#{rdbg} -c bundle exec rake test'         starts like 'bundle exec rake test'."
    o.separator "  '#{rdbg} -O target.rb foo bar'             starts and accepts attaching with UNIX domain socket."
    o.separator "  '#{rdbg} -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234."
    o.separator "  '#{rdbg} -O --port 1234 -- -r foo -e bar'  starts accepts attaching with TCP/IP localhost:1234."
    o.separator "  '#{rdbg} target.rb -O chrome --port 1234'  starts and accepts connecting from Chrome Devtools with localhost:1234."
    o.separator ''
    o.separator 'Attach mode:'
    o.on('-A', '--attach', 'Attach to debuggee process.') do
      config[:mode] = :attach
    end
    o.separator ''
    o.separator '  Attach mode attaches the remote debug console to the debuggee process.'
    o.separator ''
    o.separator "  '#{rdbg} -A'           tries to connect via UNIX domain socket."
    o.separator "  #{' ' * rdbg.size}                If there are multiple processes are waiting for the"
    o.separator "  #{' ' * rdbg.size}                debugger connection, list possible debuggee names."
    o.separator "  '#{rdbg} -A path'      tries to connect via UNIX domain socket with given path name."
    o.separator "  '#{rdbg} -A port'      tries to connect to localhost:port via TCP/IP."
    o.separator "  '#{rdbg} -A host port' tries to connect to host:port via TCP/IP."
    o.separator ''
    o.separator 'Other options:'
    o.on('-v', 'Show version number') do
      puts o.ver
      have_shown_version = true
    end
    o.on('--version', 'Show version number and exit') do
      puts o.ver
      exit
    end
    o.on("-h", "--help", "Print help") do
      puts o
      exit
    end
    o.on('--util=NAME', 'Utility mode (used by tools)') do |name|
      require_relative 'client'
      Client.util(name)
      exit
    end
    o.on('--stop-at-load', 'Stop immediately when the debugging feature is loaded.') do
      config[:stop_at_load] = true
    end
    o.separator ''
    o.separator 'NOTE'
    o.separator '  All messages communicated between a debugger and a debuggee are *NOT* encrypted.'
    o.separator '  Please use the remote debugging feature carefully.'
  end
  opt.parse!(argv)
  if argv.empty?
    case
    when have_shown_version && config[:mode] == :start
      exit
    end
  end
  config
end

def self.parse_config_value name, valstr

def self.parse_config_value name, valstr
  return valstr unless valstr.kind_of? String
  case CONFIG_SET[name][2]
  when :bool
    case valstr
    when '1', 'true', 'TRUE', 'T'
      true
    else
      false
    end
  when :int
    valstr.to_i
  when :loglevel
    if DEBUGGER__::LOG_LEVELS[s = valstr.to_sym]
      s
    else
      raise "Unknown loglevel: #{valstr}"
    end
  when :forkmode
    case sym = valstr.to_sym
    when :parent, :child, :both, nil
      sym
    else
      raise "unknown fork mode: #{sym}"
    end
  when :path # array of String
    valstr.split(/:/).map{|e|
      if /\A\/(.+)\/\z/ =~ e
        Regexp.compile $1
      else
        e
      end
    }
  when :path_map
    valstr.split(',').map{|e| e.split(':')}
  else
    valstr
  end
end

def [](key)

Experimental RBS support (using type sampling data from the type_fusion project).

def []: (Symbol key) -> Symbol

This signature was generated using 2 samples from 1 application.

def [](key)
  config[key]
end

def []=(key, val)

def []=(key, val)
  set_config(key => val)
end

def append_config key, val

def append_config key, val
  conf = config.dup
  if CONFIG_SET[key]
    if CONFIG_SET[key][2] == :path
      conf[key] = [*conf[key], *parse_config_value(key, val)];
    else
      raise "not an Array type: #{key}"
    end
  else
    raise "Unknown configuration: #{key}"
  end
  update conf
end

def config

def config
lass.config

def disable_sigdump old_sig

def disable_sigdump old_sig
ld_sig, @sigdump_sig_prev)
mp_sig_prev = nil

def enable_sigdump sig

def enable_sigdump sig
mp_sig_prev = trap(sig) do
= []
<< "Simple sigdump on #{Process.pid}"
ad.list.each{|th|
r << "Thread: #{th}"
.backtrace.each{|loc|
str << "  #{loc}"
r << ''
RR.puts str

def if_updated old_conf, new_conf, key

def if_updated old_conf, new_conf, key
ew = old_conf[key], new_conf[key]
old, new if old != new

def initialize argv

def initialize argv
  if self.class.config
    raise 'Can not make multiple configurations in one process'
  end
  config = self.class.parse_argv(argv)
  # apply defaults
  CONFIG_SET.each do |k, config_detail|
    unless config.key?(k)
      default_value = config_detail[3]
      config[k] = parse_config_value(k, default_value)
    end
  end
  update config
end

def inspect

def inspect
  config.inspect
end

def parse_config_value name, valstr

def parse_config_value name, valstr
lass.parse_config_value name, valstr

def set_config(**kw)

def set_config(**kw)
  conf = config.dup
  kw.each{|k, v|
    if CONFIG_MAP[k]
      conf[k] = parse_config_value(k, v) # TODO: ractor support
    else
      raise "Unknown configuration: #{k}"
    end
  }
  update conf
end

def setup_sigdump old_sig = nil, sig = CONFIG[:sigdump_sig]

Use `sigdump` gem for more rich features.
emergency simple sigdump.
def setup_sigdump old_sig = nil, sig = CONFIG[:sigdump_sig]
d_sig && sig
le_sigdump sig
old_sig && !sig
ble_sigdump old_sig
old_sig && sig
ble_sigdump old_sig
le_sigdump sig

def update conf

def update conf
  old_conf = self.class.instance_variable_get(:@config) || {}
  # TODO: Use Ractor.make_shareable(conf)
  self.class.instance_variable_set(:@config, conf.freeze)
  # Post process
  if_updated old_conf, conf, :keep_alloc_site do |old, new|
    if new
      require 'objspace'
      ObjectSpace.trace_object_allocations_start
    end
    if old && !new
      ObjectSpace.trace_object_allocations_stop
    end
  end
  if_updated old_conf, conf, :postmortem do |_, new_p|
    if defined?(SESSION)
      SESSION.postmortem = new_p
    end
  end
  if_updated old_conf, conf, :sigdump_sig do |old_sig, new_sig|
    setup_sigdump old_sig, new_sig
  end
  if_updated old_conf, conf, :no_sigint_hook do |old, new|
    if defined?(SESSION)
      SESSION.set_no_sigint_hook old, new
    end
  end
end