| 1 |
require 'socket' |
| 2 |
module RubyAMP |
| 3 |
class RemoteDebugger |
| 4 |
RUN_FILE = "/tmp/set_breakpoint.rb" |
| 5 |
|
| 6 |
class << self |
| 7 |
attr_reader :not_running |
| 8 |
def socket(retries=1) |
| 9 |
return @socket if @socket |
| 10 |
tryCount = 0 |
| 11 |
return if @not_running |
| 12 |
|
| 13 |
begin |
| 14 |
@socket = TCPSocket.new('localhost', 8990) |
| 15 |
rescue Errno::ECONNREFUSED |
| 16 |
sleep(0.10) and retry if (tryCount+=1) < retries |
| 17 |
|
| 18 |
puts "Debugger is not running." |
| 19 |
@not_running = true |
| 20 |
end |
| 21 |
end |
| 22 |
|
| 23 |
def connected? |
| 24 |
@socket ? true : false |
| 25 |
end |
| 26 |
|
| 27 |
def disconnect |
| 28 |
if connected? |
| 29 |
socket.close |
| 30 |
@socket = nil |
| 31 |
end |
| 32 |
true |
| 33 |
end |
| 34 |
|
| 35 |
def prepare_debug_wrapper(commands) |
| 36 |
|
| 37 |
File.open(RUN_FILE, 'wb') do |f| |
| 38 |
f.puts <<-EOF |
| 39 |
require #{File.join(ENV['TM_BUNDLE_SUPPORT'], '/ext/debugger_extension.rb').inspect} |
| 40 |
Debugger.start |
| 41 |
Debugger.settings[:autoeval]=1 |
| 42 |
Debugger.settings[:autolist]=1 |
| 43 |
Debugger.add_breakpoint #{ENV['TM_FILEPATH'].to_s.inspect}, #{ENV['TM_LINE_NUMBER']} |
| 44 |
|
| 45 |
Debugger.wait_for_connection |
| 46 |
#{commands} |
| 47 |
EOF |
| 48 |
end |
| 49 |
RUN_FILE |
| 50 |
end |
| 51 |
end |
| 52 |
|
| 53 |
def initialize(retries = 1) |
| 54 |
socket(retries) |
| 55 |
end |
| 56 |
|
| 57 |
def socket(retries = 1); self.class.socket(retries) end |
| 58 |
def connected?; self.class.connected? end |
| 59 |
|
| 60 |
def send_command(cmd, msg = nil) |
| 61 |
return if self.class.not_running |
| 62 |
begin |
| 63 |
@first_output ||= socket.gets |
| 64 |
socket.puts cmd |
| 65 |
puts msg if msg |
| 66 |
rescue Exception |
| 67 |
puts "Error: #{$!.class}" |
| 68 |
end |
| 69 |
end |
| 70 |
|
| 71 |
def read_output |
| 72 |
return if self.class.not_running |
| 73 |
result = "" |
| 74 |
while line = socket.gets |
| 75 |
break if line =~ /^PROMPT/ |
| 76 |
result << line |
| 77 |
end |
| 78 |
result |
| 79 |
rescue Exception |
| 80 |
puts "Error: #{$!.class}" |
| 81 |
end |
| 82 |
|
| 83 |
def command(cmd) |
| 84 |
send_command(cmd) |
| 85 |
read_output |
| 86 |
end |
| 87 |
|
| 88 |
def evaluate(cmd, binding = :current, format = :raw) |
| 89 |
command("e require #{File.join(ENV['TM_BUNDLE_SUPPORT'], '/ext/debugger_extension.rb').inspect}") |
| 90 |
o = command("e Debugger.evaluate(#{cmd.inspect}, :#{binding}, :#{format})") |
| 91 |
eval(o) |
| 92 |
rescue Exception |
| 93 |
o |
| 94 |
end |
| 95 |
|
| 96 |
def current_frame |
| 97 |
evaluate("::Debugger.current_frame", :control) |
| 98 |
end |
| 99 |
|
| 100 |
AUTO_LOAD = { |
| 101 |
:BreakpointCommander => 'breakpoint_commander.rb', |
| 102 |
:CommanderBase => 'commander_base.rb', |
| 103 |
} |
| 104 |
|
| 105 |
def self.const_missing(name) |
| 106 |
@looked_for ||= {} |
| 107 |
raise "Class not found: #{name}" if @looked_for[name] |
| 108 |
|
| 109 |
return super unless AUTO_LOAD[name] |
| 110 |
@looked_for[name] = true |
| 111 |
|
| 112 |
require File.join(RUBYAMP_ROOT, "remote_debugger", AUTO_LOAD[name]) |
| 113 |
const_get(name) |
| 114 |
end |
| 115 |
|
| 116 |
def breakpoint |
| 117 |
@breakpoint ||= BreakpointCommander.new(self) |
| 118 |
end |
| 119 |
end |
| 120 |
end |
| 121 |
|
| 122 |
at_exit { RubyAMP::RemoteDebugger.disconnect } |