trepanning 0.0.9 → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/ChangeLog +237 -0
- data/NEWS +8 -0
- data/Rakefile +3 -2
- data/app/breakpoint.rb +8 -8
- data/app/brkptmgr.rb +86 -85
- data/app/client.rb +60 -0
- data/app/core.rb-consider +198 -0
- data/app/default.rb +17 -7
- data/app/disassemble.rb +12 -2
- data/app/options.rb +51 -20
- data/app/yarv.rb +183 -0
- data/bin/trepan +15 -9
- data/data/custom_require.rb +44 -0
- data/data/irbrc +41 -0
- data/data/prelude.rb +38 -0
- data/interface/base_intf.rb +10 -4
- data/interface/client.rb +79 -0
- data/interface/comcodes.rb +18 -0
- data/interface/server.rb +142 -0
- data/io/base_io.rb +57 -3
- data/io/tcpclient.rb +122 -0
- data/io/tcpfns.rb +31 -0
- data/io/tcpserver.rb +137 -0
- data/lib/trepanning.rb +57 -28
- data/processor/command/base/cmd.rb +10 -6
- data/processor/command/base/subcmd.rb +13 -1
- data/processor/command/directory.rb +15 -8
- data/processor/command/disassemble.rb +3 -2
- data/processor/command/help.rb +71 -19
- data/processor/command/info_subcmd/args.rb +2 -3
- data/processor/command/info_subcmd/breakpoints.rb +2 -3
- data/processor/command/info_subcmd/file.rb +2 -3
- data/processor/command/info_subcmd/frame.rb +2 -3
- data/processor/command/info_subcmd/iseq.rb +4 -5
- data/processor/command/info_subcmd/locals.rb +2 -3
- data/processor/command/info_subcmd/program.rb +2 -3
- data/processor/command/info_subcmd/registers.rb +2 -3
- data/processor/command/info_subcmd/return.rb +2 -3
- data/processor/command/info_subcmd/thread.rb +2 -3
- data/processor/command/list.rb +10 -6
- data/processor/command/reload.rb +1 -1
- data/processor/command/reload_subcmd/command.rb +29 -16
- data/processor/command/server.rb +70 -0
- data/processor/command/set_subcmd/auto.rb +2 -3
- data/processor/command/set_subcmd/basename.rb +2 -3
- data/processor/command/set_subcmd/debug.rb +2 -3
- data/processor/command/set_subcmd/different.rb +2 -3
- data/processor/command/set_subcmd/events.rb +2 -3
- data/processor/command/set_subcmd/hidelevel.rb +6 -7
- data/processor/command/set_subcmd/highlight.rb +32 -0
- data/processor/command/set_subcmd/max.rb +2 -3
- data/processor/command/set_subcmd/return.rb +2 -3
- data/processor/command/set_subcmd/sp.rb +2 -3
- data/processor/command/set_subcmd/substitute.rb +2 -3
- data/processor/command/set_subcmd/timer.rb +2 -3
- data/processor/command/set_subcmd/trace.rb +3 -4
- data/processor/command/show_subcmd/alias.rb +3 -3
- data/processor/command/show_subcmd/args.rb +2 -3
- data/processor/command/show_subcmd/auto.rb +1 -2
- data/processor/command/show_subcmd/basename.rb +2 -3
- data/processor/command/show_subcmd/debug.rb +1 -2
- data/processor/command/show_subcmd/different.rb +2 -1
- data/processor/command/show_subcmd/events.rb +2 -2
- data/processor/command/show_subcmd/hidelevel.rb +2 -3
- data/processor/command/show_subcmd/highlight.rb +23 -0
- data/processor/command/show_subcmd/macro.rb +2 -1
- data/processor/command/show_subcmd/max.rb +2 -3
- data/processor/command/show_subcmd/trace.rb +2 -3
- data/processor/command/source.rb +78 -28
- data/processor/default.rb +3 -2
- data/processor/load_cmds.rb +39 -19
- data/processor/location.rb +11 -7
- data/processor/main.rb +31 -15
- data/processor/mock.rb +22 -7
- data/processor/msg.rb +24 -8
- data/test/data/fname-with-blank.right +3 -0
- data/test/data/quit.right +2 -0
- data/test/functional/test-break-long.rb +87 -0
- data/test/functional/tmp/b3.rb +5 -0
- data/test/functional/tmp/immediate-bug1.rb +9 -0
- data/test/integration/helper.rb +14 -11
- data/test/integration/test-fname-with-blank.rb +5 -1
- data/test/integration/test-quit.rb +6 -2
- data/test/unit/cmd-helper.rb +9 -4
- data/test/unit/mock-helper.rb +9 -0
- data/test/unit/test-app-brkpt.rb +4 -4
- data/test/unit/test-app-brkptmgr.rb +2 -2
- data/test/unit/test-app-file.rb +0 -1
- data/test/unit/test-app-options.rb +26 -5
- data/test/unit/test-base-subcmd.rb +0 -1
- data/test/unit/test-cmd-alias.rb +0 -1
- data/test/unit/test-cmd-break.rb +0 -4
- data/test/unit/test-cmd-endisable.rb +1 -3
- data/test/unit/test-cmd-help.rb +0 -1
- data/test/unit/test-cmd-kill.rb +4 -5
- data/test/unit/test-cmd-quit.rb +4 -7
- data/test/unit/test-cmd-source.rb +33 -0
- data/test/unit/test-cmd-step.rb +0 -2
- data/test/unit/test-io-tcp.rb +32 -0
- data/test/unit/test-io-tcpclient.rb +53 -0
- data/test/unit/test-io-tcpserver.rb +49 -0
- data/test/unit/test-proc-main.rb +2 -2
- metadata +195 -175
- data/processor/command/stepi.rb +0 -63
- data/test/functional/tmp/b1.rb +0 -5
- data/test/functional/tmp/s1.rb +0 -9
- data/test/functional/tmp/t2.rb +0 -6
- data/test/integration/try-test-enable.rb +0 -11
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
3
|
+
|
4
|
+
# Communication status codes
|
5
|
+
module Trepanning
|
6
|
+
# Most of these go from debugged process to front-end
|
7
|
+
# client interface. COMMAND goes the other way.
|
8
|
+
module RemoteCommunication
|
9
|
+
PRINT = '.'
|
10
|
+
COMMAND = 'C'
|
11
|
+
CONFIRM_TRUE = 'Y'
|
12
|
+
CONFIRM_FALSE = 'N'
|
13
|
+
CONFIRM_REPLY = '?'
|
14
|
+
QUIT = 'q'
|
15
|
+
PROMPT = 'p'
|
16
|
+
RESTART = 'r'
|
17
|
+
end
|
18
|
+
end
|
data/interface/server.rb
ADDED
@@ -0,0 +1,142 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
3
|
+
|
4
|
+
# Our local modules
|
5
|
+
require_relative 'base_intf'
|
6
|
+
require_relative 'comcodes'
|
7
|
+
require_relative '../io/input'
|
8
|
+
require_relative '../io/tcpserver'
|
9
|
+
|
10
|
+
# Mfifoserver = import_relative('fifoserver', '..io', 'pydbgr')
|
11
|
+
|
12
|
+
# Interface for debugging a program but having user control
|
13
|
+
# reside outside of the debugged process, possibly on another
|
14
|
+
# computer
|
15
|
+
class Trepan::ServerInterface < Trepan::Interface
|
16
|
+
|
17
|
+
include Trepanning::RemoteCommunication
|
18
|
+
|
19
|
+
DEFAULT_INIT_CONNECTION_OPTS = {
|
20
|
+
:io => 'TCP'
|
21
|
+
} unless defined?(DEFAULT_INIT_CONNECTION_OPTS)
|
22
|
+
|
23
|
+
def initialize(inout=nil, out=nil, connection_opts={})
|
24
|
+
|
25
|
+
@connection_opts = DEFAULT_INIT_CONNECTION_OPTS.merge(connection_opts)
|
26
|
+
|
27
|
+
at_exit { finalize }
|
28
|
+
@inout =
|
29
|
+
if inout
|
30
|
+
inout
|
31
|
+
else
|
32
|
+
server_type = @connection_opts[:io]
|
33
|
+
# FIXME: complete this.
|
34
|
+
# if 'FIFO' == server_type
|
35
|
+
# FIFOServer.new
|
36
|
+
# else
|
37
|
+
Trepan::TCPDbgServer.new(@connection_opts)
|
38
|
+
# end
|
39
|
+
end
|
40
|
+
# For Compatability
|
41
|
+
@output = @inout
|
42
|
+
@input = @inout
|
43
|
+
@interactive = true # Or at least so we think initially
|
44
|
+
end
|
45
|
+
|
46
|
+
# Closes both input and output
|
47
|
+
def close
|
48
|
+
if @inout && @inout.connected?
|
49
|
+
@inout.write(QUIT + 'bye')
|
50
|
+
@inout.close
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Called when a dangerous action is about to be done to make sure
|
55
|
+
# it's okay. `prompt' is printed; user response is returned.
|
56
|
+
# FIXME: make common routine for this and user.rb
|
57
|
+
def confirm(prompt, default)
|
58
|
+
while true
|
59
|
+
begin
|
60
|
+
write_confirm(prompt, default)
|
61
|
+
reply = readline(nil).strip.downcase
|
62
|
+
rescue EOFError
|
63
|
+
return default
|
64
|
+
end
|
65
|
+
if YES.member?(reply)
|
66
|
+
return true
|
67
|
+
elsif NO.member?(reply)
|
68
|
+
return false
|
69
|
+
else
|
70
|
+
msg "Please answer 'yes' or 'no'. Try again."
|
71
|
+
end
|
72
|
+
end
|
73
|
+
return default
|
74
|
+
end
|
75
|
+
|
76
|
+
# Return true if we are connected
|
77
|
+
def connected?
|
78
|
+
:connected == @inout.state
|
79
|
+
end
|
80
|
+
|
81
|
+
# print exit annotation
|
82
|
+
def finalize(last_wishes=QUIT)
|
83
|
+
@inout.writeline(last_wishes) if connected?
|
84
|
+
close
|
85
|
+
end
|
86
|
+
|
87
|
+
def input_eof?
|
88
|
+
false
|
89
|
+
end
|
90
|
+
|
91
|
+
# used to write to a debugger that is connected to this
|
92
|
+
# server; `str' written will have a newline added to it
|
93
|
+
def msg(msg)
|
94
|
+
@inout.writeline(PRINT + msg)
|
95
|
+
end
|
96
|
+
|
97
|
+
# used to write to a debugger that is connected to this
|
98
|
+
# server; `str' written will not have a newline added to it
|
99
|
+
def msg_nocr(msg)
|
100
|
+
@inout.write(PRINT + msg)
|
101
|
+
end
|
102
|
+
|
103
|
+
def read_command(prompt)
|
104
|
+
readline(prompt)
|
105
|
+
end
|
106
|
+
|
107
|
+
def read_data
|
108
|
+
@inout.read_dat
|
109
|
+
end
|
110
|
+
|
111
|
+
def readline(prompt, add_to_history=true)
|
112
|
+
if prompt
|
113
|
+
write_prompt(prompt)
|
114
|
+
end
|
115
|
+
coded_line = @inout.read_msg()
|
116
|
+
@read_ctrl = coded_line[0..0]
|
117
|
+
coded_line[1..-1]
|
118
|
+
end
|
119
|
+
|
120
|
+
# Return connected
|
121
|
+
def state
|
122
|
+
@inout.state
|
123
|
+
end
|
124
|
+
|
125
|
+
def write_prompt(prompt)
|
126
|
+
@inout.write(PROMPT + prompt)
|
127
|
+
end
|
128
|
+
|
129
|
+
def write_confirm(prompt, default)
|
130
|
+
if default
|
131
|
+
code = CONFIRM_TRUE
|
132
|
+
else
|
133
|
+
code = CONFIRM_FALSE
|
134
|
+
end
|
135
|
+
@inout.write(code + prompt)
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Demo
|
140
|
+
if __FILE__ == $0
|
141
|
+
intf = Trepan::ServerInterface.new(nil, nil, :open => false)
|
142
|
+
end
|
data/io/base_io.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
1
|
+
# Copyright (C) 2010, 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
2
2
|
# classes to support communication to and from the debugger. This
|
3
3
|
# communcation might be to/from another process or another computer.
|
4
4
|
# And reading may be from a debugger command script.
|
@@ -60,8 +60,8 @@ class Trepan
|
|
60
60
|
attr_reader :output
|
61
61
|
def initialize(out, opts={})
|
62
62
|
@output = out
|
63
|
-
@eof = false
|
64
63
|
@flush_after_write = false
|
64
|
+
@eof = false
|
65
65
|
end
|
66
66
|
|
67
67
|
def close
|
@@ -86,9 +86,63 @@ class Trepan
|
|
86
86
|
# used to write to a debugger that is connected to this
|
87
87
|
# `str' written will have a newline added to it
|
88
88
|
#
|
89
|
-
def writeline(
|
89
|
+
def writeline(msg)
|
90
90
|
@output.write("%s\n" % msg)
|
91
91
|
end
|
92
92
|
end
|
93
|
+
|
94
|
+
# This is an abstract class that specifies debugger input output when
|
95
|
+
# handled by the same channel, e.g. a socket or tty.
|
96
|
+
#
|
97
|
+
class InOutBase
|
98
|
+
|
99
|
+
def initialize(inout, opts={})
|
100
|
+
@opts = DEFAULT_OPTS.merge(opts)
|
101
|
+
@inout = inout
|
102
|
+
end
|
103
|
+
|
104
|
+
def close
|
105
|
+
@inout.close() if @inout
|
106
|
+
end
|
107
|
+
|
108
|
+
def eof?
|
109
|
+
begin
|
110
|
+
@input.eof?
|
111
|
+
rescue IOError
|
112
|
+
true
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
def flush
|
117
|
+
@inout.flush
|
118
|
+
end
|
119
|
+
|
120
|
+
# Read a line of input. EOFError will be raised on EOF.
|
121
|
+
#
|
122
|
+
# Note that we don't support prompting first. Instead, arrange to
|
123
|
+
# call DebuggerOutput.write() first with the prompt. If `use_raw'
|
124
|
+
# is set raw_input() will be used in that is supported by the
|
125
|
+
# specific input input. If this option is left nil as is normally
|
126
|
+
# expected the value from the class initialization is used.
|
127
|
+
def readline(use_raw=nil)
|
128
|
+
@input.readline
|
129
|
+
end
|
130
|
+
|
131
|
+
# Use this to set where to write to. output can be a
|
132
|
+
# file object or a string. This code raises IOError on error.
|
133
|
+
#
|
134
|
+
# Use this to set where to write to. output can be a
|
135
|
+
# file object or a string. This code raises IOError on error.
|
136
|
+
def write(*args)
|
137
|
+
@inout.write(*args)
|
138
|
+
end
|
139
|
+
|
140
|
+
# used to write to a debugger that is connected to this
|
141
|
+
# server; `str' written will have a newline added to it
|
142
|
+
def writeline( msg)
|
143
|
+
@inout.write("%s\n" % msg)
|
144
|
+
end
|
145
|
+
end
|
146
|
+
|
93
147
|
end
|
94
148
|
|
data/io/tcpclient.rb
ADDED
@@ -0,0 +1,122 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
3
|
+
# Debugger Socket Input/Output Interface.
|
4
|
+
|
5
|
+
require 'socket'
|
6
|
+
require_relative 'base_io'
|
7
|
+
require_relative 'tcpfns'
|
8
|
+
|
9
|
+
class Trepan
|
10
|
+
# Debugger Client Input/Output Socket.
|
11
|
+
class TCPDbgClient < Trepan::InOutBase
|
12
|
+
|
13
|
+
include Trepanning::TCPPacking
|
14
|
+
|
15
|
+
DEFAULT_INIT_OPTS = {:open => true}
|
16
|
+
|
17
|
+
CLIENT_SOCKET_OPTS = {
|
18
|
+
:host => 'localhost', # Symbolic name
|
19
|
+
:port => 1027, # Arbitrary non-privileged port
|
20
|
+
}
|
21
|
+
|
22
|
+
def initialize(opts={})
|
23
|
+
@opts = CLIENT_SOCKET_OPTS.merge(opts)
|
24
|
+
@addr = nil
|
25
|
+
@buf = ''
|
26
|
+
@line_edit = false # Our name for GNU readline capability
|
27
|
+
@state = :disconnected
|
28
|
+
@inout = nil
|
29
|
+
open(@opts) if @opts[:open]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Closes both input and output
|
33
|
+
def close
|
34
|
+
@state = :closing
|
35
|
+
@inout.close if @inout
|
36
|
+
@state = :disconnnected
|
37
|
+
end
|
38
|
+
|
39
|
+
def open(opts={})
|
40
|
+
@opts = CLIENT_SOCKET_OPTS.merge(opts)
|
41
|
+
@host = @opts[:host]
|
42
|
+
@port = @opts[:port]
|
43
|
+
begin
|
44
|
+
@inout = TCPSocket.new(@host, @port)
|
45
|
+
@state = :connected
|
46
|
+
rescue SystemCallError => e
|
47
|
+
raise IOError,
|
48
|
+
('Open client for host %s on port %s gives error: %s' %
|
49
|
+
[@host, @port, e])
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
# Read one message unit. It's possible however that
|
54
|
+
# more than one message will be set in a receive, so we will
|
55
|
+
# have to buffer that for the next read.
|
56
|
+
# EOFError will be raised on EOF.
|
57
|
+
def read_msg
|
58
|
+
if @state == :connected
|
59
|
+
if !@buf || @buf.empty?
|
60
|
+
@buf = @inout.recv(TCP_MAX_PACKET)
|
61
|
+
if @buf.empty?
|
62
|
+
@state = :disconnected
|
63
|
+
raise EOFError
|
64
|
+
end
|
65
|
+
end
|
66
|
+
@buf, data = unpack_msg(@buf)
|
67
|
+
return data
|
68
|
+
else
|
69
|
+
raise IOError, ("read_msg called in state: %s." % @state.to_s)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
# This method the debugger uses to write a message unit.
|
74
|
+
def write(msg)
|
75
|
+
# FIXME: do we have to check the size of msg and split output?
|
76
|
+
@inout.write(pack_msg(msg))
|
77
|
+
end
|
78
|
+
|
79
|
+
def writeline(msg)
|
80
|
+
write(msg + "\n")
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Demo
|
86
|
+
if __FILE__ == $0
|
87
|
+
client = Trepan::TCPDbgClient.new({'open' => false})
|
88
|
+
if ARGV.size > 0
|
89
|
+
threads = []
|
90
|
+
Thread.new do
|
91
|
+
server = TCPServer.new('localhost', 1027)
|
92
|
+
session = server.accept
|
93
|
+
while 'quit' != (line = session.gets)
|
94
|
+
session.puts line
|
95
|
+
end
|
96
|
+
session.close
|
97
|
+
end
|
98
|
+
|
99
|
+
threads << Thread.new do
|
100
|
+
print 'Connecting...'
|
101
|
+
client.open()
|
102
|
+
puts 'connected.'
|
103
|
+
while true
|
104
|
+
print "input? "
|
105
|
+
line = STDIN.gets
|
106
|
+
break if line.chomp == 'quit'
|
107
|
+
begin
|
108
|
+
line = client.writeline(line)
|
109
|
+
puts "Got: #{client.read_msg.chomp}"
|
110
|
+
rescue EOFError
|
111
|
+
puts "Got EOF"
|
112
|
+
break
|
113
|
+
rescue Exception => e
|
114
|
+
puts "Got #{e}"
|
115
|
+
break
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
threads.each {|t| t.join }
|
120
|
+
end
|
121
|
+
client.close
|
122
|
+
end
|
data/io/tcpfns.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
3
|
+
# Subsidiary routines used to "pack" and "unpack" TCP messages.
|
4
|
+
|
5
|
+
module Trepanning
|
6
|
+
module TCPPacking
|
7
|
+
|
8
|
+
TCP_MAX_PACKET = 8192 # Largest size for a recv
|
9
|
+
LOG_MAX_MSG = Math.log10(TCP_MAX_PACKET).ceil
|
10
|
+
|
11
|
+
def pack_msg(msg)
|
12
|
+
fmt = '%%%dd' % LOG_MAX_MSG # A funny way of writing: '%4d'
|
13
|
+
(fmt % msg.size) + msg
|
14
|
+
end
|
15
|
+
|
16
|
+
def unpack_msg(buf)
|
17
|
+
length = Integer(buf[0...LOG_MAX_MSG])
|
18
|
+
data = buf[LOG_MAX_MSG..LOG_MAX_MSG+length]
|
19
|
+
buf = buf[LOG_MAX_MSG+length..-1]
|
20
|
+
[buf, data]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Demo
|
26
|
+
if __FILE__ == $0
|
27
|
+
include Trepanning::TCPPacking
|
28
|
+
msg = "Hi there!"
|
29
|
+
puts unpack_msg(pack_msg(msg))[1] == msg
|
30
|
+
end
|
31
|
+
|
data/io/tcpserver.rb
ADDED
@@ -0,0 +1,137 @@
|
|
1
|
+
# -*- coding: utf-8 -*-
|
2
|
+
# Copyright (C) 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
3
|
+
# Debugger Server Input/Output interface.
|
4
|
+
|
5
|
+
require 'socket'
|
6
|
+
require_relative '../app/default' # For host and port
|
7
|
+
require_relative 'base_io'
|
8
|
+
require_relative 'tcpfns'
|
9
|
+
|
10
|
+
class Trepan
|
11
|
+
# Debugger Server Input/Output Socket.
|
12
|
+
class TCPDbgServer < Trepan::InOutBase
|
13
|
+
|
14
|
+
include Trepanning::TCPPacking
|
15
|
+
|
16
|
+
DEFAULT_INIT_OPTS = {:open => true}
|
17
|
+
|
18
|
+
SERVER_SOCKET_OPTS = {
|
19
|
+
:host => Trepan::DEFAULT_SETTINGS[:host],
|
20
|
+
:port => Trepan::DEFAULT_SETTINGS[:port], # A non-privileged port
|
21
|
+
:timeout => 5, # FIXME: not used
|
22
|
+
:reuse => true, # FIXME: not used. Allow port to be resued on close?
|
23
|
+
# Python has: 'posix' == os.name
|
24
|
+
}
|
25
|
+
|
26
|
+
attr_reader :state
|
27
|
+
|
28
|
+
def initialize(opts={})
|
29
|
+
@opts = DEFAULT_INIT_OPTS.merge(opts)
|
30
|
+
@input = @output = @session = nil
|
31
|
+
@buf = '' # Read buffer
|
32
|
+
@state = :disconnected
|
33
|
+
@port = nil # Current port in use
|
34
|
+
@host = nil # current host in use
|
35
|
+
open(@opts) if @opts[:open]
|
36
|
+
end
|
37
|
+
|
38
|
+
def connected?
|
39
|
+
:connected == @state
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
# Closes server connection.
|
44
|
+
def close
|
45
|
+
@state = :closing
|
46
|
+
@session.close if @session
|
47
|
+
@state = :disconnected
|
48
|
+
end
|
49
|
+
|
50
|
+
def open(opts={})
|
51
|
+
@opts = SERVER_SOCKET_OPTS.merge(opts)
|
52
|
+
@host = @opts[:host]
|
53
|
+
@port = @opts[:port]
|
54
|
+
@server = TCPServer.new(@host, @port)
|
55
|
+
# @server.setsockopt(Socket::SOL_SOCKET, Socket::SO_RCVTIMEO, 5)
|
56
|
+
# # @opts[:timeout])
|
57
|
+
@state = :listening
|
58
|
+
end
|
59
|
+
|
60
|
+
# Read one message unit. It's possible however that
|
61
|
+
# more than one message will be set in a receive, so we will
|
62
|
+
# have to buffer that for the next read.
|
63
|
+
# EOFError will be raised on EOF.
|
64
|
+
def read_msg
|
65
|
+
wait_for_connect unless connected?
|
66
|
+
while !@buf || @buf.empty?
|
67
|
+
@buf, info = @session.recvfrom(TCP_MAX_PACKET)
|
68
|
+
end
|
69
|
+
@buf, data = unpack_msg(@buf)
|
70
|
+
data
|
71
|
+
end
|
72
|
+
|
73
|
+
def wait_for_connect
|
74
|
+
@input = @output = @session = @server.accept
|
75
|
+
@state = :connected
|
76
|
+
end
|
77
|
+
|
78
|
+
# This method the debugger uses to write. In contrast to
|
79
|
+
# writeline, no newline is added to the end to `str'. Also
|
80
|
+
# msg doesn't have to be a string.
|
81
|
+
def write(msg)
|
82
|
+
wait_for_connect() unless connected?
|
83
|
+
# FIXME: do we have to check the size of msg and split output?
|
84
|
+
@session.print(pack_msg(msg))
|
85
|
+
end
|
86
|
+
|
87
|
+
def writeline(msg)
|
88
|
+
write(msg + "\n")
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
# Demo
|
95
|
+
if __FILE__ == $0
|
96
|
+
include Trepanning::TCPPacking
|
97
|
+
server = Trepan::TCPDbgServer.new({ :open => false,
|
98
|
+
:port => 1027,
|
99
|
+
:host => 'localhost'
|
100
|
+
})
|
101
|
+
if ARGV.size > 0
|
102
|
+
puts 'Listening for connection...'
|
103
|
+
server.open
|
104
|
+
threads = []
|
105
|
+
Thread.new do
|
106
|
+
while true do
|
107
|
+
begin
|
108
|
+
line = server.read_msg.chomp
|
109
|
+
puts "got #{line}"
|
110
|
+
rescue EOFError
|
111
|
+
puts 'Got EOF'
|
112
|
+
break
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
threads << Thread.new do
|
117
|
+
t = TCPSocket.new('localhost', 1027)
|
118
|
+
while true do
|
119
|
+
begin
|
120
|
+
print "input? "
|
121
|
+
line = STDIN.gets
|
122
|
+
break if !line || line.chomp == 'quit'
|
123
|
+
t.puts(pack_msg(line))
|
124
|
+
rescue EOFError
|
125
|
+
puts "Got EOF"
|
126
|
+
break
|
127
|
+
rescue Exception => e
|
128
|
+
puts "Got #{e}"
|
129
|
+
break
|
130
|
+
end
|
131
|
+
end
|
132
|
+
t.close
|
133
|
+
end
|
134
|
+
threads.each {|t| t.join }
|
135
|
+
server.close
|
136
|
+
end
|
137
|
+
end
|