File: //proc/thread-self/root/usr/lib/ruby/gems/3.2.0/gems/debug-1.7.1/lib/debug/client.rb
# frozen_string_literal: true
require 'socket'
require 'io/console/size'
require_relative 'config'
require_relative 'version'
require_relative 'console'
# $VERBOSE = true
module DEBUGGER__
class CommandLineOptionError < Exception; end
class Client
class << self
def util name
case name
when 'gen-sockpath'
puts DEBUGGER__.create_unix_domain_socket_name
when 'gen-portpath'
port_path = File.join(DEBUGGER__.unix_domain_socket_dir, 'tcp_port')
File.unlink port_path if File.exist?(port_path)
puts port_path
when 'list-socks'
cleanup_unix_domain_sockets
puts list_connections
when 'list-socks-verbose'
cleanup_unix_domain_sockets
puts list_connections verbose: true
when 'setup-autoload'
setup_autoload
else
abort "Unknown utility: #{name}"
end
end
def working_shell_type
shell = `ps -p #{Process.ppid} -o 'args='`
case shell
when /bash/
:bash
when /fish/
:fish
when /csh/
:csh
when /zsh/
:zsh
when /dash/
:dash
else
:unknown
end
end
def setup_autoload
prelude_path = File.join(__dir__, 'prelude.rb')
case shell = working_shell_type
when :bash, :zsh
puts <<~EOS
# add the following lines in your ~/.#{shell}_profile
if test -s #{prelude_path} ; then
export RUBYOPT='-r #{prelude_path}'
fi
# Add `Kernel#bb` method which is alias of `Kernel#debugger`
# export RUBY_DEBUG_BB=1
EOS
when :fish
puts <<~EOS
# add the following lines in your ~/.config/fish/config.fish
set -x RUBYOPT "-r #{__dir__}/prelude" $RUBYOPT
EOS
else
puts "# Sorry that your shell is not supported yet.",
"# Please use the content in #{prelude_path} as a reference and modify your login script accordingly."
end
end
def cleanup_unix_domain_sockets
Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*') do |file|
if File.socket?(file) && (/-(\d+)-\d+$/ =~ file || /-(\d+)$/ =~ file)
begin
Process.kill(0, $1.to_i)
rescue Errno::EPERM
rescue Errno::ESRCH
File.unlink(file)
end
end
end
end
def list_connections verbose: false
socks = Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*').find_all do |path|
File.socket?(path)
end
if verbose
socks = socks.map{|sock_path|
Socket.unix(sock_path){|sock|
sock.puts "info cookie: #{CONFIG[:cookie] || '-'}"
pid = sock.gets.chomp
_dbg = sock.gets.chomp
_unm = sock.gets.chomp
[sock_path, pid]
}
}
end
socks
end
end
def initialize argv
@multi_process = false
@pid = nil
@console = Console.new
case argv.size
when 0
connect_unix
when 1
if /\A\d+\z/ =~ (arg = argv.shift.strip)
connect_tcp nil, arg.to_i
else
connect_unix arg
end
when 2
connect_tcp argv[0], argv[1]
else
raise CommandLineOptionError
end
@width = IO.console_size[1]
@width = 80 if @width == 0
send "version: #{VERSION} " +
"width: #{@width} " +
"cookie: #{CONFIG[:cookie] || '-'} " +
"nonstop: #{CONFIG[:nonstop] ? 'true' : 'false'}"
end
def deactivate
@console.deactivate if @console
end
def readline
if @multi_process
@console.readline "(rdbg:remote\##{@pid}) "
else
@console.readline "(rdbg:remote) "
end
end
def connect_unix name = nil
if name
if File.exist? name
@s = Socket.unix(name)
else
@s = Socket.unix(File.join(DEBUGGER__.unix_domain_socket_dir, name))
end
else
Client.cleanup_unix_domain_sockets
files = Client.list_connections verbose: true
case files.size
when 0
$stderr.puts "No debug session is available."
exit
when 1
@s = Socket.unix(files.first.first)
else
$stderr.puts "Please select a debug session:"
files.each{|(f, desc)|
$stderr.puts " #{File.basename(f)} (#{desc})"
}
exit
end
end
end
def connect_tcp host, port
@s = Socket.tcp(host, port)
end
def send msg
p send: msg if $VERBOSE
@s.puts msg
end
def connect
pre_commands = (CONFIG[:commands] || '').split(';;')
trap(:SIGINT){
send "pause"
}
begin
trap(:SIGWINCH){
@width = IO.console_size[1]
}
rescue ArgumentError
@width = 80
end
while line = @s.gets
p recv: line if $VERBOSE
case line
when /^out (.*)/
puts "#{$1}"
when /^input (.+)/
pid = $1
@multi_process = true if @pid && @pid != pid
@pid = pid
prev_trap = trap(:SIGINT, 'DEFAULT')
begin
if pre_commands.empty?
line = readline
else
line = pre_commands.shift
puts "(rdbg:remote:command) #{line}"
end
rescue Interrupt
retry
ensure
trap(:SIGINT, prev_trap)
end
line = (line || 'quit').strip
send "command #{pid} #{@width} #{line}"
when /^ask (\d+) (.*)/
pid = $1
print $2
send "answer #{pid} #{$stdin.gets || ''}"
when /^quit/
raise 'quit'
else
puts "(unknown) #{line.inspect}"
end
end
rescue => e
STDERR.puts "disconnected (#{e})"
exit
ensure
deactivate
end
end
end
if __FILE__ == $0
DEBUGGER__::Client.new(argv).connect
end