class DEBUGGER__::UI_ServerBase

Constants

TRAP_SIGNAL

maybe Windows?

Attributes

reader_thread[R]

Public Class Methods

new() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 10
def initialize
  @sock = @sock_for_fork = nil
  @accept_m = Mutex.new
  @accept_cv = ConditionVariable.new
  @client_addr = nil
  @q_msg = nil
  @q_ans = nil
  @unsent_messages = []
  @width = 80
  @repl = true
  @session = nil
end

Public Instance Methods

accept() { |sock_for_fork, already_connected: true| ... } click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 31
def accept
  if @sock_for_fork
    begin
      yield @sock_for_fork, already_connected: true
    ensure
      @sock_for_fork.close
      @sock_for_fork = nil
    end
  end
end
activate(session, on_fork: false) click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 42
def activate session, on_fork: false
  @session = session
  @reader_thread = Thread.new do
    # An error on this thread should break the system.
    Thread.current.abort_on_exception = true
    Thread.current.name = 'DEBUGGER__::Server::reader'

    accept do |server, already_connected: false|
      DEBUGGER__.warn "Connected."
      greeting_done = false
      @need_pause_at_first = true

      @accept_m.synchronize{
        @sock = server
        greeting
        greeting_done = true

        @accept_cv.signal

        # flush unsent messages
        @unsent_messages.each{|m|
          @sock.puts m
        } if @repl
        @unsent_messages.clear

        @q_msg = Queue.new
        @q_ans = Queue.new
      } unless already_connected

      setup_interrupt do
        pause if !already_connected && @need_pause_at_first
        process
      end

    rescue GreetingError => e
      DEBUGGER__.warn "GreetingError: #{e.message}"
      next
    rescue Terminate
      raise # should catch at outer scope
    rescue => e
      DEBUGGER__.warn "ReaderThreadError: #{e}"
      pp e.backtrace
    ensure
      DEBUGGER__.warn "Disconnected."
      cleanup_reader if greeting_done
    end # accept

  rescue Terminate
    # ignore
  end
end
after_fork_parent() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 371
def after_fork_parent
  # do nothing
end
ask(prompt) click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 307
def ask prompt
  sock do |s|
    s.puts "ask #{Process.pid} #{prompt}"
    @q_ans.pop
  end
end
cleanup_reader() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 94
def cleanup_reader
  @sock.close if @sock
  @sock = nil
  @q_msg.close
  @q_msg = nil
  @q_ans.close
  @q_ans = nil
end
deactivate() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 26
def deactivate
  @reader_thread.raise Terminate
  @reader_thread.join
end
greeting() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 128
def greeting
  case g = @sock.gets
  when /^info cookie:\s+(.*)$/
    check_cookie $1
    @sock.puts "PID: #{Process.pid}, $0: #{$0}"
    @sock.puts "debug #{VERSION} on #{RUBY_DESCRIPTION}"
    @sock.puts "uname: #{Etc.uname.inspect}"
    @sock.close
    raise GreetingError, "HEAD request"

  when /^version:\s+(\S+)\s+(.+)$/
    v, params = $1, $2

    # TODO: protocol version
    if v != VERSION
      raise GreetingError, "Incompatible version (server:#{VERSION} and client:#{$1})"
    end
    parse_option(params)

    puts "DEBUGGER (client): Connected. PID:#{Process.pid}, $0:#{$0}"
    puts "DEBUGGER (client): Type `Ctrl-C` to enter the debug console." unless @need_pause_at_first
    puts

  when /^Content-Length: (\d+)/
    require_relative 'server_dap'

    raise unless @sock.read(2) == "\r\n"
    self.extend(UI_DAP)
    @repl = false
    @need_pause_at_first = false
    dap_setup @sock.read($1.to_i)

  when /^GET \/.* HTTP\/1.1/
    require_relative 'server_cdp'

    self.extend(UI_CDP)
    @repl = false
    @need_pause_at_first = false
    CONFIG.set_config no_color: true

    @ws_server = UI_CDP::WebSocketServer.new(@sock)
    @ws_server.handshake
  else
    raise GreetingError, "Unknown greeting message: #{g}"
  end
end
parse_option(params) click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 110
def parse_option params
  case params.strip
  when /width:\s+(\d+)/
    @width = $1.to_i
    parse_option $~.post_match
  when /cookie:\s+(\S+)/
    check_cookie $1 if $1 != '-'
    parse_option $~.post_match
  when /nonstop: (true|false)/
    @need_pause_at_first = false if $1 == 'true'
    parse_option $~.post_match
  when /(.+):(.+)/
    raise GreetingError, "Unkown option: #{params}"
  else
    # OK
  end
