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