Created
September 23, 2009 16:41
-
-
Save timcharper/192105 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 | |
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