rtinspect 0.0.1 → 0.0.19
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/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: []
|