rb-threadframe 0.39 → 0.40
Sign up to get free protection for your applications and to get access to all the features.
- data/NEWS +6 -0
- data/README.md +6 -3
- data/Rakefile +4 -4
- data/ext/1.9.3/thread_frame.c +22 -0
- data/ext/extconf.rb +2 -2
- data/ext/version.h +1 -0
- data/lib/iseq_extra.rb +7 -6
- data/lib/thread_frame.rb +9 -1
- data/test/unit/cfunc-use.rb +1 -1
- data/test/unit/test-argc.rb +3 -2
- data/test/unit/test-binding.rb +5 -5
- data/test/unit/{test-thread.rb → test-frame.rb} +36 -33
- data/test/unit/test-invalid.rb +6 -5
- data/test/unit/test-iseq-brkpt.rb +4 -4
- data/test/unit/test-iseq-save.rb +1 -1
- data/test/unit/test-iseq.rb +10 -9
- data/test/unit/test-lib-iseq-extra.rb +14 -5
- data/test/unit/test-lib-iseq.rb +9 -8
- data/test/unit/test-prev.rb +12 -12
- data/test/unit/test-proc.rb +4 -1
- data/test/unit/test-return-stop.rb +3 -4
- data/test/unit/test-settracefunc.rb +67 -43
- data/test/unit/test-source.rb +7 -8
- data/test/unit/test-sp-size.rb +4 -5
- data/test/unit/test-thread-trace-masks.rb +2 -1
- data/test/unit/test-trace.rb +4 -4
- data/threadframe.rd +31 -31
- metadata +31 -57
- data/ext/iseq_extra.c +0 -441
- data/ext/iseq_extra.h +0 -8
- data/ext/iseq_mini.h +0 -41
- data/ext/node.h +0 -483
- data/ext/proc_extra.c +0 -186
- data/ext/proc_extra.h +0 -3
- data/ext/thread_extra.c +0 -84
- data/ext/thread_extra.h +0 -5
- data/ext/thread_frame.c +0 -1165
- data/ext/thread_frame.h +0 -4
- data/ext/thread_pthread.h +0 -24
- data/test/ruby/test_brkpt.rb +0 -60
- data/test/ruby/test_disasm.rb +0 -17
- data/test/ruby/test_iseq.rb +0 -50
- data/test/ruby/test_tracefunc_raise.rb +0 -26
data/NEWS
CHANGED
@@ -1,3 +1,9 @@
|
|
1
|
+
Nov 25, 2012 (0.40)
|
2
|
+
- Redone for Ruby 1.9.3 p327
|
3
|
+
- RubyVM::ThreadFrame is now RubyVM::Frame. RubyVM::ThreadFrame is still
|
4
|
+
there for compatibility
|
5
|
+
- Some small administrative and bug fixes
|
6
|
+
|
1
7
|
Oct 27, 2011 (0.39)
|
2
8
|
- Revise patch for 1.9.2 revision 33354
|
3
9
|
- save return value in one more C_RETURN hook
|
data/README.md
CHANGED
@@ -1,10 +1,13 @@
|
|
1
1
|
# rb-threadframe
|
2
2
|
|
3
|
-
rb-threadframe is a set of patches to Ruby 1.9.2 and
|
4
|
-
|
3
|
+
rb-threadframe is a set of patches to Ruby MRI 1.9.2 and 1.9.3 that
|
4
|
+
adds introspection of call frames and adds other run-time support for debuggers and run-time introspection.
|
5
|
+
|
6
|
+
For MRI 1.9.2, there are additional routines are in a C extension. For MRI 1.9.3, everthing is in a patched Ruby. Necessary patches and some simple patch code are found in this repository though.
|
5
7
|
|
6
8
|
## Requirements
|
7
9
|
|
8
|
-
|
10
|
+
Source for Ruby MRI version 1.9.2 or 1.9.3 p327. Patches are provided
|
11
|
+
for these two versions only.
|
9
12
|
|
10
13
|
See the [wiki](http://github.com/rocky/rb-threadframe/wiki) for more information.
|
data/Rakefile
CHANGED
@@ -14,7 +14,7 @@ def gemspec
|
|
14
14
|
@gemspec ||= eval(File.read(Gemspec_filename), binding, Gemspec_filename)
|
15
15
|
end
|
16
16
|
|
17
|
-
require '
|
17
|
+
require 'rubygems/package_task'
|
18
18
|
desc "Build the gem"
|
19
19
|
task :package=>:gem
|
20
20
|
task :gem=>:gemspec do
|
@@ -32,11 +32,11 @@ task :install => :gem do
|
|
32
32
|
end
|
33
33
|
end
|
34
34
|
|
35
|
-
desc 'Create the
|
35
|
+
desc 'Create the thread-frame shared library extension'
|
36
36
|
task :ext do
|
37
37
|
Dir.chdir('ext') do
|
38
38
|
system("#{Gem.ruby} extconf.rb && make")
|
39
|
-
end
|
39
|
+
end if '1.9.2' == RUBY_VERSION
|
40
40
|
end
|
41
41
|
|
42
42
|
desc 'Remove built files'
|
@@ -110,7 +110,7 @@ task :gemspec do
|
|
110
110
|
end
|
111
111
|
|
112
112
|
# --------- RDoc Documentation ------
|
113
|
-
require '
|
113
|
+
require 'rdoc/task'
|
114
114
|
desc "Generate rdoc documentation"
|
115
115
|
Rake::RDocTask.new("rdoc") do |rdoc|
|
116
116
|
rdoc.rdoc_dir = 'doc'
|
@@ -0,0 +1,22 @@
|
|
1
|
+
/* Ruby 1.9.3 doesn't need a C extension while 1.9.2 does. I can't figure
|
2
|
+
out a way build a gem so that it loads the C extension conditionally.
|
3
|
+
So instead we have a simple stupid stub extension.
|
4
|
+
|
5
|
+
This as it is not elegant.
|
6
|
+
*/
|
7
|
+
|
8
|
+
#include <ruby.h>
|
9
|
+
#include <ruby/version.h>
|
10
|
+
#include <string.h>
|
11
|
+
void
|
12
|
+
Init_thread_frame(void)
|
13
|
+
{
|
14
|
+
if (0 == strncmp("1.9.2", ruby_version, sizeof("1.9.2")))
|
15
|
+
{
|
16
|
+
rb_raise(rb_eLoadError,
|
17
|
+
"Gem installed under Ruby 1.9.3 but this Ruby 1.9.2. Please reinstall 'rb-threadframe' gem under 1.9.2.");
|
18
|
+
} else if (0 == strncmp("1.9.3", ruby_version, sizeof("1.9.3"))) {
|
19
|
+
rb_raise(rb_eLoadError,
|
20
|
+
"Under Ruby 1.9.3 there is no reason to load this thread_frame C extension.");
|
21
|
+
}
|
22
|
+
}
|
data/ext/extconf.rb
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
require
|
1
|
+
require 'mkmf'
|
2
2
|
|
3
3
|
fail "You need to install a threadframe-patched Ruby.
|
4
4
|
See http://github.com/rocky/rb-threadframe/wiki/How-to-Install" unless
|
@@ -11,4 +11,4 @@ See http://github.com/rocky/rb-threadframe/wiki/How-to-Install" unless
|
|
11
11
|
config_file = File.join(File.dirname(__FILE__), 'config_options.rb')
|
12
12
|
load config_file if File.exist?(config_file)
|
13
13
|
|
14
|
-
create_makefile("thread_frame")
|
14
|
+
create_makefile("thread_frame", RUBY_VERSION)
|
data/ext/version.h
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
#define THREADFRAME_VERSION "0.40"
|
data/lib/iseq_extra.rb
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'digest/sha1'
|
2
|
-
require_relative '../ext/thread_frame'
|
2
|
+
require_relative '../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
3
|
# Some additions to RubyVM::InstructionSequence
|
4
4
|
class RubyVM::InstructionSequence
|
5
5
|
|
@@ -141,8 +141,9 @@ end
|
|
141
141
|
|
142
142
|
if __FILE__ == $0
|
143
143
|
# Demo it.
|
144
|
-
iseq = RubyVM::
|
145
|
-
|
144
|
+
iseq = RubyVM::Frame.current.iseq
|
145
|
+
## FIXME
|
146
|
+
## puts iseq.format_args
|
146
147
|
puts iseq.disassemble
|
147
148
|
puts iseq.lineoffsets
|
148
149
|
puts iseq.sha1
|
@@ -150,7 +151,7 @@ if __FILE__ == $0
|
|
150
151
|
p iseq.line2offsets(__LINE__+100)
|
151
152
|
|
152
153
|
def show_type # :nodoc:
|
153
|
-
tf = RubyVM::
|
154
|
+
tf = RubyVM::Frame.current
|
154
155
|
while tf do
|
155
156
|
is = tf.iseq
|
156
157
|
if is
|
@@ -166,12 +167,12 @@ if __FILE__ == $0
|
|
166
167
|
|
167
168
|
line = __LINE__
|
168
169
|
def find_line(line) # :nodoc
|
169
|
-
tf = RubyVM::
|
170
|
+
tf = RubyVM::Frame.current
|
170
171
|
puts "find_line has lines: #{tf.iseq.lines}"
|
171
172
|
p tf.iseq.find_iseq_with_line(line)
|
172
173
|
end
|
173
174
|
|
174
|
-
tf = RubyVM::
|
175
|
+
tf = RubyVM::Frame.current
|
175
176
|
puts tf.iseq.disassemble
|
176
177
|
puts("offset %d above should be at line %d" %
|
177
178
|
[tf.iseq.locate_line(line), line])
|
data/lib/thread_frame.rb
CHANGED
@@ -1,3 +1,11 @@
|
|
1
1
|
# Boilerplate to pull shared object and helper routines.
|
2
|
-
|
2
|
+
if RUBY_VERSION == '1.9.2'
|
3
|
+
require_relative '../ext/thread_frame'
|
4
|
+
end
|
3
5
|
require_relative 'iseq_extra'
|
6
|
+
|
7
|
+
RubyVM::ThreadFrame = RubyVM::Frame
|
8
|
+
class Thread
|
9
|
+
# For compatibility with old stuff
|
10
|
+
alias tracing tracing?
|
11
|
+
end
|
data/test/unit/cfunc-use.rb
CHANGED
data/test/unit/test-argc.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
|
2
|
+
|
3
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
4
|
|
4
5
|
class TestARGC < Test::Unit::TestCase
|
5
6
|
|
@@ -23,7 +24,7 @@ class TestARGC < Test::Unit::TestCase
|
|
23
24
|
all_events = []
|
24
25
|
eval <<-EOF.gsub(/^.*?: /, "")
|
25
26
|
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
|
26
|
-
2: tf = RubyVM::
|
27
|
+
2: tf = RubyVM::Frame.prev
|
27
28
|
3: all_events << [tf.argc, tf.arity, tf.type, mid]
|
28
29
|
4: if :basename == mid
|
29
30
|
5: events << [tf.argc, tf.arity, tf.type, mid]
|
data/test/unit/test-binding.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require_relative '../../ext/thread_frame'
|
2
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
3
|
|
4
4
|
def outside(a)
|
5
|
-
return eval('a', RubyVM::
|
5
|
+
return eval('a', RubyVM::Frame.new(Thread::current).binding)
|
6
6
|
end
|
7
7
|
|
8
8
|
$a = 10
|
@@ -12,13 +12,13 @@ class TestBinding < Test::Unit::TestCase
|
|
12
12
|
a = 1
|
13
13
|
c = 0
|
14
14
|
assert_equal(5, outside(5))
|
15
|
-
tf = RubyVM::
|
15
|
+
tf = RubyVM::Frame.new(Thread::current)
|
16
16
|
b = tf.binding
|
17
17
|
assert_equal(1, eval('a', b))
|
18
18
|
assert_equal(10, eval('$a', b))
|
19
19
|
assert_equal(self, tf.self)
|
20
20
|
1.times do |i;a|
|
21
|
-
tf2 = Thread::current.
|
21
|
+
tf2 = Thread::current.frame()
|
22
22
|
b2 = tf2.binding
|
23
23
|
a = 2
|
24
24
|
assert_equal(2, eval('a', b2))
|
@@ -29,7 +29,7 @@ class TestBinding < Test::Unit::TestCase
|
|
29
29
|
assert_equal(1, eval('a', tf2.prev(2).binding))
|
30
30
|
end
|
31
31
|
def inner(a)
|
32
|
-
tf3 = Thread::current.
|
32
|
+
tf3 = Thread::current.frame()
|
33
33
|
b3 = tf3.binding
|
34
34
|
if a == 4
|
35
35
|
assert_equal(4, eval('a', b3))
|
@@ -1,17 +1,17 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require_relative '../../ext/thread_frame'
|
2
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
3
|
|
4
4
|
class TestThread < Test::Unit::TestCase
|
5
5
|
def test_basic
|
6
|
-
assert_equal(RubyVM::
|
7
|
-
RubyVM::
|
8
|
-
assert_equal(RubyVM::
|
6
|
+
assert_equal(RubyVM::Frame.new(Thread::current).thread,
|
7
|
+
RubyVM::Frame::current.thread)
|
8
|
+
assert_equal(RubyVM::Frame.new(Thread::current).thread,
|
9
9
|
Thread::current)
|
10
|
-
assert_equal(Thread::current.
|
10
|
+
assert_equal(Thread::current.frame.thread, Thread::current)
|
11
11
|
end
|
12
12
|
|
13
13
|
def test_pc_offset
|
14
|
-
tf = RubyVM::
|
14
|
+
tf = RubyVM::Frame::current
|
15
15
|
offset_1 = tf.pc_offset
|
16
16
|
assert_equal(true, offset_1 > 0,
|
17
17
|
"Expecting a positive integer pc offset, got %s" % offset_1)
|
@@ -24,11 +24,10 @@ class TestThread < Test::Unit::TestCase
|
|
24
24
|
end
|
25
25
|
|
26
26
|
def test_sp
|
27
|
-
tf = RubyVM::
|
27
|
+
tf = RubyVM::Frame.prev
|
28
28
|
assert tf.sp(1)
|
29
29
|
tf.sp_set(1, 5)
|
30
|
-
assert_equal(5, tf.sp(1),
|
31
|
-
'chcking value of recently-set sp(1)')
|
30
|
+
assert_equal(5, tf.sp(1), 'checking value of recently-set sp(1)')
|
32
31
|
end
|
33
32
|
|
34
33
|
def test_source_container
|
@@ -38,7 +37,7 @@ class TestThread < Test::Unit::TestCase
|
|
38
37
|
assert_equal(['CFUNC', cfunc_filebase], [type, File.basename(loc, '.rb')],
|
39
38
|
'CFUNCs should get their file location from frame.prev*')
|
40
39
|
cont = 'file'
|
41
|
-
eval '1.times{cont = RubyVM::
|
40
|
+
eval '1.times{cont = RubyVM::Frame.current.source_container[0]}'
|
42
41
|
assert_equal('string', cont,
|
43
42
|
'source container[0] of an eval(...) should be "string"')
|
44
43
|
end
|
@@ -51,8 +50,9 @@ class TestThread < Test::Unit::TestCase
|
|
51
50
|
# because the location is reported on a traceback
|
52
51
|
# and that probably can't happen at PC 0.
|
53
52
|
def bug_when_zero_pc
|
53
|
+
skip "pc_offset= not implemented on 1.9.3" if '1.9.3' == RUBY_VERSION
|
54
54
|
@not_first = true
|
55
|
-
tf = RubyVM::
|
55
|
+
tf = RubyVM::Frame::current.prev
|
56
56
|
pc_save = tf.pc_offset
|
57
57
|
tf.pc_offset = 0
|
58
58
|
loc = tf.source_location
|
@@ -65,23 +65,23 @@ class TestThread < Test::Unit::TestCase
|
|
65
65
|
|
66
66
|
|
67
67
|
def test_thread_tracing
|
68
|
-
assert_equal(false, Thread.current.tracing)
|
68
|
+
assert_equal(false, Thread.current.tracing?)
|
69
69
|
Thread.current.tracing = true
|
70
|
-
assert_equal(true, Thread.current.tracing)
|
70
|
+
assert_equal(true, Thread.current.tracing?)
|
71
71
|
Thread.current.tracing = false
|
72
|
-
assert_equal(false, Thread.current.tracing)
|
72
|
+
assert_equal(false, Thread.current.tracing?)
|
73
73
|
end
|
74
74
|
|
75
75
|
def test_thread_exec_event_tracing
|
76
|
-
assert_equal(false, Thread.current.exec_event_tracing)
|
76
|
+
assert_equal(false, Thread.current.exec_event_tracing?)
|
77
77
|
Thread.current.exec_event_tracing = true
|
78
|
-
assert_equal(true, Thread.current.exec_event_tracing)
|
78
|
+
assert_equal(true, Thread.current.exec_event_tracing?)
|
79
79
|
Thread.current.exec_event_tracing = false
|
80
|
-
assert_equal(false, Thread.current.exec_event_tracing)
|
80
|
+
assert_equal(false, Thread.current.exec_event_tracing?)
|
81
81
|
end
|
82
82
|
|
83
83
|
def test_fields(notused=nil)
|
84
|
-
tf = RubyVM::
|
84
|
+
tf = RubyVM::Frame::current
|
85
85
|
pc1 = tf.pc_offset
|
86
86
|
assert(pc1 > 0, 'Should be able to get a valid PC offset')
|
87
87
|
# pc_offset is dynamic - it changes constantly
|
@@ -91,12 +91,13 @@ class TestThread < Test::Unit::TestCase
|
|
91
91
|
assert_equal(self, tf.self)
|
92
92
|
assert_equal(0, tf.arity)
|
93
93
|
assert_equal(0, tf.argc)
|
94
|
-
|
95
|
-
assert tf.
|
94
|
+
## FIXME: Should we allow this?
|
95
|
+
## assert tf.dfp(0)
|
96
|
+
## assert tf.lfp(0)
|
96
97
|
|
97
|
-
assert_raises IndexError do
|
98
|
-
|
99
|
-
end
|
98
|
+
# assert_raises IndexError do
|
99
|
+
# x = tf.lfp(tf.iseq.local_size+1)
|
100
|
+
# end
|
100
101
|
|
101
102
|
|
102
103
|
tf_prev = tf.prev
|
@@ -107,10 +108,10 @@ class TestThread < Test::Unit::TestCase
|
|
107
108
|
|
108
109
|
# 1.times creates a C frame.
|
109
110
|
1.times do
|
110
|
-
tf = RubyVM::
|
111
|
+
tf = RubyVM::Frame::current
|
111
112
|
tup = tf.source_container
|
112
113
|
tup[1] = File.basename(tup[1])
|
113
|
-
assert_equal(['file', 'test-
|
114
|
+
assert_equal(['file', 'test-frame.rb'], tup)
|
114
115
|
assert_equal('block in test_fields', tf.method)
|
115
116
|
assert_equal('CFUNC', tf.prev.type)
|
116
117
|
assert_equal('times', tf.prev.method)
|
@@ -124,24 +125,26 @@ class TestThread < Test::Unit::TestCase
|
|
124
125
|
|
125
126
|
# 1.upto also creates a C frame.
|
126
127
|
1.upto(1) do
|
127
|
-
|
128
|
+
# We could use ".prev" below instead of '.current.prev", but we
|
129
|
+
# may as well test current.prev.
|
130
|
+
tf = RubyVM::Frame::current.prev
|
128
131
|
assert_equal('CFUNC', tf.type)
|
129
132
|
assert_equal(1, tf.arity, 'C arity should work nowadays' )
|
130
133
|
assert_equal(1, tf.argc)
|
131
134
|
end
|
132
135
|
|
133
136
|
x = lambda do |x,y|
|
134
|
-
frame = RubyVM::
|
137
|
+
frame = RubyVM::Frame::current
|
135
138
|
assert_equal('block in test_fields', frame.method)
|
136
139
|
assert_equal('LAMBDA', frame.type)
|
137
140
|
assert_equal(x, tf.self)
|
138
|
-
|
139
|
-
|
141
|
+
assert_equal(2, frame.arity)
|
142
|
+
assert_equal(2, frame.argc)
|
140
143
|
end
|
141
144
|
x.call(x,2)
|
142
145
|
|
143
146
|
x = Proc.new do |x, y|
|
144
|
-
frame = RubyVM::
|
147
|
+
frame = RubyVM::Frame::current
|
145
148
|
assert_equal('block in test_fields', frame.method)
|
146
149
|
assert_equal(x, tf.self)
|
147
150
|
assert_equal('BLOCK', frame.type)
|
@@ -150,9 +153,9 @@ class TestThread < Test::Unit::TestCase
|
|
150
153
|
|
151
154
|
end
|
152
155
|
|
153
|
-
def
|
154
|
-
tf = RubyVM::
|
155
|
-
tf2 = RubyVM::
|
156
|
+
def test_frame_equal
|
157
|
+
tf = RubyVM::Frame.current
|
158
|
+
tf2 = RubyVM::Frame.current
|
156
159
|
assert_equal(true, tf.equal?(tf))
|
157
160
|
assert_equal(true, tf.equal?(tf2))
|
158
161
|
tf2 = tf2.prev
|
data/test/unit/test-invalid.rb
CHANGED
@@ -1,17 +1,18 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require_relative '../../ext/thread_frame'
|
3
2
|
|
4
|
-
|
3
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
4
|
+
|
5
|
+
class TestInvalid < Test::Unit::TestCase
|
5
6
|
def test_basic
|
6
|
-
@tf = RubyVM::
|
7
|
+
@tf = RubyVM::Frame::current
|
7
8
|
assert_equal(false, @tf.invalid?,
|
8
|
-
'Frame should be valid right after
|
9
|
+
'Frame should be valid right after RubyVM::Frame::current')
|
9
10
|
def notgood(test_tf=nil)
|
10
11
|
# FIXME
|
11
12
|
# if test_tf
|
12
13
|
# assert_equal(test_tf != @tf, test_tf.invalid?)
|
13
14
|
# end
|
14
|
-
return RubyVM::
|
15
|
+
return RubyVM::Frame::current
|
15
16
|
end
|
16
17
|
|
17
18
|
def inner_fn(tf)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
require 'test/unit'
|
2
|
-
require_relative '../../ext/thread_frame'
|
2
|
+
require_relative '../../ext/thread_frame' if '1.9.2' == RUBY_VERSION
|
3
3
|
|
4
4
|
class TestISeqBrkpt < Test::Unit::TestCase
|
5
5
|
|
6
6
|
def test_iseq_brkpt
|
7
|
-
iseq = RubyVM::
|
7
|
+
iseq = RubyVM::Frame.current.iseq
|
8
8
|
assert iseq
|
9
9
|
assert_equal(nil, iseq.brkpts)
|
10
10
|
assert_equal(true, iseq.brkpt_alloc)
|
@@ -41,14 +41,14 @@ class TestISeqBrkpt < Test::Unit::TestCase
|
|
41
41
|
end
|
42
42
|
|
43
43
|
def test_iseq_brkpt_set
|
44
|
-
|
44
|
+
set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
|
45
45
|
if 'brkpt' == event
|
46
46
|
$saw_brkpt = true
|
47
47
|
end
|
48
48
|
})
|
49
49
|
|
50
50
|
$saw_brkpt = false
|
51
|
-
tf = RubyVM::
|
51
|
+
tf = RubyVM::Frame.current
|
52
52
|
tf.iseq.offsetlines.keys.each do |offset|
|
53
53
|
tf.iseq.brkpt_set(offset)
|
54
54
|
end
|
data/test/unit/test-iseq-save.rb
CHANGED