rtinspect 0.0.1 → 0.0.19
Sign up to get free protection for your applications and to get access to all the features.
- data/README +84 -50
- data/TODO +18 -7
- data/VERSION +1 -0
- data/bin/rti +69 -0
- data/lib/rti/manager.rb +411 -0
- data/lib/rti/redirect.rb +102 -0
- data/lib/rti/state.rb +65 -0
- data/lib/rti/thread.rb +553 -0
- data/lib/rti/tracer.rb +71 -0
- data/lib/rtinspect.rb +76 -899
- data/test/test.rb +248 -17
- metadata +13 -6
data/test/test.rb
CHANGED
@@ -1,9 +1,42 @@
|
|
1
|
+
# Contains the TestRuntimeInspectionThread.
|
2
|
+
#
|
3
|
+
# Copyright (c) 2006-2007 Brad Robel-Forrest.
|
4
|
+
#
|
5
|
+
# This software can be distributed under the terms of the Ruby license
|
6
|
+
# as detailed in the accompanying LICENSE[link:files/LICENSE.html]
|
7
|
+
# file.
|
8
|
+
|
9
|
+
require 'thread'
|
10
|
+
|
11
|
+
# Extend the Thread class so we can know exactly where a given thread
|
12
|
+
# was created to validate that there aren't any leftover in various
|
13
|
+
# spots.
|
14
|
+
class Thread
|
15
|
+
alias orig_initialize initialize
|
16
|
+
def initialize( *args, &block )
|
17
|
+
orig_initialize( *args, &block )
|
18
|
+
self[:caller] = caller
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
1
22
|
require 'timeout'
|
2
23
|
require 'expect'
|
3
24
|
require 'test/unit'
|
4
25
|
|
5
26
|
require 'rtinspect'
|
6
27
|
|
28
|
+
if ARGV.include?( '-show_trace' )
|
29
|
+
RuntimeInspection.show_trace = true
|
30
|
+
end
|
31
|
+
|
32
|
+
$mybp_line = __LINE__ + 2
|
33
|
+
class MyBreakpoint
|
34
|
+
def mybreakpoint( i )
|
35
|
+
a = 1
|
36
|
+
b = 2
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
7
40
|
class TestRuntimeInspectionThread < Test::Unit::TestCase
|
8
41
|
|
9
42
|
class Tracker
|
@@ -11,12 +44,13 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
|
|
11
44
|
@@assert_equal = block
|
12
45
|
end
|
13
46
|
|
14
|
-
def initialize
|
47
|
+
def initialize
|
15
48
|
$expect_verbose = $DEBUG
|
16
49
|
@sock = TCPSocket.new( 'localhost', 56789 )
|
17
50
|
@cmd_count = 1
|
18
51
|
@block_count = 0
|
19
52
|
@at_prompt = false
|
53
|
+
@prompt_name = File.basename( $0, '.rb' )
|
20
54
|
end
|
21
55
|
|
22
56
|
def assert( cmd=nil, *expected )
|
@@ -38,7 +72,7 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
|
|
38
72
|
timeout( 5 ) do
|
39
73
|
out += @sock.expect( prompt ).first
|
40
74
|
end
|
41
|
-
out.sub!(
|
75
|
+
out.sub!( /#{@prompt_name}:\d+:\d> $/, '' )
|
42
76
|
end
|
43
77
|
|
44
78
|
if @block_count > 0
|
@@ -48,13 +82,23 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
|
|
48
82
|
timeout( 5 ) do
|
49
83
|
out += @sock.expect( prompt ).first
|
50
84
|
end
|
51
|
-
out.sub!(
|
85
|
+
out.sub!( /#{@prompt_name}:\d+:\d> $/, '' )
|
52
86
|
end
|
53
87
|
|
54
88
|
@at_prompt = true
|
55
89
|
|
56
90
|
out.chomp!
|
57
|
-
|
91
|
+
if expected.empty?
|
92
|
+
return out
|
93
|
+
else
|
94
|
+
if expected.last == :no_newline
|
95
|
+
expected.pop
|
96
|
+
nl = ''
|
97
|
+
else
|
98
|
+
nl = "\n"
|
99
|
+
end
|
100
|
+
@@assert_equal.call( expected.join("\n") + nl, out )
|
101
|
+
end
|
58
102
|
end
|
59
103
|
|
60
104
|
def puts( msg )
|
@@ -86,7 +130,7 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
|
|
86
130
|
private
|
87
131
|
|
88
132
|
def prompt
|
89
|
-
"
|
133
|
+
"#{@prompt_name}:#{'%03d'%@cmd_count}:#{@block_count}> "
|
90
134
|
end
|
91
135
|
end
|
92
136
|
|
@@ -96,9 +140,22 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
|
|
96
140
|
end
|
97
141
|
end
|
98
142
|
|
143
|
+
def teardown
|
144
|
+
# Ensure that the RTI is not running so if one test fails,
|
145
|
+
# future ones won't just because it can't take the port.
|
146
|
+
rti_th = nil
|
147
|
+
Thread.list.each do |th|
|
148
|
+
if th.kind_of?( RuntimeInspection::Thread )
|
149
|
+
rti_th = th
|
150
|
+
break
|
151
|
+
end
|
152
|
+
end
|
153
|
+
rti_th.kill if rti_th
|
154
|
+
end
|
155
|
+
|
99
156
|
def test_prompt
|
100
157
|
threads = Thread.list.dup
|
101
|
-
r =
|
158
|
+
r = RuntimeInspection::Thread.new
|
102
159
|
assert_not_equal( threads, Thread.list )
|
103
160
|
t = Tracker.new
|
104
161
|
t.assert
|
@@ -112,33 +169,37 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
|
|
112
169
|
end
|
113
170
|
|
114
171
|
def test_locals
|
115
|
-
r =
|
172
|
+
r = RuntimeInspection::Thread.new
|
116
173
|
t = Tracker.new
|
117
174
|
|
118
175
|
t.assert( 'rti.state.use_yaml = true', "=> --- true" )
|
119
176
|
|
120
|
-
|
121
|
-
|
122
|
-
|
177
|
+
# As in the case of running within the GEM framework, the
|
178
|
+
# toplevel binding is not clean. Thus, we can only check that
|
179
|
+
# at least rti is around.
|
180
|
+
out = t.assert( 'local_variables' )
|
181
|
+
out.sub!( /^=> /, '' )
|
182
|
+
assert( YAML.load( out ).include?( 'rti' ), 'Missing rti local' )
|
123
183
|
|
124
184
|
r.kill
|
125
185
|
end
|
126
186
|
|
187
|
+
RTI_LIST = ["backtrace", "bp_add", "bp_attach", "bp_continue", "bp_delete",
|
188
|
+
"bp_finish", "bp_list", "bp_next", "bp_start", "bp_step",
|
189
|
+
"bp_stop", "get_object", "list", "name2class", "start_world",
|
190
|
+
"state", "stop_world", "threads"]
|
191
|
+
|
127
192
|
def test_list
|
128
|
-
r =
|
193
|
+
r = RuntimeInspection::Thread.new
|
129
194
|
t = Tracker.new
|
130
195
|
|
131
|
-
t.assert( 'rti.list',
|
132
|
-
'=> ["bp_add", "bp_continue", "bp_del", "bp_fin", "bp_list", ' +
|
133
|
-
'"bp_next", "bp_start", "bp_step", "bp_stop", "get_object", ' +
|
134
|
-
'"initialize", "list", "name2class", "start_world", "state", ' +
|
135
|
-
'"stop_world"]' )
|
196
|
+
t.assert( 'rti.list', "=> #{RTI_LIST.inspect}" )
|
136
197
|
|
137
198
|
r.kill
|
138
199
|
end
|
139
200
|
|
140
201
|
def test_lookup
|
141
|
-
r =
|
202
|
+
r = RuntimeInspection::Thread.new
|
142
203
|
t = Tracker.new
|
143
204
|
|
144
205
|
Struct.new( 'Foo', :one, :two, :three )
|
@@ -159,4 +220,174 @@ class TestRuntimeInspectionThread < Test::Unit::TestCase
|
|
159
220
|
r.kill
|
160
221
|
end
|
161
222
|
|
223
|
+
def test_signal
|
224
|
+
assert_raise( Errno::ECONNREFUSED ) do
|
225
|
+
t = Tracker.new
|
226
|
+
end
|
227
|
+
|
228
|
+
RuntimeInspection.on_signal
|
229
|
+
|
230
|
+
Process.kill( 'USR1', $$ )
|
231
|
+
t = Tracker.new
|
232
|
+
|
233
|
+
t.assert( 'rti.list', "=> #{RTI_LIST.inspect}" )
|
234
|
+
|
235
|
+
Process.kill( 'USR1', $$ )
|
236
|
+
|
237
|
+
assert_raise( Errno::ECONNREFUSED ) do
|
238
|
+
t = Tracker.new
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def test_breakpoints
|
243
|
+
r = RuntimeInspection::Thread.new
|
244
|
+
t = Tracker.new
|
245
|
+
|
246
|
+
# Try some invalid breakpoints.
|
247
|
+
t.assert( '.bp_add Foo#bar',
|
248
|
+
'=> "Unable to find matching module or class for Foo"' )
|
249
|
+
t.assert( '.bp_add MyBreakpoint#doesnotexist',
|
250
|
+
'=> "Unable to find doesnotexist method in MyBreakpoint"' )
|
251
|
+
t.assert( '.bp_add bar',
|
252
|
+
'=> "Unable to find containing class or module for bar"' )
|
253
|
+
|
254
|
+
# Now let's run through a simple breakpoint analysis.
|
255
|
+
t.assert( '.bp_add MyBreakpoint#mybreakpoint',
|
256
|
+
'=> "Added breakpoint 1"' )
|
257
|
+
|
258
|
+
line = __LINE__
|
259
|
+
th = Thread.new do
|
260
|
+
bp = MyBreakpoint.new
|
261
|
+
5.times do |i|
|
262
|
+
sleep 0.3
|
263
|
+
bp.mybreakpoint( i )
|
264
|
+
end
|
265
|
+
end
|
266
|
+
|
267
|
+
begin
|
268
|
+
t.assert( '.bp_start',
|
269
|
+
'=> nil', '',
|
270
|
+
'Breakpoint 1 in MyBreakpoint#mybreakpoint ' +
|
271
|
+
"from #{__FILE__}:#{$mybp_line} (call) in #{th}",
|
272
|
+
:no_newline )
|
273
|
+
|
274
|
+
t.assert( 'local_variables',
|
275
|
+
'=> ["i", "a", "b", "rti"]', '',
|
276
|
+
'Breakpoint 1 in MyBreakpoint#mybreakpoint ' +
|
277
|
+
"from #{__FILE__}:#{$mybp_line} (call) in #{th}",
|
278
|
+
:no_newline )
|
279
|
+
|
280
|
+
t.assert( '.bp_next',
|
281
|
+
'=> nil', '',
|
282
|
+
'Stopped in MyBreakpoint#mybreakpoint ' +
|
283
|
+
"from #{__FILE__}:#{$mybp_line+1} (line) in #{th}",
|
284
|
+
:no_newline )
|
285
|
+
|
286
|
+
t.assert( 'rti.backtrace',
|
287
|
+
"=> [\"#{__FILE__}:#{$mybp_line+1}:in `mybreakpoint'\"," +
|
288
|
+
" \"#{__FILE__}:#{line+5}:in `test_breakpoints'\", " +
|
289
|
+
"\"#{__FILE__}:#{line+3}:in `test_breakpoints'\", " +
|
290
|
+
"\"#{__FILE__}:17:in `initialize'\", " +
|
291
|
+
"\"#{__FILE__}:#{line+1}:in `test_breakpoints'\"]",
|
292
|
+
'',
|
293
|
+
'Stopped in MyBreakpoint#mybreakpoint ' +
|
294
|
+
"from #{__FILE__}:#{$mybp_line+1} (line) in #{th}",
|
295
|
+
:no_newline )
|
296
|
+
|
297
|
+
out = t.assert( 'rti.threads' )
|
298
|
+
assert_match( / :main sleep/, out )
|
299
|
+
assert_no_match( /RuntimeInspection::Thread:/, out )
|
300
|
+
assert_no_match( /:rti_/, out )
|
301
|
+
|
302
|
+
t.assert( '.bp_continue',
|
303
|
+
'=> nil', '',
|
304
|
+
'Breakpoint 1 in MyBreakpoint#mybreakpoint ' +
|
305
|
+
"from #{__FILE__}:#{$mybp_line} (call) in #{th}",
|
306
|
+
:no_newline )
|
307
|
+
ensure
|
308
|
+
t.assert( '.bp_stop', '=> nil' )
|
309
|
+
th.kill
|
310
|
+
end
|
311
|
+
|
312
|
+
r.kill
|
313
|
+
end
|
314
|
+
|
315
|
+
def test_attach
|
316
|
+
r = RuntimeInspection::Thread.new
|
317
|
+
t = Tracker.new
|
318
|
+
|
319
|
+
line = __LINE__ + 4
|
320
|
+
th = Thread.new do
|
321
|
+
Thread.current[:thread_name] = :mybreakpoint
|
322
|
+
5.times do
|
323
|
+
sleep( 0.1 )
|
324
|
+
end
|
325
|
+
end
|
326
|
+
|
327
|
+
begin
|
328
|
+
t.assert( '.bp_attach :mybreakpoint',
|
329
|
+
'=> nil', '',
|
330
|
+
'Breakpoint 1 in TestRuntimeInspectionThread#test_attach ' +
|
331
|
+
"from #{__FILE__}:#{line} (line) in #{th}",
|
332
|
+
:no_newline )
|
333
|
+
ensure
|
334
|
+
t.assert( '.bp_stop', '=> nil' )
|
335
|
+
th.kill
|
336
|
+
end
|
337
|
+
|
338
|
+
r.kill
|
339
|
+
end
|
340
|
+
|
341
|
+
def test_client_close
|
342
|
+
r = RuntimeInspection::Thread.new
|
343
|
+
t = Tracker.new
|
344
|
+
|
345
|
+
# Have the client disconnect while waiting in the bp_start
|
346
|
+
# method.
|
347
|
+
|
348
|
+
t.assert( '.bp_add MyBreakpoint#mybreakpoint',
|
349
|
+
'=> "Added breakpoint 1"' )
|
350
|
+
|
351
|
+
th = Thread.new do
|
352
|
+
t.assert( '.bp_start' )
|
353
|
+
end
|
354
|
+
|
355
|
+
sleep 0.2
|
356
|
+
|
357
|
+
th.kill
|
358
|
+
t.close
|
359
|
+
|
360
|
+
def r.breakpoints
|
361
|
+
@breakpoints
|
362
|
+
end
|
363
|
+
|
364
|
+
def r.clients
|
365
|
+
@clients
|
366
|
+
end
|
367
|
+
|
368
|
+
sleep 0.2
|
369
|
+
|
370
|
+
assert_equal( {}, r.breakpoints )
|
371
|
+
assert_equal( [], r.clients )
|
372
|
+
|
373
|
+
found = {}
|
374
|
+
Thread.list.each do |th|
|
375
|
+
if th.kind_of?( RuntimeInspection::Thread )
|
376
|
+
found[:rti] = true
|
377
|
+
elsif name = th[:thread_name]
|
378
|
+
if name == :rti_gc
|
379
|
+
found[:rti_gc] = true
|
380
|
+
end
|
381
|
+
elsif th == Thread.main
|
382
|
+
found[:main] = true
|
383
|
+
else
|
384
|
+
flunk( "Found unknown thread (#{th.inspect}): " +
|
385
|
+
"#{th[:caller].inspect}" )
|
386
|
+
end
|
387
|
+
end
|
388
|
+
assert_equal( {:rti=>true, :rti_gc=>true, :main=>true}, found )
|
389
|
+
|
390
|
+
r.kill
|
391
|
+
end
|
392
|
+
|
162
393
|
end
|
metadata
CHANGED
@@ -3,19 +3,19 @@ rubygems_version: 0.9.0
|
|
3
3
|
specification_version: 1
|
4
4
|
name: rtinspect
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.0.
|
7
|
-
date: 2007-01-
|
6
|
+
version: 0.0.19
|
7
|
+
date: 2007-01-09 00:00:00 -08:00
|
8
8
|
summary: A thread that will enable remote access capabilities in a running Ruby process for debugging and inspection.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
11
11
|
email: bradrf@gigglewax.com
|
12
12
|
homepage: http://rtinspect.rubyforge.org/
|
13
|
-
rubyforge_project:
|
13
|
+
rubyforge_project: rtinspect
|
14
14
|
description: This is a class that will enable remote access capabilities in a running Ruby process, exposing the ability to enable breakpoints, walk code (e.g. step/next/continue), inspect variables, modify codepaths, and many other debugging actions.
|
15
15
|
autorequire:
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
18
|
-
has_rdoc:
|
18
|
+
has_rdoc: true
|
19
19
|
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
20
|
requirements:
|
21
21
|
- - ">"
|
@@ -30,11 +30,17 @@ authors:
|
|
30
30
|
- Brad Robel-Forrest
|
31
31
|
files:
|
32
32
|
- lib/rtinspect.rb
|
33
|
+
- lib/rti/manager.rb
|
34
|
+
- lib/rti/redirect.rb
|
35
|
+
- lib/rti/state.rb
|
36
|
+
- lib/rti/thread.rb
|
37
|
+
- lib/rti/tracer.rb
|
33
38
|
- test/test.rb
|
34
39
|
- README
|
35
40
|
- LICENSE
|
36
41
|
- COPYING
|
37
42
|
- TODO
|
43
|
+
- VERSION
|
38
44
|
test_files:
|
39
45
|
- test/test.rb
|
40
46
|
rdoc_options: []
|
@@ -44,8 +50,9 @@ extra_rdoc_files:
|
|
44
50
|
- LICENSE
|
45
51
|
- COPYING
|
46
52
|
- TODO
|
47
|
-
|
48
|
-
|
53
|
+
- VERSION
|
54
|
+
executables:
|
55
|
+
- rti
|
49
56
|
extensions: []
|
50
57
|
|
51
58
|
requirements: []
|