rb-threadframe 0.38 → 0.39
Sign up to get free protection for your applications and to get access to all the features.
- 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
|