Class: DEBUGGER__::UI_ServerBase
Relationships & Source Files | |
Namespace Children | |
Exceptions:
| |
Extension / Inclusion / Inheritance Descendants | |
Subclasses:
|
|
Super Chains via Extension / Inclusion / Inheritance | |
Class Chain:
self,
UI_Base
|
|
Instance Chain:
self,
UI_Base
|
|
Inherits: |
DEBUGGER__::UI_Base
|
Defined in: | lib/debug/server.rb |
Class Method Summary
- .new ⇒ UI_ServerBase constructor
Instance Attribute Summary
Instance Method Summary
- #accept
- #activate(session, on_fork: false)
- #after_fork_parent
- #ask(prompt)
- #check_cookie(c)
- #cleanup_reader
- #deactivate
- #greeting
- #parse_option(params)
- #pause
- #process
- #puts(str = nil)
- #quit(n, &_b)
- #readline(prompt)
- #setup_interrupt
- #sigurg_overridden?(prev_handler) ⇒ Boolean
- #sock(skip: false)
- #vscode_setup(debug_port)
- #width
UI_Base
- Inherited
Constructor Details
.new ⇒ UI_ServerBase
# File 'lib/debug/server.rb', line 9
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
Instance Attribute Details
#reader_thread (readonly)
[ GitHub ]# File 'lib/debug/server.rb', line 276
attr_reader :reader_thread
#remote? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/debug/server.rb', line 223
def remote? true end
Instance Method Details
#accept
[ GitHub ]# File '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)
[ GitHub ]# File '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.}" next rescue Terminate raise # should catch at outer scope rescue RetryConnection next 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
[ GitHub ]# File 'lib/debug/server.rb', line 373
def after_fork_parent # do nothing end
#ask(prompt)
[ GitHub ]#check_cookie(c)
[ GitHub ]# File 'lib/debug/server.rb', line 105
def c = CONFIG[: ] if && != c raise GreetingError, "Cookie mismatch (#{$2.inspect} was sent)" end end
#cleanup_reader
[ GitHub ]# File 'lib/debug/server.rb', line 96
def cleanup_reader @sock.close if @sock @sock = nil @q_msg.close @q_msg = nil @q_ans.close @q_ans = nil end
#deactivate
[ GitHub ]# File 'lib/debug/server.rb', line 26
def deactivate @reader_thread.raise Terminate @reader_thread.join end
#greeting
[ GitHub ]# File 'lib/debug/server.rb', line 130
def greeting case g = @sock.gets when /^info cookie:\s+(.*)$/ require 'etc' $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 @sock.puts msg = "out DEBUGGER: Incompatible version (server:#{VERSION} and client:#{$1})" raise GreetingError, msg 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\s\/json\sHTTP\/1.1/, /^GET\s\/json\/version\sHTTP\/1.1/, /^GET\s\/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\sHTTP\/1.1/ # The reason for not using @uuid here is @uuid is nil if users run debugger without `--open=chrome`. require_relative 'server_cdp' self.extend(UI_CDP) send_chrome_response g else raise GreetingError, "Unknown greeting message: #{g}" end end
#parse_option(params)
[ GitHub ]# File 'lib/debug/server.rb', line 112
def parse_option params case params.strip when /width:\s(\d)/ @width = $1.to_i parse_option $~.post_match when /cookie:\s(\S)/ $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
[ GitHub ]#process
[ GitHub ]# File 'lib/debug/server.rb', line 177
def process while true DEBUGGER__.debug{ "sleep IO.select" } _r = IO.select([@sock]) DEBUGGER__.debug{ "wakeup IO.select" } line = @session.process_group.sync do unless IO.select([@sock], nil, nil, 0) DEBUGGER__.debug{ "UI_Server can not read" } break :can_not_read end @sock.gets&.chomp.tap{|line| DEBUGGER__.debug{ "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)
[ GitHub ]# File 'lib/debug/server.rb', line 316
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, &_b)
[ GitHub ]#readline(prompt)
[ GitHub ]# File 'lib/debug/server.rb', line 338
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__.debug{ "send: #{line}" } s.puts line end sleep 0.01 until @q_msg @q_msg.pop.tap{|msg| DEBUGGER__.debug{ "readline: #{msg.inspect}" } } end || 'continue') if input.is_a?(String) input.strip else input end end
#setup_interrupt
[ GitHub ]# File 'lib/debug/server.rb', line 255
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) ⇒ Boolean
# File 'lib/debug/server.rb', line 231
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)
[ GitHub ]# File 'lib/debug/server.rb', line 280
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)
[ GitHub ]#width
[ GitHub ]# File 'lib/debug/server.rb', line 227
def width @width end