rb-threadframe 0.38 → 0.39
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/NEWS +4 -0
- data/README.md +1 -1
- data/Rakefile +18 -9
- data/ext/thread_frame.c +3 -5
- data/test/ruby/test_brkpt.rb +60 -0
- data/test/ruby/test_disasm.rb +17 -0
- data/test/ruby/test_iseq.rb +50 -0
- data/test/ruby/test_tracefunc_adds.rb +151 -0
- data/test/ruby/test_tracefunc_raise.rb +26 -0
- metadata +9 -4
data/NEWS
CHANGED
data/README.md
CHANGED
@@ -5,6 +5,6 @@ that gives introspection access for method call frames.
|
|
5
5
|
|
6
6
|
## Requirements
|
7
7
|
|
8
|
-
A patched version of Ruby 1.9.2 is needed.
|
8
|
+
A patched version of YARV Ruby 1.9.2 (and not any other version Ruby) of is needed.
|
9
9
|
|
10
10
|
See the [wiki](http://github.com/rocky/rb-threadframe/wiki) for more information.
|
data/Rakefile
CHANGED
@@ -4,13 +4,14 @@ require 'rubygems'
|
|
4
4
|
require 'fileutils'
|
5
5
|
|
6
6
|
ROOT_DIR = File.dirname(__FILE__)
|
7
|
+
Gemspec_filename='rb-threadframe.gemspec'
|
7
8
|
|
8
9
|
require 'rbconfig'
|
9
10
|
RUBY_PATH = File.join(RbConfig::CONFIG['bindir'],
|
10
11
|
RbConfig::CONFIG['RUBY_INSTALL_NAME'])
|
11
12
|
|
12
13
|
def gemspec
|
13
|
-
@gemspec ||= eval(File.read(
|
14
|
+
@gemspec ||= eval(File.read(Gemspec_filename), binding, Gemspec_filename)
|
14
15
|
end
|
15
16
|
|
16
17
|
require 'rake/gempackagetask'
|
@@ -18,7 +19,7 @@ desc "Build the gem"
|
|
18
19
|
task :package=>:gem
|
19
20
|
task :gem=>:gemspec do
|
20
21
|
Dir.chdir(ROOT_DIR) do
|
21
|
-
sh "gem build
|
22
|
+
sh "gem build #{Gemspec_filename}"
|
22
23
|
FileUtils.mkdir_p 'pkg'
|
23
24
|
FileUtils.mv("#{gemspec.file_name}", "pkg/")
|
24
25
|
end
|
@@ -50,19 +51,20 @@ task :clean do
|
|
50
51
|
end
|
51
52
|
end
|
52
53
|
|
53
|
-
def run_standalone_ruby_file(directory)
|
54
|
-
puts
|
54
|
+
def run_standalone_ruby_file(directory, opts={})
|
55
|
+
puts(('*' * 10) + ' ' + directory + ' ' + ('*' * 10))
|
55
56
|
Dir.chdir(directory) do
|
56
57
|
Dir.glob('*.rb').each do |ruby_file|
|
57
|
-
puts
|
58
|
+
puts(('-' * 20) + ' ' + ruby_file + ' ' + ('-' * 20))
|
58
59
|
system(RUBY_PATH, ruby_file)
|
60
|
+
break if $?.exitstatus != 0 && !opts[:continue]
|
59
61
|
end
|
60
62
|
end
|
61
63
|
end
|
62
64
|
|
63
|
-
desc
|
65
|
+
desc 'Create a GNU-style ChangeLog via git2cl'
|
64
66
|
task :ChangeLog do
|
65
|
-
system(
|
67
|
+
system('git log --pretty --numstat --summary | git2cl > ChangeLog')
|
66
68
|
end
|
67
69
|
|
68
70
|
task :default => [:test]
|
@@ -109,8 +111,8 @@ end
|
|
109
111
|
|
110
112
|
# --------- RDoc Documentation ------
|
111
113
|
require 'rake/rdoctask'
|
112
|
-
desc
|
113
|
-
Rake::RDocTask.new(
|
114
|
+
desc "Generate rdoc documentation"
|
115
|
+
Rake::RDocTask.new("rdoc") do |rdoc|
|
114
116
|
rdoc.rdoc_dir = 'doc'
|
115
117
|
rdoc.title = 'rb-threadframe'
|
116
118
|
# Make the readme file the start page for the generated html
|
@@ -128,3 +130,10 @@ end
|
|
128
130
|
task :clobber_rdoc do
|
129
131
|
FileUtils.rm_rf File.join(ROOT_DIR, 'doc')
|
130
132
|
end
|
133
|
+
|
134
|
+
task :rm_patch_residue do
|
135
|
+
FileUtils.rm_rf FileList['**/*.{rej,orig}'].to_a
|
136
|
+
end
|
137
|
+
|
138
|
+
desc "Remove built files"
|
139
|
+
task :clean => [:clobber_package, :clobber_rdoc, :rm_patch_residue]
|
data/ext/thread_frame.c
CHANGED
@@ -6,7 +6,7 @@
|
|
6
6
|
*/
|
7
7
|
|
8
8
|
/* What release we got? */
|
9
|
-
#define THREADFRAME_VERSION "0.
|
9
|
+
#define THREADFRAME_VERSION "0.39"
|
10
10
|
|
11
11
|
#include <string.h>
|
12
12
|
#include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
|
@@ -339,10 +339,8 @@ thread_frame_sp_size_internal(thread_frame_t *tf)
|
|
339
339
|
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(tf->th, prev_cfp))
|
340
340
|
return Qnil;
|
341
341
|
ret_val = tf->cfp->sp - prev_cfp->sp - 1;
|
342
|
-
/* FIXME: Why For C Functions we tack on
|
343
|
-
|
344
|
-
we should be looking at.? */
|
345
|
-
if (RUBYVM_CFUNC_FRAME_P(tf->cfp)) ret_val += 3;
|
342
|
+
/* FIXME: Why For C Functions we tack on 2 for this RubyVM::ENV? */
|
343
|
+
if (RUBYVM_CFUNC_FRAME_P(tf->cfp)) ret_val += 2;
|
346
344
|
return ret_val;
|
347
345
|
}
|
348
346
|
|
@@ -0,0 +1,60 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
class TestISeqBrkpt < Test::Unit::TestCase
|
4
|
+
|
5
|
+
def setup
|
6
|
+
@original_compile_option = RubyVM::InstructionSequence.compile_option
|
7
|
+
RubyVM::InstructionSequence.compile_option = {
|
8
|
+
:trace_instruction => false,
|
9
|
+
:specialized_instruction => false
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def teardown
|
14
|
+
set_trace_func(nil)
|
15
|
+
RubyVM::InstructionSequence.compile_option = @original_compile_option
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_iseq_brkpt
|
19
|
+
iseq = RubyVM::InstructionSequence.compile('x=1; y=2')
|
20
|
+
assert iseq
|
21
|
+
assert_equal(nil, iseq.brkpts)
|
22
|
+
assert_equal(true, iseq.brkpt_alloc)
|
23
|
+
assert_equal([], iseq.brkpts)
|
24
|
+
assert_equal(false, iseq.brkpt_alloc)
|
25
|
+
|
26
|
+
assert_equal(true, iseq.brkpt_set(0))
|
27
|
+
assert_equal(1, iseq.brkpts.size)
|
28
|
+
assert_equal(true, iseq.brkpt_get(0), 'Offset 0 should be set')
|
29
|
+
assert_equal(true, iseq.brkpt_unset(0),'Offset 0 should be unset')
|
30
|
+
assert_equal(false, iseq.brkpt_get(0), 'Offset 0 should be unset now')
|
31
|
+
assert_equal(true, iseq.brkpt_unset(0),
|
32
|
+
'Offset 0 should be unset again')
|
33
|
+
assert_raises TypeError do iseq.brkpt_get(100) end
|
34
|
+
assert_equal(true, iseq.brkpt_dealloc)
|
35
|
+
assert_equal(false, iseq.brkpt_dealloc)
|
36
|
+
assert_equal(true, iseq.brkpt_unset(0),
|
37
|
+
'Offset 0 should be unset even when deallocated')
|
38
|
+
|
39
|
+
assert_raises TypeError do iseq.brkpt_set('a') end
|
40
|
+
|
41
|
+
iseq.brkpt_set(2)
|
42
|
+
iseq.brkpt_set(4)
|
43
|
+
events = []
|
44
|
+
eval <<-EOF.gsub(/^.*?: /, "")
|
45
|
+
1: set_trace_func(Proc.new { |event, file, lineno, mid, binding, klass|
|
46
|
+
2: events << [event, lineno, mid, klass]
|
47
|
+
3: })
|
48
|
+
4: iseq.eval
|
49
|
+
5: set_trace_func(nil)
|
50
|
+
EOF
|
51
|
+
# puts iseq.disassemble
|
52
|
+
brkpt_events = events.select{|item| item[0] == 'brkpt'}
|
53
|
+
assert_equal(2, brkpt_events.size,
|
54
|
+
"Expecting to see 2 brkpts in #{events}.inspect")
|
55
|
+
assert_equal(true, iseq.brkpt_dealloc)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# We want to double-check we didn't mess up any pointers somewhere.
|
60
|
+
at_exit { GC.start }
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# Some simple tests of RubyVM::InstructionSequence#disasm, and
|
2
|
+
# #disasm_nochildren
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class TestDisasmClass < Test::Unit::TestCase
|
6
|
+
|
7
|
+
def test_basic
|
8
|
+
assert_equal(RubyVM::InstructionSequence.compile('1+2').disassemble,
|
9
|
+
RubyVM::InstructionSequence.compile('1+2').disasm)
|
10
|
+
|
11
|
+
p='def five; 5 end; five'
|
12
|
+
s1=RubyVM::InstructionSequence.compile(p).disasm
|
13
|
+
assert_equal String, s1.class, 'disasm output should be a string'
|
14
|
+
s2=RubyVM::InstructionSequence.compile(p).disasm_nochildren
|
15
|
+
assert_equal true, s1.size > s2.size
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# See that setting ISEQS__ and SCRIPT_ISEQS__ saves
|
2
|
+
# RubyVM::Instruction_sequenses
|
3
|
+
require 'test/unit'
|
4
|
+
|
5
|
+
class TestIseqAccess < Test::Unit::TestCase
|
6
|
+
def setup
|
7
|
+
old_verbosity = $VERBOSE
|
8
|
+
$VERBOSE = nil
|
9
|
+
Kernel.const_set(:ISEQS__, {})
|
10
|
+
Kernel.const_set(:SCRIPT_ISEQS__, {})
|
11
|
+
$VERBOSE = old_verbosity
|
12
|
+
end
|
13
|
+
|
14
|
+
def teardown
|
15
|
+
old_verbosity = $VERBOSE
|
16
|
+
$VERBOSE = nil
|
17
|
+
Kernel.const_set(:ISEQS__, nil)
|
18
|
+
Kernel.const_set(:SCRIPT_ISEQS__, nil)
|
19
|
+
$VERBOSE = old_verbosity
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_basic
|
23
|
+
sizes=[]
|
24
|
+
[ISEQS__, SCRIPT_ISEQS__].each do |iseq_hash|
|
25
|
+
sizes << iseq_hash.size
|
26
|
+
end
|
27
|
+
# defining five should trigger five instruction sequence additions
|
28
|
+
# to ISEQS__ and SCRIPT_ISEQS__
|
29
|
+
#
|
30
|
+
eval 'def five; 5 end'
|
31
|
+
assert_equal sizes[0], sizes[1]
|
32
|
+
[SCRIPT_ISEQS__, ISEQS__].each do |iseq_hash|
|
33
|
+
assert_equal true, iseq_hash.size > sizes.pop
|
34
|
+
assert_equal Hash, iseq_hash.class
|
35
|
+
a = iseq_hash.first
|
36
|
+
assert_equal Array, a.class
|
37
|
+
assert_equal RubyVM::InstructionSequence, iseq_hash.values[0][0].class
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Check RubyVM::InstructionSequence#arity
|
42
|
+
def test_arity
|
43
|
+
eval 'def five; 5 end'
|
44
|
+
eval 'def add(a,b); a+b end'
|
45
|
+
eval 'def splat(*a); 5 end'
|
46
|
+
[['five', 0,], ['add', 2], ['splat', -1]].each do |meth, expect|
|
47
|
+
assert_equal(expect, ISEQS__[meth][0].arity)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,151 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
# tests set_trace_func with event bitmasks, clear_trace_func,
|
4
|
+
# Newer changes
|
5
|
+
class TestSetTraceFuncAdds < Test::Unit::TestCase
|
6
|
+
|
7
|
+
# Some of the below setup is similar to what is in lib/trace_mod.rb of
|
8
|
+
# rb-trace
|
9
|
+
@@NO_EVENT_MASK = 0x0000
|
10
|
+
@@LINE_EVENT_MASK = 0x0001
|
11
|
+
@@CLASS_EVENT_MASK = 0x0002
|
12
|
+
@@END_EVENT_MASK = 0x0004
|
13
|
+
@@CALL_EVENT_MASK = 0x0008
|
14
|
+
@@RETURN_EVENT_MASK = 0x0010
|
15
|
+
@@C_CALL_EVENT_MASK = 0x0020
|
16
|
+
@@C_RETURN_EVENT_MASK = 0x0040
|
17
|
+
@@RAISE_EVENT_MASK = 0x0080
|
18
|
+
@@INSN_EVENT_MASK = 0x0100
|
19
|
+
@@ALL_EVENTS_MASK = (0xffff & ~@@INSN_EVENT_MASK)
|
20
|
+
|
21
|
+
@@EVENT2MASK = {
|
22
|
+
'line' => @@LINE_EVENT_MASK,
|
23
|
+
'call' => @@CALL_EVENT_MASK,
|
24
|
+
'return' => @@RETURN_EVENT_MASK,
|
25
|
+
'c-call' => @@C_CALL_EVENT_MASK,
|
26
|
+
'c-return' => @@C_RETURN_EVENT_MASK,
|
27
|
+
'c-raise' => @@RAISE_EVENT_MASK
|
28
|
+
}
|
29
|
+
|
30
|
+
# Convert +events+ into a Fixnum bitmask used internally by Ruby.
|
31
|
+
# Parameter +events+ should be Enumerable and each element should
|
32
|
+
# either be a Fixnum mask value or something that can be converted
|
33
|
+
# to a symbol. If the latter, the case is not important as we'll
|
34
|
+
# downcase the string representation.
|
35
|
+
def events2bitmask(event_list)
|
36
|
+
bitmask = @@NO_EVENT_MASK
|
37
|
+
event_list.each do |event|
|
38
|
+
bitmask |= @@EVENT2MASK[event]
|
39
|
+
end
|
40
|
+
return bitmask
|
41
|
+
end
|
42
|
+
|
43
|
+
def setup
|
44
|
+
@original_compile_option = RubyVM::InstructionSequence.compile_option
|
45
|
+
RubyVM::InstructionSequence.compile_option = {
|
46
|
+
:trace_instruction => true,
|
47
|
+
:specialized_instruction => false
|
48
|
+
}
|
49
|
+
@proc_template = 'Proc.new { |event, file, lineno, mid, binding, klass|
|
50
|
+
%s << [event, lineno, mid, klass]}'
|
51
|
+
end
|
52
|
+
|
53
|
+
def teardown
|
54
|
+
clear_trace_func
|
55
|
+
RubyVM::InstructionSequence.compile_option = @original_compile_option
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_eventmask
|
59
|
+
returned_tuples =
|
60
|
+
[['line', 5, :test_eventmask, self.class],
|
61
|
+
['class', 5, nil, nil],
|
62
|
+
['end', 5, nil, nil],
|
63
|
+
["leave", 5, nil, nil],
|
64
|
+
['line', 6, :test_eventmask, self.class],
|
65
|
+
["send", 6, :test_eventmask, TestSetTraceFuncAdds],
|
66
|
+
['call', 1, :five, self.class],
|
67
|
+
['line', 1, :five, self.class],
|
68
|
+
['return', 1, :five, self.class],
|
69
|
+
["leave", 1, :five, TestSetTraceFuncAdds],
|
70
|
+
["send", 6, :test_eventmask, TestSetTraceFuncAdds],
|
71
|
+
['c-call', 6, :any?, Enumerable],
|
72
|
+
['c-call', 6, :each, Array],
|
73
|
+
['line', 6, :test_eventmask, self.class],
|
74
|
+
["leave", 6, :test_eventmask, TestSetTraceFuncAdds],
|
75
|
+
['c-return', 6, :each, Array],
|
76
|
+
['c-return', 6, :any?, Enumerable],
|
77
|
+
['line', 7, :test_eventmask, self.class],
|
78
|
+
["send", 7, :test_eventmask, TestSetTraceFuncAdds],
|
79
|
+
['c-call', 7, :clear_trace_func, Kernel]]
|
80
|
+
|
81
|
+
[[], nil,
|
82
|
+
%w(line),
|
83
|
+
%w(call line),
|
84
|
+
%w(c-call c-return line),
|
85
|
+
].each do |event_list|
|
86
|
+
tuples = []
|
87
|
+
event_mask = if event_list
|
88
|
+
events2bitmask(event_list)
|
89
|
+
else
|
90
|
+
@@ALL_EVENTS_MASK
|
91
|
+
end
|
92
|
+
cmd = <<-EOF.gsub(/^.*?: /, '')
|
93
|
+
1: def five; 5 end
|
94
|
+
2: p1 = #{@proc_template}
|
95
|
+
3: set_trace_func(p1, #{event_mask})
|
96
|
+
4: class Foo; end
|
97
|
+
5: [1,2,five].any? {|n| n}
|
98
|
+
6: clear_trace_func
|
99
|
+
EOF
|
100
|
+
eval(cmd % 'tuples')
|
101
|
+
expected = if event_list
|
102
|
+
returned_tuples.select{|x| !([x[0]] & event_list).empty?}
|
103
|
+
else
|
104
|
+
returned_tuples
|
105
|
+
end
|
106
|
+
assert_equal(expected, tuples,
|
107
|
+
"Error filtering #{event_list}")
|
108
|
+
# p tuples
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_chained_hook
|
113
|
+
tuples1 = []
|
114
|
+
tuples2 = []
|
115
|
+
cmd = <<-EOF.gsub(/^.*?: /, '')
|
116
|
+
1: def five; 5 end
|
117
|
+
2: p1 = #{@proc_template}
|
118
|
+
3: p2 = #{@proc_template}
|
119
|
+
4: add_trace_func(p1, @@LINE_EVENT_MASK)
|
120
|
+
5: add_trace_func(p2, @@CALL_EVENT_MASK)
|
121
|
+
6: class Foo; end
|
122
|
+
7: [1,2,five].any? {|n| n}
|
123
|
+
EOF
|
124
|
+
eval(cmd % %w(tuples1 tuples2))
|
125
|
+
clear_trace_func
|
126
|
+
assert_equal([
|
127
|
+
["line", 7, :test_chained_hook, self.class],
|
128
|
+
["line", 8, :test_chained_hook, self.class],
|
129
|
+
["line", 9, :test_chained_hook, self.class],
|
130
|
+
["line", 1, :five, self.class],
|
131
|
+
["line", 9, :test_chained_hook, self.class],
|
132
|
+
], tuples1[0..-2],
|
133
|
+
'line filtering')
|
134
|
+
assert_equal([["call", 1, :five, self.class]], tuples2,
|
135
|
+
'call filtering')
|
136
|
+
end
|
137
|
+
|
138
|
+
def test_trace_insn
|
139
|
+
tuples = []
|
140
|
+
cmd = <<-EOF.gsub(/^.*?: /, '')
|
141
|
+
1: p = #{@proc_template}
|
142
|
+
2: add_trace_func(p, @@INSN_EVENT_MASK)
|
143
|
+
4: x = 1
|
144
|
+
3: y = 2
|
145
|
+
EOF
|
146
|
+
eval cmd % 'tuples'
|
147
|
+
clear_trace_func
|
148
|
+
assert_equal true, !tuples.empty?, 'triggered instruction events'
|
149
|
+
assert_equal true, tuples.all?{|t| 'vm-insn' == t[0]}, 'instruction events'
|
150
|
+
end
|
151
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
# tests that we a trace hook has access to the runtime exception Object
|
4
|
+
# when it is called through a raise event
|
5
|
+
|
6
|
+
class TestTracefuncRaise < Test::Unit::TestCase
|
7
|
+
|
8
|
+
def test_basic
|
9
|
+
tuples = []
|
10
|
+
p = Proc.new {
|
11
|
+
|event, file, lineno, mid, binding, klass|
|
12
|
+
tuples << klass
|
13
|
+
}
|
14
|
+
msg = 'this is a message'
|
15
|
+
set_trace_func(p, 0x0080)
|
16
|
+
begin ; x = 1/0; rescue; end
|
17
|
+
begin ; raise RuntimeError, msg; rescue; end
|
18
|
+
clear_trace_func
|
19
|
+
assert_equal(2, tuples.size,
|
20
|
+
"Wrong number of tuples captured #{tuples.inspect}")
|
21
|
+
assert_equal msg, tuples[1].message
|
22
|
+
assert_equal([ZeroDivisionError, RuntimeError], tuples.map{|t| t.class},
|
23
|
+
"Mismatched tuples classes in #{tuples.inspect}")
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
metadata
CHANGED
@@ -4,8 +4,8 @@ version: !ruby/object:Gem::Version
|
|
4
4
|
prerelease: false
|
5
5
|
segments:
|
6
6
|
- 0
|
7
|
-
-
|
8
|
-
version: "0.
|
7
|
+
- 39
|
8
|
+
version: "0.39"
|
9
9
|
platform: ruby
|
10
10
|
authors:
|
11
11
|
- R. Bernstein
|
@@ -13,7 +13,7 @@ autorequire:
|
|
13
13
|
bindir: bin
|
14
14
|
cert_chain: []
|
15
15
|
|
16
|
-
date: 2011-
|
16
|
+
date: 2011-10-27 00:00:00 -04:00
|
17
17
|
default_executable:
|
18
18
|
dependencies: []
|
19
19
|
|
@@ -69,6 +69,11 @@ files:
|
|
69
69
|
- test/unit/test-thread-trace-masks.rb
|
70
70
|
- test/unit/test-settracefunc.rb
|
71
71
|
- test/unit/test-sp-size.rb
|
72
|
+
- test/ruby/test_disasm.rb
|
73
|
+
- test/ruby/test_iseq.rb
|
74
|
+
- test/ruby/test_brkpt.rb
|
75
|
+
- test/ruby/test_tracefunc_adds.rb
|
76
|
+
- test/ruby/test_tracefunc_raise.rb
|
72
77
|
- threadframe.rd
|
73
78
|
- ext/extconf.rb
|
74
79
|
has_rdoc: true
|
@@ -80,7 +85,7 @@ rdoc_options:
|
|
80
85
|
- --main
|
81
86
|
- README.md
|
82
87
|
- --title
|
83
|
-
- ThreadFrame 0.
|
88
|
+
- ThreadFrame 0.39 Documentation
|
84
89
|
require_paths:
|
85
90
|
- lib
|
86
91
|
required_ruby_version: !ruby/object:Gem::Requirement
|