rb-threadframe 0.36 → 0.37
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 +6 -1
- data/ext/thread_frame.c +102 -10
- data/include/vm_core_mini.h +12 -8
- 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 +145 -0
- data/test/ruby/test_tracefunc_raise.rb +26 -0
- metadata +27 -23
- data/ext/thread_frame.h +0 -4
data/NEWS
CHANGED
@@ -1,4 +1,9 @@
|
|
1
|
-
|
1
|
+
Feb 1, 2011 (0.37)
|
2
|
+
- 1.9.2 patches:
|
3
|
+
* Mark VM Instruction sequences for no garbage collection if they are
|
4
|
+
referred to by a ThreadFrame Object or are in ISEQS__ or ISEQ_SCRIPTS__
|
5
|
+
|
6
|
+
December 25, 2010 (0.35, 0.36)
|
2
7
|
- 1.9.2 patches:
|
3
8
|
* Add new compile-option flags: save_tree_node and save_compile_opts
|
4
9
|
* Start saving parse tree node. (More cases need to be done.)
|
data/ext/thread_frame.c
CHANGED
@@ -1,12 +1,12 @@
|
|
1
1
|
/*
|
2
|
-
* Copyright (C) 2010 Rocky Bernstein
|
2
|
+
* Copyright (C) 2010, 2011 Rocky Bernstein
|
3
3
|
*
|
4
4
|
* Access to Ruby's rb_control_frame_t and methods for working with that.
|
5
5
|
* Things like getting a binding for a control frame.
|
6
6
|
*/
|
7
7
|
|
8
8
|
/* What release we got? */
|
9
|
-
#define THREADFRAME_VERSION "0.
|
9
|
+
#define THREADFRAME_VERSION "0.37"
|
10
10
|
|
11
11
|
#include <string.h>
|
12
12
|
#include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
|
@@ -14,6 +14,51 @@
|
|
14
14
|
#include "iseq_extra.h"
|
15
15
|
#include "thread_extra.h"
|
16
16
|
|
17
|
+
/* for GC debug */
|
18
|
+
|
19
|
+
#ifndef RUBY_MARK_FREE_DEBUG
|
20
|
+
#define RUBY_MARK_FREE_DEBUG 0
|
21
|
+
#endif
|
22
|
+
|
23
|
+
#if RUBY_MARK_FREE_DEBUG
|
24
|
+
extern int ruby_gc_debug_indent;
|
25
|
+
|
26
|
+
static void
|
27
|
+
rb_gc_debug_indent(void)
|
28
|
+
{
|
29
|
+
printf("%*s", ruby_gc_debug_indent, "");
|
30
|
+
}
|
31
|
+
|
32
|
+
static void
|
33
|
+
rb_gc_debug_body(const char *mode, const char *msg, int st, void *ptr)
|
34
|
+
{
|
35
|
+
if (st == 0) {
|
36
|
+
ruby_gc_debug_indent--;
|
37
|
+
}
|
38
|
+
rb_gc_debug_indent();
|
39
|
+
printf("%s: %s %s (%p)\n", mode, st ? "->" : "<-", msg, ptr);
|
40
|
+
|
41
|
+
if (st) {
|
42
|
+
ruby_gc_debug_indent++;
|
43
|
+
}
|
44
|
+
|
45
|
+
fflush(stdout);
|
46
|
+
}
|
47
|
+
|
48
|
+
#define RUBY_MARK_ENTER(msg) rb_gc_debug_body("mark", msg, 1, ptr)
|
49
|
+
#define RUBY_MARK_LEAVE(msg) rb_gc_debug_body("mark", msg, 0, ptr)
|
50
|
+
#define RUBY_FREE_ENTER(msg) rb_gc_debug_body("free", msg, 1, ptr)
|
51
|
+
#define RUBY_FREE_LEAVE(msg) rb_gc_debug_body("free", msg, 0, ptr)
|
52
|
+
#define RUBY_GC_INFO rb_gc_debug_indent(); printf
|
53
|
+
|
54
|
+
#else
|
55
|
+
#define RUBY_MARK_ENTER(msg)
|
56
|
+
#define RUBY_MARK_LEAVE(msg)
|
57
|
+
#define RUBY_FREE_ENTER(msg)
|
58
|
+
#define RUBY_FREE_LEAVE(msg)
|
59
|
+
#define RUBY_GC_INFO if(0)printf
|
60
|
+
#endif
|
61
|
+
|
17
62
|
/* Frames can't be detached from the control frame they live in.
|
18
63
|
So we create a structure to contain the pair.
|
19
64
|
|
@@ -45,20 +90,51 @@ static int thread_frame_stack_size_internal(rb_control_frame_t *cfp,
|
|
45
90
|
static VALUE thread_frame_type(VALUE klass);
|
46
91
|
|
47
92
|
|
93
|
+
extern void iseq_mark(void *ptr); /* in iseq.c */
|
94
|
+
|
95
|
+
/*
|
96
|
+
FIXME: I've never seen the following routine get called.
|
97
|
+
Why?
|
98
|
+
*/
|
99
|
+
static void
|
100
|
+
thread_frame_mark(void *ptr)
|
101
|
+
{
|
102
|
+
RUBY_MARK_ENTER("thread_frame");
|
103
|
+
if (ptr) {
|
104
|
+
thread_frame_t *tf = ptr;
|
105
|
+
if (tf && tf->cfp && RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq)) {
|
106
|
+
iseq_mark(tf->cfp->iseq);
|
107
|
+
}
|
108
|
+
}
|
109
|
+
}
|
110
|
+
|
111
|
+
static void
|
112
|
+
tf_free(void *ptr)
|
113
|
+
{
|
114
|
+
thread_frame_t *tf;
|
115
|
+
if (ptr) {
|
116
|
+
tf = ptr;
|
117
|
+
if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq))
|
118
|
+
tf->cfp->iseq->in_use--;
|
119
|
+
xfree(ptr);
|
120
|
+
}
|
121
|
+
}
|
122
|
+
|
48
123
|
/*
|
49
124
|
Allocate a RubyVM::ThreadFrame used by new. Less common than
|
50
|
-
thread_frame_t_alloc().
|
125
|
+
thread_frame_t_alloc(). The caller is responsible for filling in
|
126
|
+
the C struct data. Below we wrap NULL.
|
51
127
|
*/
|
52
128
|
static VALUE
|
53
129
|
thread_frame_alloc(VALUE klass)
|
54
130
|
{
|
55
|
-
return Data_Wrap_Struct(klass,
|
131
|
+
return Data_Wrap_Struct(klass, thread_frame_mark, tf_free, NULL);
|
56
132
|
}
|
57
133
|
|
58
134
|
/*
|
59
135
|
Allocate a RubyVM::ThreadFrame and set its threadframe structure.
|
60
136
|
This is the more common allocate routine since one normally doesn't
|
61
|
-
create a threadframe without <i>first</i> having
|
137
|
+
create a threadframe without <i>first</i> having something to put in it.
|
62
138
|
*/
|
63
139
|
static thread_frame_t *
|
64
140
|
thread_frame_t_alloc(VALUE tfval)
|
@@ -69,6 +145,13 @@ thread_frame_t_alloc(VALUE tfval)
|
|
69
145
|
return tf;
|
70
146
|
}
|
71
147
|
|
148
|
+
/*
|
149
|
+
Check to see if tf is valid. +true+ is returned if we can't prove
|
150
|
+
the frame is invalide. +nil+ or +false+ is returned if something is not
|
151
|
+
right. In those cases where we don't know that we have a valid frame,
|
152
|
+
we also NULL out the cfp if that hasn't been done already. This will
|
153
|
+
keep garbage collection from marking bad data.
|
154
|
+
*/
|
72
155
|
static VALUE
|
73
156
|
thread_frame_invalid_internal(thread_frame_t *tf)
|
74
157
|
{
|
@@ -77,16 +160,24 @@ thread_frame_invalid_internal(thread_frame_t *tf)
|
|
77
160
|
/* All valid frame types have 0x1 set so we will use this.
|
78
161
|
Warning: this is an undocumented assumption which may someday
|
79
162
|
be wrong. */
|
80
|
-
if (
|
163
|
+
if (!tf->cfp) return Qtrue;
|
164
|
+
if ((tf->cfp->flag & 0x1) == 0) {
|
165
|
+
tf->cfp = NULL;
|
166
|
+
return Qtrue;
|
167
|
+
}
|
81
168
|
|
82
|
-
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(tf->th, tf->cfp))
|
169
|
+
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(tf->th, tf->cfp)) {
|
170
|
+
tf->cfp = NULL;
|
83
171
|
return Qtrue;
|
172
|
+
}
|
84
173
|
if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq)) {
|
85
174
|
cmp = (0 == memcmp(tf->signature1, &(tf->cfp->iseq),
|
86
175
|
sizeof(tf->signature1)) &&
|
87
176
|
0 == memcmp(tf->signature2, &(tf->cfp->proc),
|
88
177
|
sizeof(tf->signature2)));
|
89
|
-
|
178
|
+
if (cmp) return Qfalse;
|
179
|
+
tf->cfp = NULL;
|
180
|
+
return Qtrue;
|
90
181
|
} else {
|
91
182
|
/* FIXME: figure out what to do here. Probably more work is
|
92
183
|
* needed in thread_frame_prev_internal.
|
@@ -114,6 +205,7 @@ thread_frame_invalid_internal(thread_frame_t *tf)
|
|
114
205
|
#define SAVE_FRAME(TF, TH) \
|
115
206
|
tf->th = TH; \
|
116
207
|
tf->cfp = thread_control_frame(tf->th); \
|
208
|
+
tf->cfp->iseq->in_use++; \
|
117
209
|
COPY_SIGNATURE(tf, tf->cfp); \
|
118
210
|
|
119
211
|
#define GET_THREAD_PTR \
|
@@ -134,7 +226,7 @@ thread_frame_threadframe(VALUE thval)
|
|
134
226
|
GET_THREAD_PTR;
|
135
227
|
memset(tf, 0, sizeof(thread_frame_t));
|
136
228
|
SAVE_FRAME(tf, th) ;
|
137
|
-
return Data_Wrap_Struct(rb_cThreadFrame,
|
229
|
+
return Data_Wrap_Struct(rb_cThreadFrame, thread_frame_mark, tf_free, tf);
|
138
230
|
}
|
139
231
|
|
140
232
|
#define THREAD_FRAME_SETUP \
|
@@ -714,7 +806,7 @@ thread_frame_s_current(VALUE klass)
|
|
714
806
|
{
|
715
807
|
thread_frame_t *tf = thread_frame_t_alloc(klass);
|
716
808
|
SAVE_FRAME(tf, ruby_current_thread) ;
|
717
|
-
return Data_Wrap_Struct(klass,
|
809
|
+
return Data_Wrap_Struct(klass, thread_frame_mark, tf_free, tf);
|
718
810
|
}
|
719
811
|
|
720
812
|
/*
|
data/include/vm_core_mini.h
CHANGED
@@ -94,13 +94,13 @@ typedef struct rb_iseq_struct {
|
|
94
94
|
/***************/
|
95
95
|
|
96
96
|
VALUE type; /* instruction sequence type */
|
97
|
-
VALUE name;
|
97
|
+
VALUE name; /* String: iseq name */
|
98
98
|
VALUE filename; /* file information where this sequence from */
|
99
99
|
VALUE filepath; /* real file path or nil */
|
100
100
|
VALUE *iseq; /* iseq (insn number and operands) */
|
101
101
|
VALUE *iseq_encoded; /* encoded iseq */
|
102
102
|
unsigned long iseq_size;
|
103
|
-
VALUE mark_ary;
|
103
|
+
VALUE mark_ary; /* Array: includes operands which should be GC marked */
|
104
104
|
VALUE coverage; /* coverage array */
|
105
105
|
unsigned short line_no;
|
106
106
|
|
@@ -108,7 +108,7 @@ typedef struct rb_iseq_struct {
|
|
108
108
|
struct iseq_insn_info_entry *insn_info_table;
|
109
109
|
size_t insn_info_size;
|
110
110
|
|
111
|
-
ID *local_table;
|
111
|
+
ID *local_table; /* must free */
|
112
112
|
int local_table_size;
|
113
113
|
|
114
114
|
/* method, class frame: sizeof(vars) + 1, block frame: sizeof(vars) */
|
@@ -165,7 +165,7 @@ typedef struct rb_iseq_struct {
|
|
165
165
|
/****************/
|
166
166
|
|
167
167
|
VALUE self;
|
168
|
-
VALUE orig;
|
168
|
+
VALUE orig; /* non-NULL if its data have origin */
|
169
169
|
|
170
170
|
/* block inlining */
|
171
171
|
/*
|
@@ -189,13 +189,17 @@ typedef struct rb_iseq_struct {
|
|
189
189
|
|
190
190
|
/* If this instruction sequence came from eval, the string of the
|
191
191
|
source as a String. */
|
192
|
-
VALUE
|
192
|
+
VALUE eval_source;
|
193
193
|
|
194
194
|
/* If we are saving tree nodes (a compile option), then tree_node
|
195
195
|
is the internal parse tree node representation for this
|
196
196
|
instruction sequence.
|
197
197
|
*/
|
198
198
|
NODE *tree_node;
|
199
|
+
int in_use; /* Reference count of number of times and instruction
|
200
|
+
sequence is in use such as via thread_frame object
|
201
|
+
access or is stored in ISEQS__ or SCRIPT_ISEQS.
|
202
|
+
*/
|
199
203
|
} rb_iseq_t;
|
200
204
|
|
201
205
|
enum ruby_special_exceptions {
|
@@ -234,8 +238,8 @@ typedef struct rb_vm_struct {
|
|
234
238
|
|
235
239
|
/* signal */
|
236
240
|
struct {
|
237
|
-
|
238
|
-
|
241
|
+
VALUE cmd;
|
242
|
+
int safe;
|
239
243
|
} trap_list[RUBY_NSIG];
|
240
244
|
|
241
245
|
/* hook */
|
@@ -308,8 +312,8 @@ typedef struct rb_thread_struct
|
|
308
312
|
rb_event_flag_t event_flags;
|
309
313
|
int tracing; /* 0 if not tracing. If less than 0, skip that many
|
310
314
|
C call/return pairs */
|
315
|
+
|
311
316
|
int exec_event_tracing; /* 0 if not in rb_threadptr_evec_event_hooks. */
|
312
|
-
int trace_skip_insn_count; /* # of VM instructions to skip */
|
313
317
|
|
314
318
|
/* misc */
|
315
319
|
int method_missing_reason;
|
@@ -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,145 @@
|
|
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
|
+
['line', 6, :test_eventmask, self.class],
|
64
|
+
['call', 1, :five, self.class],
|
65
|
+
['line', 1, :five, self.class],
|
66
|
+
['return', 1, :five, self.class],
|
67
|
+
['c-call', 6, :any?, Enumerable],
|
68
|
+
['c-call', 6, :each, Array],
|
69
|
+
['line', 6, :test_eventmask, self.class],
|
70
|
+
['c-return', 6, :each, Array],
|
71
|
+
['c-return', 6, :any?, Enumerable],
|
72
|
+
['line', 7, :test_eventmask, self.class],
|
73
|
+
['c-call', 7, :clear_trace_func, Kernel]]
|
74
|
+
|
75
|
+
[[], nil,
|
76
|
+
%w(line),
|
77
|
+
%w(call line),
|
78
|
+
%w(c-call c-return line),
|
79
|
+
].each do |event_list|
|
80
|
+
tuples = []
|
81
|
+
event_mask = if event_list
|
82
|
+
events2bitmask(event_list)
|
83
|
+
else
|
84
|
+
@@ALL_EVENTS_MASK
|
85
|
+
end
|
86
|
+
cmd = <<-EOF.gsub(/^.*?: /, '')
|
87
|
+
1: def five; 5 end
|
88
|
+
2: p1 = #{@proc_template}
|
89
|
+
3: set_trace_func(p1, #{event_mask})
|
90
|
+
4: class Foo; end
|
91
|
+
5: [1,2,five].any? {|n| n}
|
92
|
+
6: clear_trace_func
|
93
|
+
EOF
|
94
|
+
eval(cmd % 'tuples')
|
95
|
+
expected = if event_list
|
96
|
+
returned_tuples.select{|x| !([x[0]] & event_list).empty?}
|
97
|
+
else
|
98
|
+
returned_tuples
|
99
|
+
end
|
100
|
+
assert_equal(expected, tuples,
|
101
|
+
"Error filtering #{event_list}")
|
102
|
+
# p tuples
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
def test_chained_hook
|
107
|
+
tuples1 = []
|
108
|
+
tuples2 = []
|
109
|
+
cmd = <<-EOF.gsub(/^.*?: /, '')
|
110
|
+
1: def five; 5 end
|
111
|
+
2: p1 = #{@proc_template}
|
112
|
+
3: p2 = #{@proc_template}
|
113
|
+
4: add_trace_func(p1, @@LINE_EVENT_MASK)
|
114
|
+
5: add_trace_func(p2, @@CALL_EVENT_MASK)
|
115
|
+
6: class Foo; end
|
116
|
+
7: [1,2,five].any? {|n| n}
|
117
|
+
EOF
|
118
|
+
eval(cmd % %w(tuples1 tuples2))
|
119
|
+
clear_trace_func
|
120
|
+
assert_equal([
|
121
|
+
["line", 7, :test_chained_hook, self.class],
|
122
|
+
["line", 8, :test_chained_hook, self.class],
|
123
|
+
["line", 9, :test_chained_hook, self.class],
|
124
|
+
["line", 1, :five, self.class],
|
125
|
+
["line", 9, :test_chained_hook, self.class],
|
126
|
+
], tuples1[0..-2],
|
127
|
+
'line filtering')
|
128
|
+
assert_equal([["call", 1, :five, self.class]], tuples2,
|
129
|
+
'call filtering')
|
130
|
+
end
|
131
|
+
|
132
|
+
def test_trace_insn
|
133
|
+
tuples = []
|
134
|
+
cmd = <<-EOF.gsub(/^.*?: /, '')
|
135
|
+
1: p = #{@proc_template}
|
136
|
+
2: add_trace_func(p, @@INSN_EVENT_MASK)
|
137
|
+
4: x = 1
|
138
|
+
3: y = 2
|
139
|
+
EOF
|
140
|
+
eval cmd % 'tuples'
|
141
|
+
clear_trace_func
|
142
|
+
assert_equal true, !tuples.empty?, 'triggered instruction events'
|
143
|
+
assert_equal true, tuples.all?{|t| 'vm-insn' == t[0]}, 'instruction events'
|
144
|
+
end
|
145
|
+
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
|
+
- 37
|
8
|
+
version: "0.37"
|
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:
|
16
|
+
date: 2011-02-01 00:00:00 -05:00
|
17
17
|
default_executable:
|
18
18
|
dependencies: []
|
19
19
|
|
@@ -33,42 +33,46 @@ files:
|
|
33
33
|
- Makefile
|
34
34
|
- LICENSE
|
35
35
|
- NEWS
|
36
|
-
- include/thread_pthread.h
|
37
36
|
- include/node.h
|
38
|
-
- include/method_mini.h
|
39
37
|
- include/ruby19_externs.h
|
38
|
+
- include/method_mini.h
|
39
|
+
- include/thread_pthread.h
|
40
40
|
- include/vm_core_mini.h
|
41
|
-
- lib/iseq_extra.rb
|
42
41
|
- lib/thread_frame.rb
|
42
|
+
- lib/iseq_extra.rb
|
43
43
|
- ext/iseq_extra.c
|
44
44
|
- ext/thread_frame.c
|
45
|
-
- ext/proc_extra.c
|
46
45
|
- ext/thread_extra.c
|
47
|
-
- ext/
|
48
|
-
- ext/proc_extra.h
|
49
|
-
- ext/thread_pthread.h
|
50
|
-
- ext/node.h
|
46
|
+
- ext/proc_extra.c
|
51
47
|
- ext/thread_extra.h
|
52
|
-
- ext/iseq_extra.h
|
53
48
|
- ext/iseq_mini.h
|
54
|
-
-
|
49
|
+
- ext/iseq_extra.h
|
50
|
+
- ext/node.h
|
51
|
+
- ext/thread_pthread.h
|
52
|
+
- ext/proc_extra.h
|
53
|
+
- test/ruby/test_tracefunc_adds.rb
|
54
|
+
- test/ruby/test_brkpt.rb
|
55
|
+
- test/ruby/test_disasm.rb
|
56
|
+
- test/ruby/test_iseq.rb
|
57
|
+
- test/ruby/test_tracefunc_raise.rb
|
58
|
+
- test/unit/test-sp-size.rb
|
59
|
+
- test/unit/test-iseq-brkpt.rb
|
55
60
|
- test/unit/test-iseq-save.rb
|
61
|
+
- test/unit/test-binding.rb
|
56
62
|
- test/unit/test-prev.rb
|
57
|
-
- test/unit/test-source.rb
|
58
63
|
- test/unit/test-lib-iseq.rb
|
59
64
|
- test/unit/test-return-stop.rb
|
60
|
-
- test/unit/test-
|
61
|
-
- test/unit/test-invalid.rb
|
62
|
-
- test/unit/test-argc.rb
|
65
|
+
- test/unit/test-iseq.rb
|
63
66
|
- test/unit/cfunc-use.rb
|
64
|
-
- test/unit/test-
|
67
|
+
- test/unit/test-argc.rb
|
68
|
+
- test/unit/test-invalid.rb
|
65
69
|
- test/unit/test-lib-iseq-extra.rb
|
66
|
-
- test/unit/test-thread.rb
|
67
|
-
- test/unit/test-iseq-brkpt.rb
|
68
|
-
- test/unit/test-iseq.rb
|
69
70
|
- test/unit/test-thread-trace-masks.rb
|
71
|
+
- test/unit/test-proc.rb
|
72
|
+
- test/unit/test-thread.rb
|
70
73
|
- test/unit/test-settracefunc.rb
|
71
|
-
- test/unit/test-
|
74
|
+
- test/unit/test-trace.rb
|
75
|
+
- test/unit/test-source.rb
|
72
76
|
- threadframe.rd
|
73
77
|
- ext/extconf.rb
|
74
78
|
has_rdoc: true
|
@@ -80,7 +84,7 @@ rdoc_options:
|
|
80
84
|
- --main
|
81
85
|
- README.md
|
82
86
|
- --title
|
83
|
-
- ThreadFrame 0.
|
87
|
+
- ThreadFrame 0.37 Documentation
|
84
88
|
require_paths:
|
85
89
|
- lib
|
86
90
|
required_ruby_version: !ruby/object:Gem::Requirement
|
data/ext/thread_frame.h
DELETED