Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
require 'ruby-debug'
require 'socket'
require 'forwardable'
class SporkDebugger
DEFAULT_PORT = 10_123
HOST = '127.0.0.1'
extend Forwardable
def_delegators :state, *[:prepare_debugger, :initialize]
attr_reader :state
class << self
attr_reader :instance
def run
@instance = new
end
end
def initialize
@state = SporkDebugger::PreloadState.new
Spork.send(:each_run_procs).unshift(lambda do
@state = @state.transition_to_each_run_state
end)
end
module NetworkHelpers
def find_port(starting_with)
port = starting_with
begin
server = TCPServer.new(HOST, port)
server.close
rescue Errno::EADDRINUSE
port += 1
retry
end
port
end
end
class PreloadState
include NetworkHelpers
def initialize
install_hook
listen_for_connection_signals
end
def finish
@tcp_service.close; @tcp_service = nil;
end
def transition_to_each_run_state
finish
SporkDebugger::EachRunState.new(@port)
end
protected
def install_hook
Kernel.class_eval do
alias :debugger_without_spork_hook :debugger
def debugger(steps = 1)
SporkDebugger.instance.prepare_debugger
debugger_without_spork_hook
end
end
end
def listen_for_connection_signals
@port = SporkDebugger::DEFAULT_PORT
begin
@tcp_service = TCPServer.new(SporkDebugger::HOST, @port)
rescue Errno::EADDRINUSE
@port += 1
retry
end
Thread.new { main_loop }
end
def main_loop
while @tcp_service do
socket = @tcp_service.accept
port = Marshal.load(socket)
Marshal.dump(true, socket)
connect_rdebug_client(port)
socket.close
end
rescue => e
puts "#{$$} - error: #{e}"
end
def connect_rdebug_client(port = Debugger::PORT)
Debugger.start_client(SporkDebugger::HOST, port)
puts "- Process terminated -"
end
end
class EachRunState
include NetworkHelpers
def initialize(connection_request_port)
@connection_request_port = connection_request_port
end
def prepare_debugger
port, cport = start_rdebug_server
signal_spork_server_to_connect_to_rdebug_server(port)
wait_for_connection
end
def signal_spork_server_to_connect_to_rdebug_server(rdebug_server_port)
socket = TCPSocket.new(SporkDebugger::HOST, @connection_request_port)
Marshal.dump(rdebug_server_port, socket)
response = Marshal.load(socket)
socket.close
end
def start_rdebug_server
Debugger.stop rescue nil
port = find_port(Debugger::PORT)
cport = find_port(port + 1)
Debugger.start_remote(SporkDebugger::HOST, [port, cport]) do
Debugger.run_init_script(StringIO.new)
end
[port, cport]
end
protected
def wait_for_connection
while Debugger.handler.interface.nil?; sleep 0.10; end
end
end
end
Spork.prefork do
SporkDebugger.run
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.