rb-threadframe 0.32
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/LICENSE +23 -0
- data/NEWS +2 -0
- data/README.md +9 -0
- data/Rakefile +162 -0
- data/ext/extconf.rb +12 -0
- data/ext/iseq_extra.c +404 -0
- data/ext/iseq_extra.h +8 -0
- data/ext/iseq_mini.h +41 -0
- data/ext/node.h +483 -0
- data/ext/proc_extra.c +108 -0
- data/ext/proc_extra.h +3 -0
- data/ext/thread_extra.c +84 -0
- data/ext/thread_extra.h +5 -0
- data/ext/thread_frame.c +1022 -0
- data/ext/thread_frame.h +4 -0
- data/ext/thread_pthread.h +24 -0
- data/include/method_mini.h +90 -0
- data/include/node.h +483 -0
- data/include/ruby19_externs.h +36 -0
- data/include/thread_pthread.h +24 -0
- data/include/vm_core_mini.h +357 -0
- data/lib/iseq_extra.rb +89 -0
- data/lib/thread_frame.rb +3 -0
- data/test/unit/cfunc-use.rb +11 -0
- data/test/unit/test-argc.rb +45 -0
- data/test/unit/test-binding.rb +44 -0
- data/test/unit/test-invalid.rb +40 -0
- data/test/unit/test-iseq-brkpt.rb +61 -0
- data/test/unit/test-iseq.rb +121 -0
- data/test/unit/test-lib-iseq-extra.rb +57 -0
- data/test/unit/test-prev.rb +54 -0
- data/test/unit/test-proc.rb +23 -0
- data/test/unit/test-return-stop.rb +64 -0
- data/test/unit/test-settracefunc.rb +315 -0
- data/test/unit/test-source.rb +104 -0
- data/test/unit/test-sp-size.rb +45 -0
- data/test/unit/test-thread-trace-masks.rb +90 -0
- data/test/unit/test-thread.rb +168 -0
- data/test/unit/test-trace.rb +55 -0
- data/threadframe.rd +163 -0
- metadata +110 -0
data/ext/proc_extra.c
ADDED
@@ -0,0 +1,108 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (C) 2010 Rocky Bernstein
|
3
|
+
*/
|
4
|
+
#if 0 /* The following is to fake out rdoc, until I find a better fix. */
|
5
|
+
/*
|
6
|
+
* Additions to the RubyVM::Proc classes
|
7
|
+
*/
|
8
|
+
VALUE rb_cProc = rb_define_class("Proc", ...)
|
9
|
+
#endif
|
10
|
+
|
11
|
+
#include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
|
12
|
+
#include "../include/ruby19_externs.h"
|
13
|
+
|
14
|
+
struct METHOD {
|
15
|
+
VALUE recv;
|
16
|
+
VALUE rclass;
|
17
|
+
ID id;
|
18
|
+
rb_method_entry_t me;
|
19
|
+
};
|
20
|
+
|
21
|
+
/*
|
22
|
+
* call-seq:
|
23
|
+
* Proc#iseq -> RubyVM::InstructionSequence
|
24
|
+
*
|
25
|
+
* Returns the instruction sequence for a Proc object.
|
26
|
+
*/
|
27
|
+
VALUE
|
28
|
+
proc_iseq(VALUE self)
|
29
|
+
{
|
30
|
+
rb_proc_t *proc;
|
31
|
+
rb_iseq_t *iseq;
|
32
|
+
VALUE rb_iseq;
|
33
|
+
GetProcPtr(self, proc);
|
34
|
+
iseq = proc->block.iseq;
|
35
|
+
if (!iseq
|
36
|
+
#if 0
|
37
|
+
/* Our iseq struct isn't complete enough to contain self. */
|
38
|
+
|| !RUBY_VM_NORMAL_ISEQ_P(iseq->self)
|
39
|
+
#endif
|
40
|
+
)
|
41
|
+
return Qnil;
|
42
|
+
rb_iseq = iseq_alloc_shared(rb_cISeq);
|
43
|
+
RDATA(rb_iseq)->data = iseq;
|
44
|
+
return rb_iseq;
|
45
|
+
}
|
46
|
+
|
47
|
+
/* Defined in Ruby 1.9 proc.c */
|
48
|
+
extern rb_iseq_t *rb_method_get_iseq(VALUE method);
|
49
|
+
|
50
|
+
#if 0 /* The following is to fake out rdoc, until I find a better fix. */
|
51
|
+
/*
|
52
|
+
* Additions to the RubyVM::Method class
|
53
|
+
*/
|
54
|
+
VALUE rb_cIseq = rb_define_class("Method", ...)
|
55
|
+
#endif
|
56
|
+
/*
|
57
|
+
* call-seq:
|
58
|
+
* Method#iseq -> RubyVM::InstructionSequence
|
59
|
+
*
|
60
|
+
* Returns the instruction sequence of a Method object.
|
61
|
+
*/
|
62
|
+
VALUE
|
63
|
+
method_iseq(VALUE self)
|
64
|
+
{
|
65
|
+
VALUE rb_iseq;
|
66
|
+
rb_iseq_t *iseq = rb_method_get_iseq(self);
|
67
|
+
if (!iseq) return Qnil;
|
68
|
+
rb_iseq = iseq_alloc_shared(rb_cISeq);
|
69
|
+
RDATA(rb_iseq)->data = iseq;
|
70
|
+
return rb_iseq;
|
71
|
+
}
|
72
|
+
|
73
|
+
/*
|
74
|
+
* call-seq:
|
75
|
+
* Method#alias_count -> Fixnum
|
76
|
+
*
|
77
|
+
* number of aliases a method has
|
78
|
+
*/
|
79
|
+
VALUE
|
80
|
+
method_alias_count(VALUE self)
|
81
|
+
{
|
82
|
+
struct METHOD *m1 = (struct METHOD *)DATA_PTR(self);
|
83
|
+
return INT2FIX(m1->me.def->alias_count);
|
84
|
+
}
|
85
|
+
|
86
|
+
/*
|
87
|
+
* call-seq:
|
88
|
+
* Method#original_id - Original name of method
|
89
|
+
*/
|
90
|
+
VALUE
|
91
|
+
method_original_id(VALUE self)
|
92
|
+
{
|
93
|
+
struct METHOD *m1 = (struct METHOD *)DATA_PTR(self);
|
94
|
+
return ID2SYM(m1->me.def->original_id);
|
95
|
+
}
|
96
|
+
|
97
|
+
|
98
|
+
void
|
99
|
+
Init_proc_extra(void)
|
100
|
+
{
|
101
|
+
/* Additions to Proc */
|
102
|
+
rb_define_method(rb_cProc, "iseq", proc_iseq, 0);
|
103
|
+
|
104
|
+
/* Additions to Method */
|
105
|
+
rb_define_method(rb_cMethod, "iseq", method_iseq, 0);
|
106
|
+
rb_define_method(rb_cMethod, "alias_count", method_alias_count, 0);
|
107
|
+
rb_define_method(rb_cMethod, "original_id", method_original_id, 0);
|
108
|
+
}
|
data/ext/proc_extra.h
ADDED
data/ext/thread_extra.c
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (C) 2010 Rocky Bernstein
|
3
|
+
*/
|
4
|
+
#if 0 /* The following is to fake out rdoc, until I find a better fix. */
|
5
|
+
/*
|
6
|
+
* Additions to the RubyVM::Method class
|
7
|
+
*/
|
8
|
+
VALUE rb_cThread = rb_define_class("Method", ...)
|
9
|
+
#endif
|
10
|
+
|
11
|
+
#include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
|
12
|
+
#include "thread_extra.h"
|
13
|
+
|
14
|
+
/*
|
15
|
+
* call-seq:
|
16
|
+
* Thread#exec_event_tracing -> bool
|
17
|
+
*
|
18
|
+
* Returns the value of the thread event-hook tracing.
|
19
|
+
*/
|
20
|
+
VALUE
|
21
|
+
thread_exec_event_tracing(VALUE self)
|
22
|
+
{
|
23
|
+
rb_thread_t *th;
|
24
|
+
GetThreadPtr(self, th);
|
25
|
+
return th->exec_event_tracing ? Qtrue : Qfalse;
|
26
|
+
}
|
27
|
+
|
28
|
+
/*
|
29
|
+
* call-seq:
|
30
|
+
* Thread#exec_event_tracing=(new_value)
|
31
|
+
*
|
32
|
+
* Sets the value of thread event-hook tracing.
|
33
|
+
*/
|
34
|
+
VALUE
|
35
|
+
thread_exec_event_tracing_set(VALUE self, VALUE new_value)
|
36
|
+
{
|
37
|
+
rb_thread_t *th;
|
38
|
+
GetThreadPtr(self, th);
|
39
|
+
|
40
|
+
th->exec_event_tracing = RTEST(new_value) ? Qtrue : Qfalse;
|
41
|
+
return th->exec_event_tracing;
|
42
|
+
}
|
43
|
+
|
44
|
+
/*
|
45
|
+
* call-seq:
|
46
|
+
* Thread#tracing -> bool
|
47
|
+
*
|
48
|
+
* Returns the value of the thread event-hook tracing.
|
49
|
+
*/
|
50
|
+
VALUE
|
51
|
+
thread_tracing(VALUE self)
|
52
|
+
{
|
53
|
+
rb_thread_t *th;
|
54
|
+
GetThreadPtr(self, th);
|
55
|
+
return th->tracing ? Qtrue : Qfalse;
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
* call-seq:
|
60
|
+
* Thread#tracing= bool
|
61
|
+
*
|
62
|
+
* Sets the value of thread event-hook tracing.
|
63
|
+
*/
|
64
|
+
VALUE
|
65
|
+
thread_tracing_set(VALUE self, VALUE new_value)
|
66
|
+
{
|
67
|
+
rb_thread_t *th;
|
68
|
+
GetThreadPtr(self, th);
|
69
|
+
|
70
|
+
th->tracing = RTEST(new_value) ? Qtrue : Qfalse;
|
71
|
+
return th->tracing;
|
72
|
+
}
|
73
|
+
|
74
|
+
void
|
75
|
+
Init_thread_extra(void)
|
76
|
+
{
|
77
|
+
/* Additions to Thread class */
|
78
|
+
rb_define_method(rb_cThread, "exec_event_tracing=",
|
79
|
+
thread_exec_event_tracing_set, 1);
|
80
|
+
rb_define_method(rb_cThread, "exec_event_tracing",
|
81
|
+
thread_exec_event_tracing, 0);
|
82
|
+
rb_define_method(rb_cThread, "tracing=", thread_tracing_set, 1);
|
83
|
+
rb_define_method(rb_cThread, "tracing", thread_tracing, 0);
|
84
|
+
}
|
data/ext/thread_extra.h
ADDED
data/ext/thread_frame.c
ADDED
@@ -0,0 +1,1022 @@
|
|
1
|
+
/*
|
2
|
+
* Copyright (C) 2010 Rocky Bernstein
|
3
|
+
*
|
4
|
+
* Access to Ruby's rb_control_frame_t and methods for working with that.
|
5
|
+
* Things like getting a binding for a control frame.
|
6
|
+
*/
|
7
|
+
|
8
|
+
/* What release we got? */
|
9
|
+
#define THREADFRAME_VERSION "0.32"
|
10
|
+
|
11
|
+
#include <string.h>
|
12
|
+
#include "../include/vm_core_mini.h" /* Pulls in ruby.h and node.h */
|
13
|
+
#include "proc_extra.h"
|
14
|
+
#include "iseq_extra.h"
|
15
|
+
#include "thread_extra.h"
|
16
|
+
|
17
|
+
/* Frames can't be detached from the control frame they live in.
|
18
|
+
So we create a structure to contain the pair.
|
19
|
+
|
20
|
+
The signature fields are used to weakly verify the validity of cfp.
|
21
|
+
it stores to contents of fields in cfp on allocation. This, the
|
22
|
+
validity of "th" pointing to a valid thread, and cfp pointing to valid
|
23
|
+
location inside the frame area we use to check that this structure
|
24
|
+
is valid. */
|
25
|
+
|
26
|
+
typedef struct
|
27
|
+
{
|
28
|
+
rb_thread_t *th;
|
29
|
+
rb_control_frame_t *cfp;
|
30
|
+
VALUE *signature1[3]; /* iseq, flag, self */
|
31
|
+
VALUE *signature2[1]; /* proc */
|
32
|
+
} thread_frame_t;
|
33
|
+
|
34
|
+
#include "../include/ruby19_externs.h"
|
35
|
+
|
36
|
+
VALUE rb_cThreadFrame; /* ThreadFrame class */
|
37
|
+
VALUE rb_eThreadFrameError; /* Error raised on invalid frames. */
|
38
|
+
|
39
|
+
/* Static forward declarations */
|
40
|
+
static VALUE thread_frame_iseq(VALUE klass);
|
41
|
+
static VALUE thread_frame_prev_internal(rb_control_frame_t *prev_cfp,
|
42
|
+
rb_thread_t *th, int n);
|
43
|
+
static int thread_frame_stack_size_internal(rb_control_frame_t *cfp,
|
44
|
+
rb_thread_t *th);
|
45
|
+
static VALUE thread_frame_type(VALUE klass);
|
46
|
+
|
47
|
+
|
48
|
+
/*
|
49
|
+
Allocate a RubyVM::ThreadFrame used by new. Less common than
|
50
|
+
thread_frame_t_alloc().
|
51
|
+
*/
|
52
|
+
static VALUE
|
53
|
+
thread_frame_alloc(VALUE klass)
|
54
|
+
{
|
55
|
+
return Data_Wrap_Struct(klass, NULL, xfree, 0);
|
56
|
+
}
|
57
|
+
|
58
|
+
/*
|
59
|
+
Allocate a RubyVM::ThreadFrame and set its threadframe structure.
|
60
|
+
This is the more common allocate routine since one normally doesn't
|
61
|
+
create a threadframe without <i>first</i> having seomthing to put in it.
|
62
|
+
*/
|
63
|
+
static thread_frame_t *
|
64
|
+
thread_frame_t_alloc(VALUE tfval)
|
65
|
+
{
|
66
|
+
thread_frame_t *tf = ALLOC(thread_frame_t);
|
67
|
+
memset(tf, 0, sizeof(thread_frame_t));
|
68
|
+
DATA_PTR(tfval) = tf;
|
69
|
+
return tf;
|
70
|
+
}
|
71
|
+
|
72
|
+
static VALUE
|
73
|
+
thread_frame_invalid_internal(thread_frame_t *tf)
|
74
|
+
{
|
75
|
+
int cmp;
|
76
|
+
|
77
|
+
/* All valid frame types have 0x1 set so we will use this.
|
78
|
+
Warning: this is an undocumented assumption which may someday
|
79
|
+
be wrong. */
|
80
|
+
if ((tf->cfp->flag & 0x1) == 0) return Qtrue;
|
81
|
+
|
82
|
+
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(tf->th, tf->cfp))
|
83
|
+
return Qtrue;
|
84
|
+
if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq)) {
|
85
|
+
cmp = (0 == memcmp(tf->signature1, &(tf->cfp->iseq),
|
86
|
+
sizeof(tf->signature1)) &&
|
87
|
+
0 == memcmp(tf->signature2, &(tf->cfp->proc),
|
88
|
+
sizeof(tf->signature2)));
|
89
|
+
return cmp ? Qfalse : Qtrue;
|
90
|
+
} else {
|
91
|
+
/* FIXME: figure out what to do here. Probably more work is
|
92
|
+
* needed in thread_frame_prev_internal.
|
93
|
+
*/
|
94
|
+
return Qnil;
|
95
|
+
}
|
96
|
+
}
|
97
|
+
|
98
|
+
/*
|
99
|
+
COPY_SIGNATURE saves some invariant data from the frame for
|
100
|
+
comparison later when the frame is used again.
|
101
|
+
|
102
|
+
Even though fields like iseq may not be valid for things C function
|
103
|
+
frames, nevertheless all we care about is whether they could change
|
104
|
+
or not over the course of evaluation. Hving more data to compare
|
105
|
+
against to verify whether a frame is valid is helpful. If the data
|
106
|
+
is random unitialized data, that's even better. Again just so long as
|
107
|
+
that random data doesn't change in the course of normal use.
|
108
|
+
|
109
|
+
FIXME: There are probably more fields which could be saved. */
|
110
|
+
#define COPY_SIGNATURE(tf, cfp) \
|
111
|
+
memcpy(tf->signature1, &(cfp->iseq), sizeof(tf->signature1)); \
|
112
|
+
memcpy(tf->signature2, &(cfp->proc), sizeof(tf->signature2))
|
113
|
+
|
114
|
+
#define SAVE_FRAME(TF, TH) \
|
115
|
+
tf->th = TH; \
|
116
|
+
tf->cfp = thread_control_frame(tf->th); \
|
117
|
+
COPY_SIGNATURE(tf, tf->cfp); \
|
118
|
+
|
119
|
+
#define GET_THREAD_PTR \
|
120
|
+
rb_thread_t *th; \
|
121
|
+
GetThreadPtr(thval, th)
|
122
|
+
|
123
|
+
|
124
|
+
/*
|
125
|
+
* call-seq:
|
126
|
+
* RubyVM::ThreadFrame#threadframe -> thread_frame_object
|
127
|
+
*
|
128
|
+
* Returns a RubyVM::ThreadFrame object for the given thread.
|
129
|
+
*/
|
130
|
+
static VALUE
|
131
|
+
thread_frame_threadframe(VALUE thval)
|
132
|
+
{
|
133
|
+
thread_frame_t *tf = ALLOC(thread_frame_t);
|
134
|
+
GET_THREAD_PTR;
|
135
|
+
memset(tf, 0, sizeof(thread_frame_t));
|
136
|
+
SAVE_FRAME(tf, th) ;
|
137
|
+
return Data_Wrap_Struct(rb_cThreadFrame, NULL, xfree, tf);
|
138
|
+
}
|
139
|
+
|
140
|
+
#define THREAD_FRAME_SETUP \
|
141
|
+
thread_frame_t *tf; \
|
142
|
+
Data_Get_Struct(klass, thread_frame_t, tf)
|
143
|
+
|
144
|
+
#define THREAD_FRAME_SETUP_WITH_ERROR \
|
145
|
+
THREAD_FRAME_SETUP; \
|
146
|
+
if (Qtrue == thread_frame_invalid_internal(tf)) \
|
147
|
+
rb_raise(rb_eThreadFrameError, "invalid frame")
|
148
|
+
|
149
|
+
#define THREAD_FRAME_FIELD_METHOD(FIELD) \
|
150
|
+
static VALUE \
|
151
|
+
thread_frame_##FIELD(VALUE klass) \
|
152
|
+
{ \
|
153
|
+
THREAD_FRAME_SETUP ; \
|
154
|
+
return tf->cfp->FIELD; \
|
155
|
+
}
|
156
|
+
|
157
|
+
#define THREAD_FRAME_FP_METHOD(REG) \
|
158
|
+
VALUE \
|
159
|
+
thread_frame_##REG(VALUE klass, VALUE index) \
|
160
|
+
{ \
|
161
|
+
if (!FIXNUM_P(index)) { \
|
162
|
+
rb_raise(rb_eTypeError, "integer argument expected"); \
|
163
|
+
} else { \
|
164
|
+
long int i = FIX2INT(index); \
|
165
|
+
THREAD_FRAME_SETUP_WITH_ERROR ; \
|
166
|
+
/* FIXME: check index is within range. */ \
|
167
|
+
return tf->cfp->REG[-i]; /* stack grows "down" */ \
|
168
|
+
} \
|
169
|
+
}
|
170
|
+
|
171
|
+
#if 0
|
172
|
+
/*
|
173
|
+
* call-seq:
|
174
|
+
* RubyVM::ThreadFrame#dfp(n) -> object
|
175
|
+
*
|
176
|
+
* Returns a RubyVM object stored at dfp position <i>i</i>. The top object
|
177
|
+
* is position 0.
|
178
|
+
*/
|
179
|
+
static VALUE
|
180
|
+
thread_frame_dfp(VALUE klass, VALUE index)
|
181
|
+
{
|
182
|
+
/* handled by THREAD_FRAME_FP_METHOD macro; */
|
183
|
+
}
|
184
|
+
#endif
|
185
|
+
/* The above declaration is to make RDOC happy. */
|
186
|
+
THREAD_FRAME_FP_METHOD(dfp)
|
187
|
+
|
188
|
+
/*
|
189
|
+
* call-seq:
|
190
|
+
* RubyVM::ThreadFrame#lfp(i) -> object
|
191
|
+
*
|
192
|
+
* Returns a RubyVM object stored at lfp position <i>i</i>. The top object
|
193
|
+
* is position 0. Negative values of <i>i</i> count from the end.
|
194
|
+
*/
|
195
|
+
static VALUE
|
196
|
+
thread_frame_lfp(VALUE klass, VALUE index)
|
197
|
+
{
|
198
|
+
if (!FIXNUM_P(index)) {
|
199
|
+
rb_raise(rb_eTypeError, "integer argument expected");
|
200
|
+
} else {
|
201
|
+
long int i = FIX2INT(index);
|
202
|
+
long int size;
|
203
|
+
|
204
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
205
|
+
|
206
|
+
size = tf->cfp->iseq->local_size;
|
207
|
+
if (i < 0) i = size - i;
|
208
|
+
|
209
|
+
if (i > size)
|
210
|
+
rb_raise(rb_eIndexError,
|
211
|
+
"local frame index %ld should be in the range %ld .. %ld",
|
212
|
+
i, -size, size-1);
|
213
|
+
|
214
|
+
return tf->cfp->lfp[-i]; /* stack grows "down" */
|
215
|
+
}
|
216
|
+
}
|
217
|
+
|
218
|
+
#if 0
|
219
|
+
/*
|
220
|
+
* call-seq:
|
221
|
+
* RubyVM::ThreadFrame#sp(n) -> object
|
222
|
+
*
|
223
|
+
* Returns a RubyVM object stored at stack position <i>i</i>. The top object
|
224
|
+
* is position 0. 1 is the next object.
|
225
|
+
*/
|
226
|
+
VALUE
|
227
|
+
thread_frame_sp(VALUE klass, VALUE index)
|
228
|
+
{
|
229
|
+
/* handled by THREAD_FRAME_FP_METHOD macro; */
|
230
|
+
}
|
231
|
+
#endif
|
232
|
+
/* The above declaration is to make RDOC happy.
|
233
|
+
FIXME: Figure out a way to check if "index" is valid!
|
234
|
+
*/
|
235
|
+
THREAD_FRAME_FP_METHOD(sp)
|
236
|
+
|
237
|
+
static long int
|
238
|
+
thread_frame_sp_size_internal(thread_frame_t *tf)
|
239
|
+
{
|
240
|
+
rb_control_frame_t *prev_cfp;
|
241
|
+
long int ret_val;
|
242
|
+
prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(tf->cfp);
|
243
|
+
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(tf->th, prev_cfp))
|
244
|
+
return Qnil;
|
245
|
+
ret_val = tf->cfp->sp - prev_cfp->sp - 1;
|
246
|
+
/* FIXME: Why For C Functions we tack on 3? Possibly we're doing
|
247
|
+
the previous frame and possibly that's where the action is that
|
248
|
+
we should be looking at.? */
|
249
|
+
if (RUBYVM_CFUNC_FRAME_P(tf->cfp)) ret_val += 3;
|
250
|
+
return ret_val;
|
251
|
+
}
|
252
|
+
|
253
|
+
/*
|
254
|
+
* call-seq:
|
255
|
+
* RubyVM::ThreadFrame#sp_size -> FixNum
|
256
|
+
*
|
257
|
+
* Returns the number of stack or sp entries in the current
|
258
|
+
* frame. That is the, number values that have been pushed onto the
|
259
|
+
* stack since the current call. This is different than
|
260
|
+
* RubyVM::ThreadFrame#stack_size which counts the number of frames in
|
261
|
+
* the call stack. +nil+ is returned if there is an error.
|
262
|
+
*/
|
263
|
+
VALUE
|
264
|
+
thread_frame_sp_size(VALUE klass)
|
265
|
+
{
|
266
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
267
|
+
return INT2FIX(thread_frame_sp_size_internal(tf));
|
268
|
+
}
|
269
|
+
|
270
|
+
/*
|
271
|
+
* call-seq:
|
272
|
+
* RubyVM::ThreadFrame#sp_set(n, newvalue) -> object
|
273
|
+
*
|
274
|
+
* Sets VM stack position <i>n</i> to <i>newvalue</i>. The top object
|
275
|
+
* is position 0. 1 is the next object.
|
276
|
+
*/
|
277
|
+
static VALUE
|
278
|
+
thread_frame_sp_set(VALUE klass, VALUE index, VALUE newvalue)
|
279
|
+
{
|
280
|
+
if (!FIXNUM_P(index)) {
|
281
|
+
rb_raise(rb_eTypeError, "integer argument expected");
|
282
|
+
} else {
|
283
|
+
long int i = FIX2INT(index);
|
284
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
285
|
+
if (i <= thread_frame_sp_size_internal(tf)) {
|
286
|
+
/* stack grows "down" */
|
287
|
+
tf->cfp->sp[-i] = newvalue;
|
288
|
+
} else {
|
289
|
+
rb_raise(rb_eArgError, "argument too big");
|
290
|
+
}
|
291
|
+
return newvalue;
|
292
|
+
}
|
293
|
+
}
|
294
|
+
|
295
|
+
#ifndef NO_reg_pc
|
296
|
+
/*
|
297
|
+
* call-seq:
|
298
|
+
* RubyVM::ThreadFrame#pc_offset=
|
299
|
+
*
|
300
|
+
* Sets pc to the offset given.
|
301
|
+
* WARNING, this is pretty dangerous. You need to set this to a valid
|
302
|
+
* instruction offset since little checking is done.
|
303
|
+
*/
|
304
|
+
static VALUE
|
305
|
+
thread_frame_set_pc_offset(VALUE klass, VALUE offset_val)
|
306
|
+
{
|
307
|
+
int offset;
|
308
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
309
|
+
|
310
|
+
if (!FIXNUM_P(offset_val)) {
|
311
|
+
rb_raise(rb_eTypeError, "integer argument expected");
|
312
|
+
} else {
|
313
|
+
offset = FIX2INT(offset_val);
|
314
|
+
if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq) &&
|
315
|
+
(tf->cfp->pc != 0 && tf->cfp->iseq != 0)) {
|
316
|
+
tf->cfp->pc = tf->cfp->iseq->iseq_encoded + offset;
|
317
|
+
}
|
318
|
+
}
|
319
|
+
return Qtrue;
|
320
|
+
}
|
321
|
+
#endif
|
322
|
+
|
323
|
+
#if 0
|
324
|
+
/*
|
325
|
+
* call-seq:
|
326
|
+
* RubyVM::ThreadFrame#flag -> Fixnum
|
327
|
+
*
|
328
|
+
* Returns the frame flags, a FIXNUM which should be interpreted as a
|
329
|
+
* bitmask.
|
330
|
+
*
|
331
|
+
*/
|
332
|
+
static VALUE thread_frame_flag(VALUE klass)
|
333
|
+
{
|
334
|
+
/* handled by THREAD_FRAME_FIELD_METHOD macro; */
|
335
|
+
}
|
336
|
+
/* The above declaration is to make RDOC happy. */
|
337
|
+
#endif
|
338
|
+
THREAD_FRAME_FIELD_METHOD(flag) ;
|
339
|
+
|
340
|
+
/*
|
341
|
+
* call-seq:
|
342
|
+
* RubyVM::ThreadFrame#argc -> Fixnum
|
343
|
+
*
|
344
|
+
* Returns the number of arguments that were actually passed
|
345
|
+
* in the call to this frame. This differs from arity when
|
346
|
+
* arity can take optional or "splat"ted parameters.
|
347
|
+
*
|
348
|
+
*/
|
349
|
+
static VALUE
|
350
|
+
thread_frame_argc(VALUE klass)
|
351
|
+
{
|
352
|
+
THREAD_FRAME_SETUP_WITH_ERROR;
|
353
|
+
|
354
|
+
if (RUBYVM_CFUNC_FRAME_P(tf->cfp)) {
|
355
|
+
return INT2FIX(tf->cfp->me->def->body.cfunc.actual_argc);
|
356
|
+
} else if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq)) {
|
357
|
+
return iseq_argc(thread_frame_iseq(klass));
|
358
|
+
} else
|
359
|
+
return Qnil;
|
360
|
+
}
|
361
|
+
|
362
|
+
/*
|
363
|
+
* call-seq:
|
364
|
+
* RubyVM::ThreadFrame#arity -> Fixnum
|
365
|
+
*
|
366
|
+
* Returns the number of arguments that would not be ignored.
|
367
|
+
* See Ruby 1.9 proc_arity of proc.c
|
368
|
+
*
|
369
|
+
*/
|
370
|
+
static VALUE
|
371
|
+
thread_frame_arity(VALUE klass)
|
372
|
+
{
|
373
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
374
|
+
|
375
|
+
if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq)) {
|
376
|
+
return rb_iseq_arity(thread_frame_iseq(klass));
|
377
|
+
} else if (RUBYVM_CFUNC_FRAME_P(tf->cfp)) {
|
378
|
+
return INT2FIX(tf->cfp->me->def->body.cfunc.argc);
|
379
|
+
} else
|
380
|
+
return Qnil;
|
381
|
+
}
|
382
|
+
|
383
|
+
/*
|
384
|
+
* call-seq:
|
385
|
+
* RubyVM::ThreadFrame#binding -> binding
|
386
|
+
*
|
387
|
+
* Returns a binding for a given thread frame.
|
388
|
+
*/
|
389
|
+
static VALUE
|
390
|
+
thread_frame_binding(VALUE klass)
|
391
|
+
{
|
392
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
393
|
+
|
394
|
+
{
|
395
|
+
rb_binding_t *bind = 0;
|
396
|
+
VALUE bindval = rb_binding_frame_new(tf->th, tf->cfp);
|
397
|
+
GetBindingPtr(bindval, bind);
|
398
|
+
bind->line_no = rb_vm_get_sourceline(tf->cfp);
|
399
|
+
if (tf->cfp->iseq) {
|
400
|
+
bind->filename = tf->cfp->iseq->filename;
|
401
|
+
} else {
|
402
|
+
bind->filename = tf->th->vm->progname;
|
403
|
+
}
|
404
|
+
return bindval;
|
405
|
+
}
|
406
|
+
/* NOTREACHED */
|
407
|
+
return Qnil;
|
408
|
+
}
|
409
|
+
|
410
|
+
/*
|
411
|
+
* call-seq:
|
412
|
+
* RubyVM::ThreadFrame#equal(tf) -> bool
|
413
|
+
*
|
414
|
+
* Returns true if two thread frames are equal.
|
415
|
+
*/
|
416
|
+
static VALUE
|
417
|
+
thread_frame_equal(VALUE klass, VALUE tfval2)
|
418
|
+
{
|
419
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
420
|
+
|
421
|
+
{
|
422
|
+
thread_frame_t *tf2;
|
423
|
+
if (!rb_obj_is_kind_of(tfval2, rb_cThreadFrame)) {
|
424
|
+
rb_raise(rb_eTypeError,
|
425
|
+
"comparison argument must be an instance of %s (is %s)",
|
426
|
+
rb_obj_classname(klass), rb_obj_classname(tfval2));
|
427
|
+
}
|
428
|
+
Data_Get_Struct(tfval2, thread_frame_t, tf2);
|
429
|
+
if (Qtrue == thread_frame_invalid_internal(tf2))
|
430
|
+
rb_raise(rb_eThreadFrameError, "invalid frame");
|
431
|
+
|
432
|
+
/* And just when you thought I'd never get around to the
|
433
|
+
actual comparison...
|
434
|
+
|
435
|
+
Comparing cfp's should be enough, but we'll throw in the thread
|
436
|
+
for good measure.
|
437
|
+
*/
|
438
|
+
return (tf->th == tf2->th && tf->cfp == tf2->cfp)
|
439
|
+
? Qtrue : Qfalse;
|
440
|
+
}
|
441
|
+
/* NOTREACHED */
|
442
|
+
return Qnil;
|
443
|
+
}
|
444
|
+
|
445
|
+
/*
|
446
|
+
* call-seq:
|
447
|
+
* RubyVM::ThreadFrame.new(thread) -> thread_frame_object
|
448
|
+
*
|
449
|
+
* Returns an RubyVM::ThreadFrame object which can contains dynamic frame
|
450
|
+
* information. Don't use this directly. Instead use one of the
|
451
|
+
* class methods.
|
452
|
+
*
|
453
|
+
* RubyVM::ThreadFrame::VERSION => 0.1
|
454
|
+
* RubyVM::ThreadFrame::current.flag => 72
|
455
|
+
* RubyVM::ThreadFrame::current.proc => false
|
456
|
+
* RubyVM::ThreadFrame::current.self => 'main'
|
457
|
+
*/
|
458
|
+
static VALUE
|
459
|
+
thread_frame_initialize(VALUE tfval, VALUE thval)
|
460
|
+
{
|
461
|
+
thread_frame_t *tf = thread_frame_t_alloc(tfval);
|
462
|
+
GET_THREAD_PTR ;
|
463
|
+
memset(tf, 0, sizeof(thread_frame_t));
|
464
|
+
DATA_PTR(tfval) = tf;
|
465
|
+
SAVE_FRAME(tf, th) ;
|
466
|
+
return tfval;
|
467
|
+
}
|
468
|
+
|
469
|
+
/*
|
470
|
+
* call-seq:
|
471
|
+
* RubyVM::ThreadFrame#invalid? -> Boolean
|
472
|
+
*
|
473
|
+
* Returns true if the frame is no longer valid. On the other hand,
|
474
|
+
* since the test we use is weak, returning false might not mean the
|
475
|
+
* frame is valid, just that we can't disprove that it is not invalid.
|
476
|
+
*
|
477
|
+
* It is suggested that frames are used in a way that ensures they will
|
478
|
+
* be valid. In particular frames should have local scope and frames to
|
479
|
+
* threads other than the running one should be stopped while the frame
|
480
|
+
* variable is active.
|
481
|
+
*/
|
482
|
+
static VALUE
|
483
|
+
thread_frame_invalid(VALUE klass)
|
484
|
+
{
|
485
|
+
THREAD_FRAME_SETUP ;
|
486
|
+
return thread_frame_invalid_internal(tf);
|
487
|
+
}
|
488
|
+
|
489
|
+
static VALUE
|
490
|
+
thread_frame_is_return_stop(VALUE klass)
|
491
|
+
{
|
492
|
+
THREAD_FRAME_SETUP ;
|
493
|
+
return (tf->cfp->tracing & VM_FRAME_TRACE_RETURN) ? Qtrue : Qfalse;
|
494
|
+
}
|
495
|
+
|
496
|
+
static VALUE
|
497
|
+
thread_frame_is_trace_off(VALUE klass)
|
498
|
+
{
|
499
|
+
THREAD_FRAME_SETUP ;
|
500
|
+
return (tf->cfp->tracing & VM_FRAME_TRACE_OFF) ? Qtrue : Qfalse;
|
501
|
+
}
|
502
|
+
|
503
|
+
/*
|
504
|
+
* call-seq:
|
505
|
+
* ThreadFrame#method -> String or nil
|
506
|
+
*
|
507
|
+
* Returns the method associated with the frame or nil of none.
|
508
|
+
* ThreadFrameError can be raised if the threadframe
|
509
|
+
* object is no longer valid.
|
510
|
+
*/
|
511
|
+
static VALUE
|
512
|
+
thread_frame_method(VALUE klass)
|
513
|
+
{
|
514
|
+
THREAD_FRAME_SETUP_WITH_ERROR ; \
|
515
|
+
|
516
|
+
switch (VM_FRAME_TYPE(tf->cfp)) {
|
517
|
+
case VM_FRAME_MAGIC_BLOCK:
|
518
|
+
case VM_FRAME_MAGIC_EVAL:
|
519
|
+
case VM_FRAME_MAGIC_LAMBDA:
|
520
|
+
case VM_FRAME_MAGIC_METHOD:
|
521
|
+
case VM_FRAME_MAGIC_TOP:
|
522
|
+
if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq))
|
523
|
+
return tf->cfp->iseq->name;
|
524
|
+
else
|
525
|
+
return Qnil; /* unknown */
|
526
|
+
case VM_FRAME_MAGIC_CFUNC: {
|
527
|
+
const char *method_name = rb_id2name(tf->cfp->me->def->original_id);
|
528
|
+
return rb_str_new2((NULL == method_name) ? "" : method_name );
|
529
|
+
}
|
530
|
+
default:
|
531
|
+
/* FIXME */
|
532
|
+
return thread_frame_type(klass);
|
533
|
+
}
|
534
|
+
/* NOTREACHED */
|
535
|
+
return Qnil;
|
536
|
+
}
|
537
|
+
|
538
|
+
/*
|
539
|
+
* call-seq:
|
540
|
+
* ThreadFrame#pc_offset -> Fixnum
|
541
|
+
*
|
542
|
+
* Returns the offset inside the iseq or "program-counter offset" or -1
|
543
|
+
* If invalid/unstarted. ThreadFrameError can be raised if the threadframe
|
544
|
+
* object is no longer valid.
|
545
|
+
*/
|
546
|
+
static VALUE
|
547
|
+
thread_frame_pc_offset(VALUE klass)
|
548
|
+
{
|
549
|
+
unsigned long pc;
|
550
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
551
|
+
|
552
|
+
if (RUBY_VM_NORMAL_ISEQ_P(tf->cfp->iseq) &&
|
553
|
+
(tf->cfp->pc != 0 && tf->cfp->iseq != 0)) {
|
554
|
+
pc = tf->cfp->pc - tf->cfp->iseq->iseq_encoded;
|
555
|
+
return INT2FIX(pc);
|
556
|
+
} else {
|
557
|
+
return INT2FIX(-1);
|
558
|
+
}
|
559
|
+
}
|
560
|
+
|
561
|
+
|
562
|
+
/*
|
563
|
+
* call-seq:
|
564
|
+
* ThreadFrame#iseq -> ISeq
|
565
|
+
*
|
566
|
+
* Returns an instruction sequence object from the instruction sequence
|
567
|
+
* found inside the ThreadFrame object or nil if there is none.
|
568
|
+
*
|
569
|
+
*/
|
570
|
+
static VALUE
|
571
|
+
thread_frame_iseq(VALUE klass)
|
572
|
+
{
|
573
|
+
rb_iseq_t *iseq;
|
574
|
+
VALUE rb_iseq;
|
575
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
576
|
+
iseq = tf->cfp->iseq;
|
577
|
+
if (!iseq) return Qnil;
|
578
|
+
rb_iseq = iseq_alloc_shared(rb_cISeq);
|
579
|
+
RDATA(rb_iseq)->data = iseq;
|
580
|
+
return rb_iseq;
|
581
|
+
}
|
582
|
+
|
583
|
+
/*
|
584
|
+
See the above thread_frame_prev comment for what's going on here.
|
585
|
+
*/
|
586
|
+
static VALUE
|
587
|
+
thread_frame_next(VALUE klass)
|
588
|
+
{
|
589
|
+
rb_control_frame_t *cfp = NULL;
|
590
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
591
|
+
cfp = RUBY_VM_NEXT_CONTROL_FRAME(tf->cfp);
|
592
|
+
|
593
|
+
if ((void *)(cfp) <= (void *)(tf->th->stack))
|
594
|
+
return Qnil;
|
595
|
+
else {
|
596
|
+
thread_frame_t *next_tf;
|
597
|
+
VALUE next = thread_frame_alloc(rb_cThreadFrame);
|
598
|
+
thread_frame_t_alloc(next);
|
599
|
+
Data_Get_Struct(next, thread_frame_t, next_tf);
|
600
|
+
next_tf->th = tf->th;
|
601
|
+
next_tf->cfp = cfp;
|
602
|
+
COPY_SIGNATURE(tf, cfp);
|
603
|
+
return next;
|
604
|
+
}
|
605
|
+
}
|
606
|
+
|
607
|
+
/*
|
608
|
+
* call-seq:
|
609
|
+
* RubyVM::ThreadFrame#prev(n=1) -> thread_frame_object
|
610
|
+
*
|
611
|
+
* Returns a RubyVM::ThreadFrame object for the frame prior to the
|
612
|
+
* ThreadFrame object or +nil+ if there is none. Setting n=0 just
|
613
|
+
* returns the object passed. A negative starts from the end. So
|
614
|
+
* prev(-1) is the top frame. Counts outside of the range
|
615
|
+
* -stack_size .. stack_size-1 exceed the the range of the stack and
|
616
|
+
* return nil.
|
617
|
+
*
|
618
|
+
*/
|
619
|
+
VALUE
|
620
|
+
thread_frame_prev(int argc, VALUE *argv, VALUE klass)
|
621
|
+
{
|
622
|
+
VALUE nv;
|
623
|
+
int n;
|
624
|
+
|
625
|
+
THREAD_FRAME_SETUP_WITH_ERROR ;
|
626
|
+
|
627
|
+
rb_scan_args(argc, argv, "01", &nv);
|
628
|
+
|
629
|
+
if (Qnil == nv)
|
630
|
+
n = 1;
|
631
|
+
else if (!FIXNUM_P(nv)) {
|
632
|
+
rb_raise(rb_eTypeError, "integer argument expected");
|
633
|
+
} else
|
634
|
+
n = FIX2INT(nv);
|
635
|
+
|
636
|
+
if (n < 0) {
|
637
|
+
int stack_size = thread_frame_stack_size_internal(tf->cfp, tf->th);
|
638
|
+
if (-n > stack_size) return Qnil;
|
639
|
+
n = stack_size + n;
|
640
|
+
}
|
641
|
+
if (n == 0) return klass;
|
642
|
+
return thread_frame_prev_internal(tf->cfp, tf->th, n);
|
643
|
+
}
|
644
|
+
|
645
|
+
/*
|
646
|
+
See the above thread_frame_prev comment for what's going on here.
|
647
|
+
*/
|
648
|
+
static VALUE
|
649
|
+
thread_frame_prev_internal(rb_control_frame_t *prev_cfp, rb_thread_t *th,
|
650
|
+
int n)
|
651
|
+
{
|
652
|
+
thread_frame_t *tf;
|
653
|
+
VALUE prev;
|
654
|
+
rb_control_frame_t *cfp = NULL;
|
655
|
+
|
656
|
+
for (; n > 0; n--) {
|
657
|
+
cfp = prev_cfp;
|
658
|
+
prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
659
|
+
if (VM_FRAME_TYPE(prev_cfp) == VM_FRAME_MAGIC_FINISH) {
|
660
|
+
prev_cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(prev_cfp);
|
661
|
+
}
|
662
|
+
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, prev_cfp))
|
663
|
+
return Qnil;
|
664
|
+
}
|
665
|
+
if (!cfp) return Qnil;
|
666
|
+
|
667
|
+
prev = thread_frame_alloc(rb_cThreadFrame);
|
668
|
+
thread_frame_t_alloc(prev);
|
669
|
+
Data_Get_Struct(prev, thread_frame_t, tf);
|
670
|
+
tf->th = th;
|
671
|
+
tf->cfp = prev_cfp;
|
672
|
+
COPY_SIGNATURE(tf, tf->cfp);
|
673
|
+
return prev;
|
674
|
+
}
|
675
|
+
|
676
|
+
THREAD_FRAME_FIELD_METHOD(proc) ;
|
677
|
+
THREAD_FRAME_FIELD_METHOD(self) ;
|
678
|
+
|
679
|
+
static VALUE
|
680
|
+
thread_frame_return_stop_set(VALUE klass, VALUE boolval)
|
681
|
+
{
|
682
|
+
short int boolmask = !(NIL_P(boolval) || Qfalse == boolval);
|
683
|
+
THREAD_FRAME_SETUP ;
|
684
|
+
|
685
|
+
if (boolmask)
|
686
|
+
tf->cfp->tracing |= VM_FRAME_TRACE_RETURN;
|
687
|
+
else
|
688
|
+
tf->cfp->tracing &= ~VM_FRAME_TRACE_RETURN;
|
689
|
+
return boolval;
|
690
|
+
}
|
691
|
+
|
692
|
+
static VALUE
|
693
|
+
thread_frame_trace_off_set(VALUE klass, VALUE boolval)
|
694
|
+
{
|
695
|
+
short int boolmask = !(NIL_P(boolval) || Qfalse == boolval);
|
696
|
+
THREAD_FRAME_SETUP ;
|
697
|
+
|
698
|
+
if (boolmask)
|
699
|
+
tf->cfp->tracing |= VM_FRAME_TRACE_OFF;
|
700
|
+
else
|
701
|
+
tf->cfp->tracing &= ~VM_FRAME_TRACE_OFF;
|
702
|
+
return boolval;
|
703
|
+
}
|
704
|
+
|
705
|
+
/*
|
706
|
+
* call-seq:
|
707
|
+
* RubyVM::ThreadFrame::current -> thread_frame_object
|
708
|
+
*
|
709
|
+
* Returns a ThreadFrame object for the currently executing thread.
|
710
|
+
* Same as: RubyVM::ThreadFrame.new(Thread::current)
|
711
|
+
*/
|
712
|
+
static VALUE
|
713
|
+
thread_frame_s_current(VALUE klass)
|
714
|
+
{
|
715
|
+
thread_frame_t *tf = thread_frame_t_alloc(klass);
|
716
|
+
SAVE_FRAME(tf, ruby_current_thread) ;
|
717
|
+
return Data_Wrap_Struct(klass, NULL, xfree, tf);
|
718
|
+
}
|
719
|
+
|
720
|
+
/*
|
721
|
+
* call-seq:
|
722
|
+
* RubyVM::ThreadFrame::prev(thread) -> threadframe_object
|
723
|
+
* RubyVM::ThreadFrame::prev(thread, n) -> threadframe_object
|
724
|
+
*
|
725
|
+
* Returns a RubyVM::ThreadFrame for the frame prior to the
|
726
|
+
* Thread object passed or nil if there is none. The default value for n
|
727
|
+
* is 1. 0 just returns the object passed.
|
728
|
+
* Negative counts or counts exceeding the stack will return nil.
|
729
|
+
*/
|
730
|
+
static VALUE
|
731
|
+
thread_frame_s_prev(int argc, VALUE *argv, VALUE klass)
|
732
|
+
{
|
733
|
+
VALUE thval;
|
734
|
+
VALUE nv;
|
735
|
+
int n;
|
736
|
+
|
737
|
+
rb_scan_args(argc, argv, "11", &thval, &nv);
|
738
|
+
|
739
|
+
if (Qfalse == rb_obj_is_kind_of(thval, rb_cThread))
|
740
|
+
rb_raise(rb_eTypeError,
|
741
|
+
"ThreadFrame object needed for first argument");
|
742
|
+
|
743
|
+
GET_THREAD_PTR ;
|
744
|
+
|
745
|
+
if (Qnil == nv)
|
746
|
+
n = 1;
|
747
|
+
else if (!FIXNUM_P(nv)) {
|
748
|
+
rb_raise(rb_eTypeError, "Fixnum needed for second argument");
|
749
|
+
} else
|
750
|
+
n = FIX2INT(nv);
|
751
|
+
|
752
|
+
if (n == 0) {
|
753
|
+
return thread_frame_s_current(klass);
|
754
|
+
} else if (n < 0) {
|
755
|
+
int stack_size = thread_frame_stack_size_internal(th->cfp, th);
|
756
|
+
if (-n > stack_size) return Qnil;
|
757
|
+
n = stack_size + n;
|
758
|
+
}
|
759
|
+
|
760
|
+
return thread_frame_prev_internal(th->cfp, th, n);
|
761
|
+
}
|
762
|
+
|
763
|
+
/*
|
764
|
+
* call-seq:
|
765
|
+
* RubyVM::ThreadFrame#source_container() -> [Type, String]
|
766
|
+
*
|
767
|
+
* Returns a tuple representing kind of container, e.g. file
|
768
|
+
* eval'd string object, and the name of the container. If file,
|
769
|
+
* it would be a file name. If an eval'd string it might be the string.
|
770
|
+
*/
|
771
|
+
static VALUE
|
772
|
+
thread_frame_source_container(VALUE klass)
|
773
|
+
{
|
774
|
+
VALUE filename = Qnil;
|
775
|
+
const char *contain_type;
|
776
|
+
rb_control_frame_t *cfp;
|
777
|
+
int is_eval = 0;
|
778
|
+
|
779
|
+
THREAD_FRAME_SETUP ;
|
780
|
+
|
781
|
+
for ( cfp = tf->cfp; cfp && !cfp->iseq && RUBYVM_CFUNC_FRAME_P(cfp);
|
782
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp) ) ;
|
783
|
+
|
784
|
+
|
785
|
+
if (cfp->iseq)
|
786
|
+
filename = cfp->iseq->filename;
|
787
|
+
else {
|
788
|
+
if (tf->th->vm->progname)
|
789
|
+
filename = tf->th->vm->progname;
|
790
|
+
else
|
791
|
+
return Qnil;
|
792
|
+
}
|
793
|
+
|
794
|
+
contain_type = source_container_type(filename);
|
795
|
+
|
796
|
+
is_eval = ( 0 == strcmp("string", contain_type)
|
797
|
+
&& VM_FRAME_MAGIC_EVAL == VM_FRAME_TYPE(tf->cfp) );
|
798
|
+
|
799
|
+
if ( is_eval ) {
|
800
|
+
/* Try to pick up string from stack. */
|
801
|
+
VALUE prev = thread_frame_prev_internal(tf->cfp, tf->th, 1);
|
802
|
+
thread_frame_t *prev_tf;
|
803
|
+
Data_Get_Struct(prev, thread_frame_t, prev_tf);
|
804
|
+
|
805
|
+
if (RUBYVM_CFUNC_FRAME_P(prev_tf->cfp) &&
|
806
|
+
thread_frame_stack_size_internal(prev_tf->cfp, prev_tf->th) >= 3)
|
807
|
+
filename = thread_frame_sp(prev, INT2FIX(3));
|
808
|
+
}
|
809
|
+
|
810
|
+
return rb_ary_new3(2, rb_str_new2(contain_type), filename);
|
811
|
+
}
|
812
|
+
|
813
|
+
/*
|
814
|
+
* call-seq:
|
815
|
+
* RubyVM::ThreadFrame#source_location() -> Array
|
816
|
+
*
|
817
|
+
* Returns an array of source location positions that match
|
818
|
+
* +tf.instruction_offset+. A source location position is left
|
819
|
+
* implementation dependent. It could be line number, a line number
|
820
|
+
* and start and end column, or a start line number, start column, end
|
821
|
+
* line number, end column.
|
822
|
+
*/
|
823
|
+
static VALUE
|
824
|
+
thread_frame_source_location(VALUE klass)
|
825
|
+
{
|
826
|
+
rb_control_frame_t *cfp;
|
827
|
+
THREAD_FRAME_SETUP ;
|
828
|
+
|
829
|
+
/* For now, it appears like we have line numbers only when there
|
830
|
+
is an instruction sequence. The heuristic that is used by
|
831
|
+
vm_backtrace_each of vm.c seems to be to use the line number of
|
832
|
+
the closest control frame that has an instruction sequence.
|
833
|
+
FIXME: investigate whether this is always the most accurate location. If
|
834
|
+
not, improve.
|
835
|
+
*/
|
836
|
+
for ( cfp = tf->cfp; cfp && !cfp->iseq && RUBYVM_CFUNC_FRAME_P(cfp);
|
837
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp) ) ;
|
838
|
+
|
839
|
+
return (cfp->iseq)
|
840
|
+
/* NOTE: for now sourceline returns a single int. In the
|
841
|
+
future it might return an array of ints.
|
842
|
+
*/
|
843
|
+
? rb_ary_new3(1, INT2FIX(rb_vm_get_sourceline(cfp)))
|
844
|
+
: Qnil;
|
845
|
+
}
|
846
|
+
|
847
|
+
/*
|
848
|
+
* call-seq:
|
849
|
+
* RubyVM::ThreadFrame#stack_size -> Fixnum;
|
850
|
+
*
|
851
|
+
* Returns a count of the number of frames including the current one.
|
852
|
+
* ThreadFrame#prev(ThreadFrame#stack_size) = nil
|
853
|
+
* ThreadFrame#prev(ThreadFrame#stack_size-1) = top frame
|
854
|
+
*
|
855
|
+
*
|
856
|
+
*/
|
857
|
+
static VALUE
|
858
|
+
thread_frame_stack_size(VALUE klass)
|
859
|
+
{
|
860
|
+
THREAD_FRAME_SETUP ;
|
861
|
+
return INT2FIX(thread_frame_stack_size_internal(tf->cfp, tf->th));
|
862
|
+
}
|
863
|
+
|
864
|
+
/*
|
865
|
+
See the above thread_frame_stack_size comment for what's going on here.
|
866
|
+
*/
|
867
|
+
static int
|
868
|
+
thread_frame_stack_size_internal(rb_control_frame_t *cfp, rb_thread_t *th)
|
869
|
+
{
|
870
|
+
int n;
|
871
|
+
for ( n = 0;
|
872
|
+
!RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp);
|
873
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp)) {
|
874
|
+
n++;
|
875
|
+
if (VM_FRAME_TYPE(cfp) == VM_FRAME_MAGIC_FINISH) {
|
876
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
877
|
+
if (RUBY_VM_CONTROL_FRAME_STACK_OVERFLOW_P(th, cfp))
|
878
|
+
break;
|
879
|
+
}
|
880
|
+
}
|
881
|
+
return n;
|
882
|
+
}
|
883
|
+
|
884
|
+
/*
|
885
|
+
* call-seq:
|
886
|
+
* RubyVM::ThreadFrame#thread -> thread
|
887
|
+
*
|
888
|
+
* Returns the thread object for the thread frame.
|
889
|
+
*/
|
890
|
+
static VALUE
|
891
|
+
thread_frame_thread(VALUE klass)
|
892
|
+
{
|
893
|
+
THREAD_FRAME_SETUP ;
|
894
|
+
return tf->th->self;
|
895
|
+
}
|
896
|
+
|
897
|
+
/* Extracted from vm_dump.c. Would be nice to have this routine put there
|
898
|
+
and used in both places. */
|
899
|
+
static const char *
|
900
|
+
frame_magic2str(rb_control_frame_t *cfp)
|
901
|
+
{
|
902
|
+
switch (VM_FRAME_TYPE(cfp)) {
|
903
|
+
case VM_FRAME_MAGIC_TOP:
|
904
|
+
return "TOP";
|
905
|
+
case VM_FRAME_MAGIC_METHOD:
|
906
|
+
return "METHOD";
|
907
|
+
case VM_FRAME_MAGIC_CLASS:
|
908
|
+
return "CLASS";
|
909
|
+
case VM_FRAME_MAGIC_BLOCK:
|
910
|
+
return "BLOCK";
|
911
|
+
case VM_FRAME_MAGIC_FINISH:
|
912
|
+
return "FINISH";
|
913
|
+
case VM_FRAME_MAGIC_CFUNC:
|
914
|
+
return "CFUNC";
|
915
|
+
case VM_FRAME_MAGIC_PROC:
|
916
|
+
return "PROC";
|
917
|
+
case VM_FRAME_MAGIC_LAMBDA:
|
918
|
+
return "LAMBDA";
|
919
|
+
case VM_FRAME_MAGIC_IFUNC:
|
920
|
+
return "IFUNC";
|
921
|
+
case VM_FRAME_MAGIC_EVAL:
|
922
|
+
return "EVAL";
|
923
|
+
case 0:
|
924
|
+
return "------";
|
925
|
+
default:
|
926
|
+
return "(none)";
|
927
|
+
}
|
928
|
+
/* NOTREACHED */
|
929
|
+
return "?";
|
930
|
+
}
|
931
|
+
|
932
|
+
/*
|
933
|
+
* call-seq:
|
934
|
+
* ThreadFrame#type -> String
|
935
|
+
*
|
936
|
+
* Returns the kind of frame. Basically interprets VM_FRAME_MAGIC for
|
937
|
+
* tf->cfp->flag
|
938
|
+
*/
|
939
|
+
static VALUE
|
940
|
+
thread_frame_type(VALUE klass)
|
941
|
+
{
|
942
|
+
THREAD_FRAME_SETUP ;
|
943
|
+
return rb_str_new2(frame_magic2str(tf->cfp));
|
944
|
+
}
|
945
|
+
|
946
|
+
void
|
947
|
+
Init_thread_frame(void)
|
948
|
+
{
|
949
|
+
/* Additions to RubyVM */
|
950
|
+
rb_cThreadFrame = rb_define_class_under(rb_cRubyVM, "ThreadFrame",
|
951
|
+
rb_cObject);
|
952
|
+
rb_define_method(rb_cThread, "threadframe", thread_frame_threadframe, 0);
|
953
|
+
|
954
|
+
/* Thread::Frame */
|
955
|
+
rb_define_const(rb_cThreadFrame, "VERSION",
|
956
|
+
rb_str_new2(THREADFRAME_VERSION));
|
957
|
+
rb_define_alloc_func(rb_cThreadFrame, thread_frame_alloc);
|
958
|
+
|
959
|
+
rb_define_method(rb_cThreadFrame, "invalid?", thread_frame_invalid, 0);
|
960
|
+
|
961
|
+
/* RubyVM::ThreadFrame */
|
962
|
+
rb_define_method(rb_cThreadFrame, "argc", thread_frame_argc, 0);
|
963
|
+
rb_define_method(rb_cThreadFrame, "arity", thread_frame_arity, 0);
|
964
|
+
rb_define_method(rb_cThreadFrame, "binding", thread_frame_binding, 0);
|
965
|
+
rb_define_method(rb_cThreadFrame, "dfp", thread_frame_dfp, 1);
|
966
|
+
rb_define_method(rb_cThreadFrame, "flag", thread_frame_flag, 0);
|
967
|
+
rb_define_method(rb_cThreadFrame, "initialize", thread_frame_initialize, 1);
|
968
|
+
rb_define_method(rb_cThreadFrame, "iseq", thread_frame_iseq, 0);
|
969
|
+
rb_define_method(rb_cThreadFrame, "lfp", thread_frame_lfp, 1);
|
970
|
+
rb_define_method(rb_cThreadFrame, "method", thread_frame_method, 0);
|
971
|
+
rb_define_method(rb_cThreadFrame, "next", thread_frame_next, 0);
|
972
|
+
rb_define_method(rb_cThreadFrame, "pc_offset", thread_frame_pc_offset, 0);
|
973
|
+
rb_define_method(rb_cThreadFrame, "prev", thread_frame_prev, -1);
|
974
|
+
rb_define_method(rb_cThreadFrame, "proc", thread_frame_proc, 0);
|
975
|
+
rb_define_method(rb_cThreadFrame, "return_stop=", thread_frame_return_stop_set, 1);
|
976
|
+
rb_define_method(rb_cThreadFrame, "return_stop?", thread_frame_is_return_stop, 0);
|
977
|
+
rb_define_method(rb_cThreadFrame, "self", thread_frame_self, 0);
|
978
|
+
rb_define_method(rb_cThreadFrame, "source_container",
|
979
|
+
thread_frame_source_container, 0);
|
980
|
+
rb_define_method(rb_cThreadFrame, "source_location",
|
981
|
+
thread_frame_source_location, 0);
|
982
|
+
|
983
|
+
/* sp[] and sp[]= would be neater, but that would require making sp an
|
984
|
+
object which I am not sure I want to do.
|
985
|
+
*/
|
986
|
+
rb_define_method(rb_cThreadFrame, "sp", thread_frame_sp, 1);
|
987
|
+
rb_define_method(rb_cThreadFrame, "sp_set", thread_frame_sp_set, 2);
|
988
|
+
rb_define_method(rb_cThreadFrame, "sp_size", thread_frame_sp_size, 0);
|
989
|
+
|
990
|
+
/* I think I like the more explicit stack_size over size or length. */
|
991
|
+
rb_define_method(rb_cThreadFrame, "stack_size",
|
992
|
+
thread_frame_stack_size, 0);
|
993
|
+
|
994
|
+
rb_define_method(rb_cThreadFrame, "thread", thread_frame_thread, 0);
|
995
|
+
rb_define_method(rb_cThreadFrame, "trace_off?", thread_frame_is_trace_off, 0);
|
996
|
+
rb_define_method(rb_cThreadFrame, "trace_off=", thread_frame_trace_off_set, 1);
|
997
|
+
rb_define_method(rb_cThreadFrame, "type", thread_frame_type, 0);
|
998
|
+
|
999
|
+
rb_define_method(rb_cThreadFrame, "equal?",
|
1000
|
+
thread_frame_equal, 1);
|
1001
|
+
|
1002
|
+
#ifndef NO_reg_pc
|
1003
|
+
rb_define_method(rb_cThreadFrame, "pc_offset=",
|
1004
|
+
thread_frame_set_pc_offset, 1);
|
1005
|
+
#endif
|
1006
|
+
|
1007
|
+
|
1008
|
+
rb_eThreadFrameError = rb_define_class("ThreadFrameError",
|
1009
|
+
rb_eStandardError);
|
1010
|
+
|
1011
|
+
rb_define_singleton_method(rb_cThreadFrame, "prev",
|
1012
|
+
thread_frame_s_prev, -1);
|
1013
|
+
rb_define_singleton_method(rb_cThreadFrame, "current",
|
1014
|
+
thread_frame_s_current, 0);
|
1015
|
+
|
1016
|
+
|
1017
|
+
/* Perform the other C extension initializations. */
|
1018
|
+
Init_iseq_extra();
|
1019
|
+
Init_proc_extra();
|
1020
|
+
Init_thread_extra();
|
1021
|
+
Init_thread_extra();
|
1022
|
+
}
|