end
pause() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 359
def pause
  # $stderr.puts "DEBUG: pause request"
  Process.kill(TRAP_SIGNAL, Process.pid)
end
process() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 175
def process
  while true
    DEBUGGER__.info "sleep IO.select"
    r = IO.select([@sock])
    DEBUGGER__.info "wakeup IO.select"

    line = @session.process_group.sync do
      unless IO.select([@sock], nil, nil, 0)
        DEBUGGER__.info "UI_Server can not read"
        break :can_not_read
      end
      @sock.gets&.chomp.tap{|line|
        DEBUGGER__.info "UI_Server received: #{line}"
      }
    end

    return unless line
    next if line == :can_not_read

    case line
    when /\Apause/
      pause
    when /\Acommand (\d+) (\d+) ?(.+)/
      raise "not in subsession, but received: #{line.inspect}" unless @session.in_subsession?

      if $1.to_i == Process.pid
        @width = $2.to_i
        @q_msg << $3
      else
        raise "pid:#{Process.pid} but get #{line}"
      end
    when /\Aanswer (\d+) (.*)/
      raise "not in subsession, but received: #{line.inspect}" unless @session.in_subsession?

      if $1.to_i == Process.pid
        @q_ans << $2
      else
        raise "pid:#{Process.pid} but get #{line}"
      end
    else
      STDERR.puts "unsupported: #{line.inspect}"
      exit!
    end
  end
end
puts(str = nil) click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 314
def puts str = nil
  case str
  when Array
    enum = str.each
  when String
    enum = str.each_line
  when nil
    enum = [''].each
  end

  sock skip: true do |s|
    enum.each do |line|
      msg = "out #{line.chomp}"
      if s
        s.puts msg
      else
        @unsent_messages << msg
      end
    end
  end
end
quit(n) click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 364
def quit n
  # ignore n
  sock do |s|
    s.puts "quit"
  end
end
readline(prompt) click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 336
def readline prompt
  input = (sock(skip: CONFIG[:skip_bp]) do |s|
    next unless s

    if @repl
      raise "not in subsession, but received: #{line.inspect}" unless @session.in_subsession?
      line = "input #{Process.pid}"
      DEBUGGER__.info "send: #{line}"
      s.puts line
    end
    sleep 0.01 until @q_msg
    @q_msg.pop.tap{|msg|
      DEBUGGER__.info "readline: #{msg.inspect}"
    }
  end || 'continue')

  if input.is_a?(String)
    input.strip
  else
    input
  end
end
remote?() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 221
def remote?
  true
end
setup_interrupt() { || ... } click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 253
def setup_interrupt
  prev_handler = trap(TRAP_SIGNAL) do
    # $stderr.puts "trapped SIGINT"
    ThreadClient.current.on_trap TRAP_SIGNAL

    case prev_handler
    when Proc
      prev_handler.call
    else
      # ignore
    end
  end

  if sigurg_overridden?(prev_handler)
    DEBUGGER__.warn "SIGURG handler is overridden by the debugger."
  end
  yield
ensure
  trap(TRAP_SIGNAL, prev_handler)
end
sigurg_overridden?(prev_handler) click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 229
def sigurg_overridden? prev_handler
  case prev_handler
  when "SYSTEM_DEFAULT", "DEFAULT"
    false
  when Proc
    if prev_handler.source_location[0] == __FILE__
      false
    else
      true
    end
  else
    true
  end
end
sock(skip: false) { |nil| ... } click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 278
def sock skip: false
  if s = @sock         # already connection
    # ok
  elsif skip == true   # skip process
    no_sock = true
    r = @accept_m.synchronize do
      if @sock
        no_sock = false
      else
        yield nil
      end
    end
    return r if no_sock
  else                 # wait for connection
    until s = @sock
      @accept_m.synchronize{
        unless @sock
          DEBUGGER__.warn "wait for debugger connection..."
          @accept_cv.wait(@accept_m)
        end
      }
    end
  end

  yield s
rescue Errno::EPIPE
  # ignore
end
vscode_setup(debug_port) click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 375
def vscode_setup debug_port
  require_relative 'server_dap'
  UI_DAP.setup debug_port
end
width() click to toggle source
# File debug-1.6.3/lib/debug/server.rb, line 225
def width
  @width
end