line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# -*- coding: utf-8 -*- |
2
|
|
|
|
|
|
|
# Copyright (C) 2011-2014 Rocky Bernstein <rocky@cpan.org> |
3
|
|
|
|
|
|
|
|
4
|
12
|
|
|
12
|
|
78
|
use warnings; no warnings 'redefine'; use utf8; |
|
12
|
|
|
12
|
|
29
|
|
|
12
|
|
|
12
|
|
365
|
|
|
12
|
|
|
|
|
64
|
|
|
12
|
|
|
|
|
26
|
|
|
12
|
|
|
|
|
363
|
|
|
12
|
|
|
|
|
68
|
|
|
12
|
|
|
|
|
29
|
|
|
12
|
|
|
|
|
92
|
|
5
|
|
|
|
|
|
|
|
6
|
|
|
|
|
|
|
# Interface for debugging a program but having user control |
7
|
|
|
|
|
|
|
# reside outside of the debugged process, possibly on another |
8
|
|
|
|
|
|
|
# computer |
9
|
|
|
|
|
|
|
package Devel::Trepan::Interface::Server; |
10
|
12
|
|
|
12
|
|
531
|
use English qw( -no_match_vars ); |
|
12
|
|
|
|
|
35
|
|
|
12
|
|
|
|
|
78
|
|
11
|
|
|
|
|
|
|
our (@ISA); |
12
|
|
|
|
|
|
|
|
13
|
|
|
|
|
|
|
# Our local modules |
14
|
12
|
|
|
12
|
|
4447
|
use rlib '../../..'; |
|
12
|
|
|
|
|
40
|
|
|
12
|
|
|
|
|
67
|
|
15
|
12
|
|
|
12
|
|
4091
|
use rlib '.'; |
|
12
|
|
|
|
|
28
|
|
|
12
|
|
|
|
|
51
|
|
16
|
12
|
|
|
12
|
|
4112
|
use if !@ISA, Devel::Trepan::Interface::ComCodes; |
|
12
|
|
|
|
|
33
|
|
|
12
|
|
|
|
|
94
|
|
17
|
12
|
|
|
12
|
|
1134
|
use if !@ISA, Devel::Trepan::IO::Input; |
|
12
|
|
|
|
|
30
|
|
|
12
|
|
|
|
|
59
|
|
18
|
12
|
|
|
12
|
|
1392
|
use Devel::Trepan::Util qw(hash_merge YES NO); |
|
12
|
|
|
|
|
28
|
|
|
12
|
|
|
|
|
733
|
|
19
|
12
|
|
|
12
|
|
71
|
use if !@ISA, Devel::Trepan::IO::TCPServer; |
|
12
|
|
|
|
|
30
|
|
|
12
|
|
|
|
|
54
|
|
20
|
12
|
|
|
12
|
|
741
|
use if !@ISA, Devel::Trepan::IO::FIFOServer; |
|
12
|
|
|
|
|
30
|
|
|
12
|
|
|
|
|
95
|
|
21
|
|
|
|
|
|
|
|
22
|
12
|
50
|
|
12
|
|
609
|
use constant HAVE_TTY => eval q(use Devel::Trepan::IO::TTYServer; 1) ? 1 : 0; |
|
12
|
|
|
12
|
|
35
|
|
|
12
|
|
|
|
|
705
|
|
|
12
|
|
|
|
|
4794
|
|
|
0
|
|
|
|
|
0
|
|
|
0
|
|
|
|
|
0
|
|
23
|
|
|
|
|
|
|
|
24
|
12
|
|
|
12
|
|
86
|
use strict; |
|
12
|
|
|
|
|
32
|
|
|
12
|
|
|
|
|
562
|
|
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
@ISA = qw(Devel::Trepan::Interface Exporter); |
27
|
|
|
|
|
|
|
|
28
|
12
|
|
|
|
|
15216
|
use constant DEFAULT_INIT_CONNECTION_OPTS => { |
29
|
|
|
|
|
|
|
io => 'tcp', |
30
|
|
|
|
|
|
|
logger => undef # An Interface. Complaints go here. |
31
|
12
|
|
|
12
|
|
77
|
}; |
|
12
|
|
|
|
|
28
|
|
32
|
|
|
|
|
|
|
|
33
|
|
|
|
|
|
|
sub new |
34
|
|
|
|
|
|
|
{ |
35
|
0
|
|
|
0
|
0
|
|
my($class, $input, $out, $connection_opts) = @_; |
36
|
0
|
|
|
|
|
|
$connection_opts = hash_merge($connection_opts, |
37
|
|
|
|
|
|
|
DEFAULT_INIT_CONNECTION_OPTS); |
38
|
|
|
|
|
|
|
|
39
|
0
|
|
|
|
|
|
my $server_type = $connection_opts->{io}; |
40
|
|
|
|
|
|
|
my $self = { |
41
|
|
|
|
|
|
|
interactive => 1, # Or at least so we think initially |
42
|
|
|
|
|
|
|
logger => $connection_opts->{logger} |
43
|
0
|
|
|
|
|
|
}; |
44
|
0
|
0
|
|
|
|
|
unless (defined($input)) { |
45
|
0
|
|
|
|
|
|
my $server; |
46
|
0
|
0
|
|
|
|
|
if ('tty' eq $server_type) { |
|
|
0
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
47
|
0
|
|
|
|
|
|
if (HAVE_TTY) { |
48
|
|
|
|
|
|
|
$server = Devel::Trepan::IO::TTYServer->new($connection_opts); |
49
|
|
|
|
|
|
|
} else { |
50
|
0
|
|
|
|
|
|
die "You don't have Devel::Trepan::TTY installed"; |
51
|
|
|
|
|
|
|
} |
52
|
|
|
|
|
|
|
} elsif ('fifo' eq $server_type) { |
53
|
0
|
|
|
|
|
|
$server = Devel::Trepan::IO::FIFOServer->new($connection_opts); |
54
|
|
|
|
|
|
|
} elsif ('tcp' eq $server_type) { |
55
|
0
|
|
|
|
|
|
$server = Devel::Trepan::IO::TCPServer->new($connection_opts); |
56
|
|
|
|
|
|
|
} else { |
57
|
0
|
|
|
|
|
|
die "Unknown communication protocol: $server_type"; |
58
|
|
|
|
|
|
|
} |
59
|
|
|
|
|
|
|
# For Compatability |
60
|
0
|
|
|
|
|
|
$self->{output} = $self->{input} = $self->{inout} = $server; |
61
|
|
|
|
|
|
|
} |
62
|
|
|
|
|
|
|
|
63
|
0
|
|
|
|
|
|
bless $self, $class; |
64
|
0
|
|
|
|
|
|
return $self; |
65
|
|
|
|
|
|
|
} |
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# Closes both input and output |
68
|
|
|
|
|
|
|
sub close($) |
69
|
|
|
|
|
|
|
{ |
70
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
71
|
0
|
|
|
|
|
|
$self->{output}->write(QUIT . 'bye'); |
72
|
|
|
|
|
|
|
# FIXME: remove sleep and figure out to find when above worked. |
73
|
0
|
|
|
|
|
|
sleep 1; |
74
|
0
|
0
|
|
|
|
|
if ($self->{output} == $self->{input}) { |
75
|
0
|
|
|
|
|
|
$self->{output}->close; |
76
|
|
|
|
|
|
|
} else { |
77
|
0
|
|
|
|
|
|
$self->{input}->close; |
78
|
0
|
|
|
|
|
|
$self->{output}->close; |
79
|
|
|
|
|
|
|
} |
80
|
|
|
|
|
|
|
} |
81
|
|
|
|
|
|
|
|
82
|
|
|
|
|
|
|
sub is_closed($) |
83
|
|
|
|
|
|
|
{ |
84
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
85
|
|
|
|
|
|
|
$self->{input}->is_closed && $self->{output}->is_closed |
86
|
0
|
0
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
|
|
|
|
|
|
sub is_interactive($) |
89
|
|
|
|
|
|
|
{ |
90
|
0
|
|
|
0
|
0
|
|
my $self = shift; |
91
|
0
|
|
|
|
|
|
$self->{input}->is_interactive; |
92
|
|
|
|
|
|
|
} |
93
|
|
|
|
|
|
|
|
94
|
|
|
|
|
|
|
sub has_completion($) |
95
|
|
|
|
|
|
|
{ |
96
|
0
|
|
|
0
|
0
|
|
0 |
97
|
|
|
|
|
|
|
} |
98
|
|
|
|
|
|
|
|
99
|
|
|
|
|
|
|
# Called when a dangerous action is about to be done to make sure |
100
|
|
|
|
|
|
|
# it's okay. `prompt' is printed; user response is returned. |
101
|
|
|
|
|
|
|
# FIXME: make common routine for this and user.rb |
102
|
|
|
|
|
|
|
sub confirm($;$$) |
103
|
|
|
|
|
|
|
{ |
104
|
0
|
|
|
0
|
0
|
|
my ($self, $prompt, $default) = @_; |
105
|
|
|
|
|
|
|
|
106
|
0
|
|
|
|
|
|
my $reply; |
107
|
0
|
|
|
|
|
|
while (1) { |
108
|
|
|
|
|
|
|
# begin |
109
|
0
|
|
|
|
|
|
$self->write_confirm($prompt, $default); |
110
|
0
|
|
|
|
|
|
$reply = $self->readline; |
111
|
0
|
|
|
|
|
|
chomp($reply); |
112
|
0
|
0
|
|
|
|
|
if (defined($reply)) { |
113
|
0
|
|
|
|
|
|
($reply = lc(unpack("A*", $reply))) =~ s/^\s+//; |
114
|
|
|
|
|
|
|
} else { |
115
|
0
|
|
|
|
|
|
return $default; |
116
|
|
|
|
|
|
|
} |
117
|
0
|
0
|
|
|
|
|
if (grep(/^${reply}$/, YES)) { |
|
|
0
|
|
|
|
|
|
118
|
0
|
|
|
|
|
|
return 1; |
119
|
|
|
|
|
|
|
} elsif (grep(/^${reply}$/, NO)) { |
120
|
0
|
|
|
|
|
|
return 0; |
121
|
|
|
|
|
|
|
} else { |
122
|
0
|
|
|
|
|
|
$self->msg("Please answer 'yes' or 'no'. Try again."); |
123
|
|
|
|
|
|
|
} |
124
|
|
|
|
|
|
|
} |
125
|
0
|
|
|
|
|
|
return $default; |
126
|
|
|
|
|
|
|
} |
127
|
|
|
|
|
|
|
|
128
|
|
|
|
|
|
|
# Return 1 if we are connected |
129
|
|
|
|
|
|
|
sub is_connected($) |
130
|
|
|
|
|
|
|
{ |
131
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
132
|
0
|
|
|
|
|
|
'connected' eq $self->{inout}->{state}; |
133
|
|
|
|
|
|
|
} |
134
|
|
|
|
|
|
|
|
135
|
|
|
|
|
|
|
sub is_input_eof($) |
136
|
|
|
|
|
|
|
{ |
137
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
138
|
0
|
|
|
|
|
|
0; |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
|
141
|
|
|
|
|
|
|
# used to write to a debugger that is connected to this |
142
|
|
|
|
|
|
|
# server; `str' written will have a newline added to it |
143
|
|
|
|
|
|
|
sub msg($;$) |
144
|
|
|
|
|
|
|
{ |
145
|
0
|
|
|
0
|
0
|
|
my ($self, $msg) = @_; |
146
|
0
|
|
|
|
|
|
my @msg = split(/\n/, $msg); |
147
|
0
|
|
|
|
|
|
foreach my $line (@msg) { |
148
|
0
|
|
|
|
|
|
$self->{inout}->writeline(PRINT . $line); |
149
|
|
|
|
|
|
|
} |
150
|
|
|
|
|
|
|
} |
151
|
|
|
|
|
|
|
|
152
|
|
|
|
|
|
|
# used to write to a debugger that is connected to this |
153
|
|
|
|
|
|
|
# server; `str' written will have a newline added to it |
154
|
|
|
|
|
|
|
sub errmsg($;$) |
155
|
|
|
|
|
|
|
{ |
156
|
0
|
|
|
0
|
0
|
|
my ($self, $msg) = @_; |
157
|
0
|
|
|
|
|
|
my @msg = split(/\n/, $msg); |
158
|
0
|
|
|
|
|
|
foreach my $line (@msg) { |
159
|
0
|
|
|
|
|
|
$self->{inout}->writeline(SERVERERR . $line); |
160
|
|
|
|
|
|
|
} |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
|
163
|
|
|
|
|
|
|
# used to write to a debugger that is connected to this |
164
|
|
|
|
|
|
|
# server; `str' written will not have a newline added to it |
165
|
|
|
|
|
|
|
sub msg_nocr($$) |
166
|
|
|
|
|
|
|
{ |
167
|
0
|
|
|
0
|
0
|
|
my ($self, $msg) = @_; |
168
|
0
|
|
|
|
|
|
$self->{inout}->write(PRINT . $msg); |
169
|
|
|
|
|
|
|
} |
170
|
|
|
|
|
|
|
|
171
|
|
|
|
|
|
|
# read a debugger command |
172
|
|
|
|
|
|
|
sub read_command($$) |
173
|
|
|
|
|
|
|
{ |
174
|
0
|
|
|
0
|
0
|
|
my ($self, $prompt) = @_; |
175
|
0
|
|
|
|
|
|
$self->readline($prompt); |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
sub read_data($) |
179
|
|
|
|
|
|
|
{ |
180
|
0
|
|
|
0
|
0
|
|
my ($self, $prompt) = @_; |
181
|
0
|
|
|
|
|
|
$self->{inout}->read_data; |
182
|
|
|
|
|
|
|
} |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
sub readline($;$) |
185
|
|
|
|
|
|
|
{ |
186
|
0
|
|
|
0
|
0
|
|
my ($self, $prompt, $add_to_history) = @_; |
187
|
|
|
|
|
|
|
# my ($self, $prompt, $add_to_history) = @_; |
188
|
|
|
|
|
|
|
# $add_to_history = 1; |
189
|
0
|
0
|
|
|
|
|
if ($prompt) { |
190
|
0
|
|
|
|
|
|
$self->write_prompt($prompt); |
191
|
|
|
|
|
|
|
} |
192
|
0
|
|
|
|
|
|
my $coded_line; |
193
|
0
|
|
|
|
|
|
eval { |
194
|
0
|
|
|
|
|
|
$coded_line = $self->{inout}->read_msg(); |
195
|
|
|
|
|
|
|
}; |
196
|
0
|
0
|
|
|
|
|
if ($EVAL_ERROR) { |
197
|
0
|
0
|
|
|
|
|
print {$self->{logger}} "$EVAL_ERROR\n" if $self->{logger}; |
|
0
|
|
|
|
|
|
|
198
|
0
|
|
|
|
|
|
$self->errmsg("Server communication protocol error, resyncing..."); |
199
|
0
|
|
|
|
|
|
return ('#'); |
200
|
|
|
|
|
|
|
} else { |
201
|
0
|
0
|
|
|
|
|
if ($coded_line) { |
202
|
0
|
|
|
|
|
|
my $read_ctrl = substr($coded_line,0,1); |
203
|
0
|
|
|
|
|
|
return substr($coded_line, 1); |
204
|
|
|
|
|
|
|
} else { |
205
|
0
|
|
|
|
|
|
return ""; |
206
|
|
|
|
|
|
|
} |
207
|
|
|
|
|
|
|
} |
208
|
|
|
|
|
|
|
} |
209
|
|
|
|
|
|
|
|
210
|
|
|
|
|
|
|
sub remove_history($;$) |
211
|
|
|
|
|
|
|
{ |
212
|
0
|
|
|
0
|
0
|
|
my ($self, $which) = @_; |
213
|
0
|
0
|
|
|
|
|
return unless ($self->{input}{readline}); |
214
|
0
|
0
|
|
|
|
|
$which = $self->{input}{readline}->where_history() unless defined $which; |
215
|
0
|
|
|
|
|
|
$self->{input}{readline}->remove_history($which); |
216
|
|
|
|
|
|
|
} |
217
|
|
|
|
|
|
|
|
218
|
|
|
|
|
|
|
# Return connected |
219
|
|
|
|
|
|
|
sub state($) |
220
|
|
|
|
|
|
|
{ |
221
|
0
|
|
|
0
|
0
|
|
my ($self) = @_; |
222
|
0
|
|
|
|
|
|
$self->{inout}->{state}; |
223
|
|
|
|
|
|
|
} |
224
|
|
|
|
|
|
|
|
225
|
|
|
|
|
|
|
sub write_prompt($$) |
226
|
|
|
|
|
|
|
{ |
227
|
0
|
|
|
0
|
0
|
|
my ($self, $prompt) = @_; |
228
|
0
|
|
|
|
|
|
$self->{inout}->write(PROMPT . $prompt); |
229
|
|
|
|
|
|
|
} |
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
sub write_confirm($$$) |
232
|
|
|
|
|
|
|
{ |
233
|
0
|
|
|
0
|
0
|
|
my ($self, $prompt, $default) = @_; |
234
|
0
|
0
|
|
|
|
|
my $code = $default ? CONFIRM_TRUE : CONFIRM_FALSE; |
235
|
0
|
|
|
|
|
|
$self->{inout}->write($code . $prompt) |
236
|
|
|
|
|
|
|
} |
237
|
|
|
|
|
|
|
|
238
|
|
|
|
|
|
|
# Demo |
239
|
|
|
|
|
|
|
unless (caller) { |
240
|
|
|
|
|
|
|
my $intf = __PACKAGE__->new(undef, undef, {open => 0, io => 'tcp'}); |
241
|
|
|
|
|
|
|
# $intf->close(); |
242
|
|
|
|
|
|
|
$intf = __PACKAGE__->new(undef, undef, |
243
|
|
|
|
|
|
|
{open => 1, io => 'tty', logger=>\*STDOUT}); |
244
|
|
|
|
|
|
|
$intf->close(); |
245
|
|
|
|
|
|
|
} |
246
|
|
|
|
|
|
|
|
247
|
|
|
|
|
|
|
1; |