ruby-debug-base193 0.0.1
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/AUTHORS +10 -0
- data/CHANGES +334 -0
- data/LICENSE +23 -0
- data/README +122 -0
- data/Rakefile +266 -0
- data/ext/ruby_debug/breakpoint.c +578 -0
- data/ext/ruby_debug/extconf.rb +39 -0
- data/ext/ruby_debug/ruby_debug.c +2541 -0
- data/ext/ruby_debug/ruby_debug.h +138 -0
- data/lib/ChangeLog +1065 -0
- data/lib/ruby-debug-base.rb +304 -0
- data/test/base/base.rb +56 -0
- data/test/base/binding.rb +31 -0
- data/test/base/catchpoint.rb +23 -0
- metadata +127 -0
@@ -0,0 +1,39 @@
|
|
1
|
+
require "mkmf"
|
2
|
+
require "ruby_core_source"
|
3
|
+
|
4
|
+
if RUBY_VERSION < "1.9"
|
5
|
+
STDERR.print("Ruby version is too old\n")
|
6
|
+
exit(1)
|
7
|
+
end
|
8
|
+
|
9
|
+
hdrs = proc {
|
10
|
+
iseqs = %w[vm_core.h iseq.h]
|
11
|
+
begin
|
12
|
+
have_struct_member("rb_method_entry_t", "called_id", "method.h") or
|
13
|
+
have_struct_member("rb_control_frame_t", "method_id", "method.h")
|
14
|
+
end and
|
15
|
+
have_header("vm_core.h") and have_header("iseq.h") and have_header("insns.inc") and
|
16
|
+
have_header("insns_info.inc") and have_header("eval_intern.h") or break
|
17
|
+
have_type("struct iseq_line_info_entry", iseqs) or
|
18
|
+
have_type("struct iseq_insn_info_entry", iseqs) or
|
19
|
+
break
|
20
|
+
if checking_for(checking_message("if rb_iseq_compile_with_option was added an argument filepath")) do
|
21
|
+
try_compile(<<SRC)
|
22
|
+
#include <ruby.h>
|
23
|
+
#include "vm_core.h"
|
24
|
+
extern VALUE rb_iseq_new_main(NODE *node, VALUE filename, VALUE filepath);
|
25
|
+
SRC
|
26
|
+
end
|
27
|
+
$defs << '-DRB_ISEQ_COMPILE_5ARGS'
|
28
|
+
end
|
29
|
+
}
|
30
|
+
|
31
|
+
dir_config("ruby")
|
32
|
+
if !Ruby_core_source::create_makefile_with_core(hdrs, "ruby_debug")
|
33
|
+
STDERR.print("Makefile creation failed\n")
|
34
|
+
STDERR.print("*************************************************************\n\n")
|
35
|
+
STDERR.print(" NOTE: For Ruby 1.9 installation instructions, please see:\n\n")
|
36
|
+
STDERR.print(" http://wiki.github.com/mark-moseley/ruby-debug\n\n")
|
37
|
+
STDERR.print("*************************************************************\n\n")
|
38
|
+
exit(1)
|
39
|
+
end
|
@@ -0,0 +1,2541 @@
|
|
1
|
+
#include <ruby.h>
|
2
|
+
#include <stdio.h>
|
3
|
+
#include <ctype.h>
|
4
|
+
#include <vm_core.h>
|
5
|
+
#include <iseq.h>
|
6
|
+
#include <version.h>
|
7
|
+
#include <eval_intern.h>
|
8
|
+
#include <gc.h>
|
9
|
+
#include <insns.inc>
|
10
|
+
#include <insns_info.inc>
|
11
|
+
#include "ruby_debug.h"
|
12
|
+
|
13
|
+
#define DEBUG_VERSION "0.12"
|
14
|
+
|
15
|
+
#define GET_CFP debug_context->cfp[check_frame_number(debug_context, frame)]
|
16
|
+
#define ZFREE(ptr) do { if(ptr != NULL) { free(ptr); ptr = NULL; } } while(0)
|
17
|
+
|
18
|
+
#ifndef min
|
19
|
+
#define min(x,y) ((x) < (y) ? (x) : (y))
|
20
|
+
#endif
|
21
|
+
|
22
|
+
RUBY_EXTERN int rb_vm_get_sourceline(const rb_control_frame_t *cfp); /* from vm.c */
|
23
|
+
|
24
|
+
/* from iseq.c */
|
25
|
+
#ifdef RB_ISEQ_COMPILE_5ARGS
|
26
|
+
RUBY_EXTERN VALUE rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE filepath, VALUE line, VALUE opt);
|
27
|
+
#else
|
28
|
+
RUBY_EXTERN VALUE rb_iseq_compile_with_option(VALUE src, VALUE file, VALUE line, VALUE opt);
|
29
|
+
#endif
|
30
|
+
|
31
|
+
typedef struct {
|
32
|
+
st_table *tbl;
|
33
|
+
} threads_table_t;
|
34
|
+
|
35
|
+
static VALUE hook_off = Qtrue;
|
36
|
+
static VALUE tracing = Qfalse;
|
37
|
+
static VALUE locker = Qnil;
|
38
|
+
static VALUE debug_flag = Qfalse;
|
39
|
+
static VALUE catchall = Qtrue;
|
40
|
+
static VALUE skip_next_exception= Qfalse;
|
41
|
+
|
42
|
+
static VALUE last_context = Qnil;
|
43
|
+
static VALUE last_thread = Qnil;
|
44
|
+
static debug_context_t *last_debug_context = NULL;
|
45
|
+
|
46
|
+
VALUE rdebug_threads_tbl = Qnil; /* Context for each of the threads */
|
47
|
+
VALUE mDebugger; /* Ruby Debugger Module object */
|
48
|
+
|
49
|
+
static VALUE bin_opt_call_c_function;
|
50
|
+
static VALUE bin_getdynamic;
|
51
|
+
static VALUE bin_throw;
|
52
|
+
static VALUE cThreadsTable;
|
53
|
+
static VALUE cContext;
|
54
|
+
static VALUE cDebugThread;
|
55
|
+
|
56
|
+
static VALUE rb_mObjectSpace;
|
57
|
+
|
58
|
+
static ID idAtBreakpoint;
|
59
|
+
static ID idAtCatchpoint;
|
60
|
+
static ID idAtLine;
|
61
|
+
static ID idAtReturn;
|
62
|
+
static ID idAtTracing;
|
63
|
+
static ID idList;
|
64
|
+
static ID id_binding_n;
|
65
|
+
static ID id_frame_binding;
|
66
|
+
|
67
|
+
static int thnum_max = 0;
|
68
|
+
static int bkp_count = 0;
|
69
|
+
static int last_debugged_thnum = -1;
|
70
|
+
static unsigned long last_check = 0;
|
71
|
+
static unsigned long hook_count = 0;
|
72
|
+
|
73
|
+
static VALUE debug_stop(VALUE);
|
74
|
+
static void save_current_position(debug_context_t *);
|
75
|
+
static void context_suspend_0(debug_context_t *);
|
76
|
+
static void context_resume_0(debug_context_t *);
|
77
|
+
static void thread_context_lookup(VALUE, VALUE *, debug_context_t **, int);
|
78
|
+
static VALUE debug_current_context(VALUE self);
|
79
|
+
static int find_prev_line_start(rb_control_frame_t *cfp);
|
80
|
+
|
81
|
+
|
82
|
+
typedef struct locked_thread_t {
|
83
|
+
VALUE thread_id;
|
84
|
+
struct locked_thread_t *next;
|
85
|
+
} locked_thread_t;
|
86
|
+
|
87
|
+
static locked_thread_t *locked_head = NULL;
|
88
|
+
static locked_thread_t *locked_tail = NULL;
|
89
|
+
|
90
|
+
/* "Step", "Next" and "Finish" do their work by saving information
|
91
|
+
about where to stop next. reset_stopping_points removes/resets this
|
92
|
+
information. */
|
93
|
+
inline static const char *
|
94
|
+
get_event_name(rb_event_flag_t event)
|
95
|
+
{
|
96
|
+
switch (event) {
|
97
|
+
case RUBY_EVENT_LINE:
|
98
|
+
return "line";
|
99
|
+
case RUBY_EVENT_CLASS:
|
100
|
+
return "class";
|
101
|
+
case RUBY_EVENT_END:
|
102
|
+
return "end";
|
103
|
+
case RUBY_EVENT_CALL:
|
104
|
+
return "call";
|
105
|
+
case RUBY_EVENT_RETURN:
|
106
|
+
return "return";
|
107
|
+
case RUBY_EVENT_C_CALL:
|
108
|
+
return "c-call";
|
109
|
+
case RUBY_EVENT_C_RETURN:
|
110
|
+
return "c-return";
|
111
|
+
case RUBY_EVENT_RAISE:
|
112
|
+
return "raise";
|
113
|
+
default:
|
114
|
+
return "unknown";
|
115
|
+
}
|
116
|
+
}
|
117
|
+
|
118
|
+
static void
|
119
|
+
debug_runtime_error(debug_context_t *debug_context, const char *err)
|
120
|
+
{
|
121
|
+
if (debug_context != NULL) CTX_FL_SET(debug_context, CTX_FL_IGNORE);
|
122
|
+
rb_raise(rb_eRuntimeError, err);
|
123
|
+
}
|
124
|
+
|
125
|
+
inline static void
|
126
|
+
reset_stepping_stop_points(debug_context_t *debug_context)
|
127
|
+
{
|
128
|
+
debug_context->dest_frame = -1;
|
129
|
+
debug_context->stop_line = -1;
|
130
|
+
debug_context->stop_next = -1;
|
131
|
+
}
|
132
|
+
|
133
|
+
inline static VALUE
|
134
|
+
real_class(VALUE klass)
|
135
|
+
{
|
136
|
+
if (klass) {
|
137
|
+
if (TYPE(klass) == T_ICLASS) {
|
138
|
+
return RBASIC(klass)->klass;
|
139
|
+
}
|
140
|
+
else if (FL_TEST(klass, FL_SINGLETON)) {
|
141
|
+
return rb_iv_get(klass, "__attached__");
|
142
|
+
}
|
143
|
+
}
|
144
|
+
return klass;
|
145
|
+
}
|
146
|
+
|
147
|
+
inline static VALUE
|
148
|
+
ref2id(VALUE obj)
|
149
|
+
{
|
150
|
+
return obj;
|
151
|
+
}
|
152
|
+
|
153
|
+
inline static VALUE
|
154
|
+
id2ref(VALUE id)
|
155
|
+
{
|
156
|
+
return id;
|
157
|
+
}
|
158
|
+
|
159
|
+
inline static VALUE
|
160
|
+
context_thread_0(debug_context_t *debug_context)
|
161
|
+
{
|
162
|
+
return id2ref(debug_context->thread_id);
|
163
|
+
}
|
164
|
+
|
165
|
+
static inline const rb_data_type_t *
|
166
|
+
threadptr_data_type(void)
|
167
|
+
{
|
168
|
+
static const rb_data_type_t *thread_data_type;
|
169
|
+
if (!thread_data_type) {
|
170
|
+
VALUE current_thread = rb_thread_current();
|
171
|
+
thread_data_type = RTYPEDDATA_TYPE(current_thread);
|
172
|
+
}
|
173
|
+
return thread_data_type;
|
174
|
+
}
|
175
|
+
|
176
|
+
#define ruby_threadptr_data_type *threadptr_data_type()
|
177
|
+
|
178
|
+
#define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
|
179
|
+
|
180
|
+
static int
|
181
|
+
is_in_locked(VALUE thread_id)
|
182
|
+
{
|
183
|
+
locked_thread_t *node;
|
184
|
+
|
185
|
+
if(!locked_head)
|
186
|
+
return 0;
|
187
|
+
|
188
|
+
for(node = locked_head; node != locked_tail; node = node->next)
|
189
|
+
{
|
190
|
+
if(node->thread_id == thread_id) return 1;
|
191
|
+
}
|
192
|
+
return 0;
|
193
|
+
}
|
194
|
+
|
195
|
+
static void
|
196
|
+
add_to_locked(VALUE thread)
|
197
|
+
{
|
198
|
+
locked_thread_t *node;
|
199
|
+
VALUE thread_id = ref2id(thread);
|
200
|
+
|
201
|
+
if(is_in_locked(thread_id))
|
202
|
+
return;
|
203
|
+
|
204
|
+
node = ALLOC(locked_thread_t);
|
205
|
+
node->thread_id = thread_id;
|
206
|
+
node->next = NULL;
|
207
|
+
if(locked_tail)
|
208
|
+
locked_tail->next = node;
|
209
|
+
locked_tail = node;
|
210
|
+
if(!locked_head)
|
211
|
+
locked_head = node;
|
212
|
+
}
|
213
|
+
|
214
|
+
static VALUE
|
215
|
+
remove_from_locked(void)
|
216
|
+
{
|
217
|
+
VALUE thread;
|
218
|
+
locked_thread_t *node;
|
219
|
+
|
220
|
+
if(locked_head == NULL)
|
221
|
+
return Qnil;
|
222
|
+
node = locked_head;
|
223
|
+
locked_head = locked_head->next;
|
224
|
+
if(locked_tail == node)
|
225
|
+
locked_tail = NULL;
|
226
|
+
thread = id2ref(node->thread_id);
|
227
|
+
xfree(node);
|
228
|
+
return thread;
|
229
|
+
}
|
230
|
+
|
231
|
+
static int is_living_thread(VALUE thread);
|
232
|
+
|
233
|
+
static int
|
234
|
+
threads_table_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl)
|
235
|
+
{
|
236
|
+
VALUE thread = id2ref((VALUE)key);
|
237
|
+
if (!value) {
|
238
|
+
return ST_CONTINUE;
|
239
|
+
}
|
240
|
+
rb_gc_mark((VALUE)value);
|
241
|
+
if (is_living_thread(thread)) {
|
242
|
+
rb_gc_mark(thread);
|
243
|
+
}
|
244
|
+
else {
|
245
|
+
st_insert((st_table *)tbl, key, 0);
|
246
|
+
}
|
247
|
+
return ST_CONTINUE;
|
248
|
+
}
|
249
|
+
|
250
|
+
static void
|
251
|
+
threads_table_mark(void* data)
|
252
|
+
{
|
253
|
+
threads_table_t *threads_table = (threads_table_t*)data;
|
254
|
+
st_table *tbl = threads_table->tbl;
|
255
|
+
st_foreach(tbl, threads_table_mark_keyvalue, (st_data_t)tbl);
|
256
|
+
}
|
257
|
+
|
258
|
+
static void
|
259
|
+
threads_table_free(void* data)
|
260
|
+
{
|
261
|
+
threads_table_t *threads_table = (threads_table_t*)data;
|
262
|
+
st_free_table(threads_table->tbl);
|
263
|
+
xfree(threads_table);
|
264
|
+
}
|
265
|
+
|
266
|
+
static VALUE
|
267
|
+
threads_table_create(void)
|
268
|
+
{
|
269
|
+
threads_table_t *threads_table;
|
270
|
+
|
271
|
+
threads_table = ALLOC(threads_table_t);
|
272
|
+
threads_table->tbl = st_init_numtable();
|
273
|
+
return Data_Wrap_Struct(cThreadsTable, threads_table_mark, threads_table_free, threads_table);
|
274
|
+
}
|
275
|
+
|
276
|
+
static void
|
277
|
+
threads_table_clear(VALUE table)
|
278
|
+
{
|
279
|
+
threads_table_t *threads_table;
|
280
|
+
|
281
|
+
Data_Get_Struct(table, threads_table_t, threads_table);
|
282
|
+
st_clear(threads_table->tbl);
|
283
|
+
}
|
284
|
+
|
285
|
+
static int
|
286
|
+
is_thread_alive(VALUE thread)
|
287
|
+
{
|
288
|
+
rb_thread_t *th;
|
289
|
+
GetThreadPtr(thread, th);
|
290
|
+
return th->status != THREAD_KILLED;
|
291
|
+
}
|
292
|
+
|
293
|
+
static int
|
294
|
+
is_living_thread(VALUE thread)
|
295
|
+
{
|
296
|
+
return rb_obj_is_kind_of(thread, rb_cThread) && is_thread_alive(thread);
|
297
|
+
}
|
298
|
+
|
299
|
+
static int
|
300
|
+
threads_table_check_i(st_data_t key, st_data_t value, st_data_t dummy)
|
301
|
+
{
|
302
|
+
VALUE thread;
|
303
|
+
|
304
|
+
if(!value)
|
305
|
+
{
|
306
|
+
return ST_DELETE;
|
307
|
+
}
|
308
|
+
thread = id2ref((VALUE)key);
|
309
|
+
if(!is_living_thread(thread))
|
310
|
+
{
|
311
|
+
return ST_DELETE;
|
312
|
+
}
|
313
|
+
return ST_CONTINUE;
|
314
|
+
}
|
315
|
+
|
316
|
+
static void
|
317
|
+
check_thread_contexts(void)
|
318
|
+
{
|
319
|
+
threads_table_t *threads_table;
|
320
|
+
|
321
|
+
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
|
322
|
+
st_foreach(threads_table->tbl, threads_table_check_i, 0);
|
323
|
+
}
|
324
|
+
|
325
|
+
static struct iseq_catch_table_entry *
|
326
|
+
create_catch_table(debug_context_t *debug_context, unsigned long cont)
|
327
|
+
{
|
328
|
+
struct iseq_catch_table_entry *catch_table = &debug_context->catch_table.tmp_catch_table;
|
329
|
+
|
330
|
+
GET_THREAD()->parse_in_eval++;
|
331
|
+
GET_THREAD()->mild_compile_error++;
|
332
|
+
/* compiling with option Qfalse (no options) prevents debug hook calls during this catch routine
|
333
|
+
*/
|
334
|
+
#ifdef RB_ISEQ_COMPILE_5ARGS
|
335
|
+
catch_table->iseq = rb_iseq_compile_with_option(
|
336
|
+
rb_str_new_cstr(""), rb_str_new_cstr("(exception catcher)"), Qnil, INT2FIX(1), Qfalse);
|
337
|
+
#else
|
338
|
+
catch_table->iseq = rb_iseq_compile_with_option(
|
339
|
+
rb_str_new_cstr(""), rb_str_new_cstr("(exception catcher)"), INT2FIX(1), Qfalse);
|
340
|
+
#endif
|
341
|
+
GET_THREAD()->mild_compile_error--;
|
342
|
+
GET_THREAD()->parse_in_eval--;
|
343
|
+
|
344
|
+
catch_table->type = CATCH_TYPE_RESCUE;
|
345
|
+
catch_table->start = 0;
|
346
|
+
catch_table->end = ULONG_MAX;
|
347
|
+
catch_table->cont = cont;
|
348
|
+
catch_table->sp = 0;
|
349
|
+
|
350
|
+
return(catch_table);
|
351
|
+
}
|
352
|
+
|
353
|
+
static rb_control_frame_t *
|
354
|
+
FUNC_FASTCALL(do_jump)(rb_thread_t *th, rb_control_frame_t *cfp)
|
355
|
+
{
|
356
|
+
VALUE context;
|
357
|
+
debug_context_t *debug_context;
|
358
|
+
rb_control_frame_t *jump_cfp;
|
359
|
+
VALUE *jump_pc;
|
360
|
+
|
361
|
+
thread_context_lookup(th->self, &context, &debug_context, 0);
|
362
|
+
if (debug_context == NULL)
|
363
|
+
debug_runtime_error(NULL, "Lost context in jump");
|
364
|
+
cfp->pc[-2] = debug_context->saved_jump_ins[0];
|
365
|
+
cfp->pc[-1] = debug_context->saved_jump_ins[1];
|
366
|
+
|
367
|
+
if ((debug_context->jump_pc < debug_context->jump_cfp->iseq->iseq_encoded) ||
|
368
|
+
(debug_context->jump_pc >= debug_context->jump_cfp->iseq->iseq_encoded + debug_context->jump_cfp->iseq->iseq_size))
|
369
|
+
debug_runtime_error(debug_context, "Invalid jump PC target");
|
370
|
+
|
371
|
+
jump_cfp = debug_context->jump_cfp;
|
372
|
+
jump_pc = debug_context->jump_pc;
|
373
|
+
debug_context->jump_pc = NULL;
|
374
|
+
debug_context->jump_cfp = NULL;
|
375
|
+
if (!CTX_FL_TEST(debug_context, CTX_FL_EXCEPTION_TEST))
|
376
|
+
{
|
377
|
+
debug_context->last_line = 0;
|
378
|
+
debug_context->last_file = NULL;
|
379
|
+
debug_context->stop_next = 1;
|
380
|
+
}
|
381
|
+
|
382
|
+
if (cfp < jump_cfp)
|
383
|
+
{
|
384
|
+
/* save all intermediate-frame catch tables
|
385
|
+
+1 for target frame
|
386
|
+
+1 for array terminator
|
387
|
+
*/
|
388
|
+
int frames = jump_cfp - cfp + 2;
|
389
|
+
debug_context->old_iseq_catch = (iseq_catch_t*)malloc(frames * sizeof(iseq_catch_t));
|
390
|
+
MEMZERO(debug_context->old_iseq_catch, iseq_catch_t, frames);
|
391
|
+
frames = 0;
|
392
|
+
do
|
393
|
+
{
|
394
|
+
if (cfp->iseq != NULL)
|
395
|
+
{
|
396
|
+
debug_context->old_iseq_catch[frames].iseq = cfp->iseq;
|
397
|
+
debug_context->old_iseq_catch[frames].catch_table = cfp->iseq->catch_table;
|
398
|
+
debug_context->old_iseq_catch[frames].catch_table_size = cfp->iseq->catch_table_size;
|
399
|
+
cfp->iseq->catch_table = NULL;
|
400
|
+
cfp->iseq->catch_table_size = 0;
|
401
|
+
|
402
|
+
frames++;
|
403
|
+
}
|
404
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
405
|
+
} while (cfp <= jump_cfp);
|
406
|
+
|
407
|
+
jump_cfp->iseq->catch_table_size = 1;
|
408
|
+
jump_cfp->iseq->catch_table =
|
409
|
+
create_catch_table(debug_context, jump_pc - jump_cfp->iseq->iseq_encoded);
|
410
|
+
jump_cfp->iseq->catch_table->sp = -1;
|
411
|
+
|
412
|
+
JUMP_TAG(TAG_RAISE);
|
413
|
+
}
|
414
|
+
else if (cfp > jump_cfp)
|
415
|
+
debug_runtime_error(debug_context, "Invalid jump frame target");
|
416
|
+
|
417
|
+
cfp->pc = jump_pc;
|
418
|
+
return(cfp);
|
419
|
+
}
|
420
|
+
|
421
|
+
static rb_control_frame_t *
|
422
|
+
FUNC_FASTCALL(do_catchall)(rb_thread_t *th, rb_control_frame_t *cfp)
|
423
|
+
{
|
424
|
+
int size;
|
425
|
+
VALUE context;
|
426
|
+
debug_context_t *debug_context;
|
427
|
+
|
428
|
+
thread_context_lookup(th->self, &context, &debug_context, 0);
|
429
|
+
if (debug_context == NULL)
|
430
|
+
debug_runtime_error(NULL, "Lost context in catchall");
|
431
|
+
if (debug_context->saved_frames == NULL)
|
432
|
+
return(cfp); /* re-throw exception */
|
433
|
+
|
434
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_RETHROW))
|
435
|
+
{
|
436
|
+
cfp->pc[-2] = debug_context->saved_jump_ins[0];
|
437
|
+
cfp->pc[-1] = debug_context->saved_jump_ins[1];
|
438
|
+
}
|
439
|
+
else
|
440
|
+
{
|
441
|
+
CTX_FL_SET(debug_context, CTX_FL_CATCHING);
|
442
|
+
CTX_FL_UNSET(debug_context, CTX_FL_EXCEPTION_TEST);
|
443
|
+
}
|
444
|
+
|
445
|
+
/* restore the call frame state */
|
446
|
+
ZFREE(debug_context->cfp);
|
447
|
+
debug_context->cfp_count = debug_context->saved_cfp_count;
|
448
|
+
debug_context->saved_cfp_count = 0;
|
449
|
+
size = sizeof(rb_control_frame_t*) * debug_context->cfp_count;
|
450
|
+
debug_context->cfp = (rb_control_frame_t**)malloc(size);
|
451
|
+
memcpy(debug_context->cfp, debug_context->saved_cfp, size);
|
452
|
+
ZFREE(debug_context->saved_cfp);
|
453
|
+
|
454
|
+
size = sizeof(rb_control_frame_t) *
|
455
|
+
((debug_context->cfp[debug_context->cfp_count-1] - debug_context->cfp[0]) + 1);
|
456
|
+
memcpy(debug_context->cfp[0], debug_context->saved_frames, size);
|
457
|
+
|
458
|
+
ZFREE(debug_context->saved_frames);
|
459
|
+
th->cfp = debug_context->cfp[0];
|
460
|
+
th->cfp->pc = th->cfp->iseq->iseq_encoded + find_prev_line_start(th->cfp);
|
461
|
+
return(th->cfp);
|
462
|
+
}
|
463
|
+
|
464
|
+
static void
|
465
|
+
create_exception_catchall(debug_context_t *debug_context)
|
466
|
+
{
|
467
|
+
rb_iseq_t *iseq;
|
468
|
+
struct iseq_catch_table_entry *entry;
|
469
|
+
rb_control_frame_t *cfp = GET_THREAD()->cfp;
|
470
|
+
while (cfp->iseq == NULL)
|
471
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
472
|
+
|
473
|
+
debug_context->start_cfp = cfp;
|
474
|
+
if (catchall == Qfalse)
|
475
|
+
return;
|
476
|
+
|
477
|
+
iseq = cfp->iseq;
|
478
|
+
iseq->catch_table_size++;
|
479
|
+
entry = ALLOC_N(struct iseq_catch_table_entry, iseq->catch_table_size);
|
480
|
+
|
481
|
+
if (iseq->catch_table_size > 1)
|
482
|
+
{
|
483
|
+
MEMCPY(entry, iseq->catch_table, struct iseq_catch_table_entry, iseq->catch_table_size-1);
|
484
|
+
xfree(iseq->catch_table);
|
485
|
+
iseq->catch_table = entry;
|
486
|
+
entry += iseq->catch_table_size-1;
|
487
|
+
}
|
488
|
+
else
|
489
|
+
iseq->catch_table = entry;
|
490
|
+
|
491
|
+
memset(&debug_context->catch_iseq, 0, sizeof(struct rb_iseq_struct));
|
492
|
+
memset(&debug_context->catch_cref_stack, 0, sizeof(struct RNode));
|
493
|
+
debug_context->catch_rdata.basic.flags = RUBY_T_DATA;
|
494
|
+
debug_context->catch_rdata.basic.klass = 0;
|
495
|
+
debug_context->catch_rdata.dmark = NULL;
|
496
|
+
debug_context->catch_rdata.dfree = NULL;
|
497
|
+
debug_context->catch_rdata.data = &debug_context->catch_iseq;
|
498
|
+
debug_context->catch_iseq.type = ISEQ_TYPE_RESCUE;
|
499
|
+
debug_context->catch_iseq.name = rb_str_new_cstr("(exception catcher)");
|
500
|
+
debug_context->catch_iseq.filename = rb_str_new_cstr("(exception catcher)");
|
501
|
+
debug_context->catch_iseq.iseq = debug_context->iseq_insn;
|
502
|
+
debug_context->catch_iseq.iseq_encoded = NULL;
|
503
|
+
debug_context->catch_iseq.iseq[0] = BIN(opt_call_c_function);
|
504
|
+
debug_context->catch_iseq.iseq[1] = (VALUE)do_catchall;
|
505
|
+
debug_context->catch_iseq.iseq[2] = BIN(getdynamic);
|
506
|
+
debug_context->catch_iseq.iseq[3] = 1; /* #$!, only value in local_table */
|
507
|
+
debug_context->catch_iseq.iseq[4] = 0;
|
508
|
+
debug_context->catch_iseq.iseq[5] = BIN(throw);
|
509
|
+
debug_context->catch_iseq.iseq[6] = 0;
|
510
|
+
debug_context->catch_iseq.iseq_size = sizeof(debug_context->iseq_insn) / sizeof(VALUE);
|
511
|
+
debug_context->catch_iseq.mark_ary = rb_ary_new();
|
512
|
+
debug_context->catch_iseq.line_info_table = &debug_context->catch_info_entry;
|
513
|
+
debug_context->catch_iseq.line_info_size = 1;
|
514
|
+
debug_context->catch_iseq.local_size = 1;
|
515
|
+
debug_context->catch_iseq.local_table = &debug_context->local_table;
|
516
|
+
debug_context->catch_iseq.local_table[0] = rb_intern("#$!");
|
517
|
+
debug_context->catch_iseq.arg_simple = 1;
|
518
|
+
debug_context->catch_iseq.arg_rest = -1;
|
519
|
+
debug_context->catch_iseq.arg_block = -1;
|
520
|
+
debug_context->catch_iseq.stack_max = 1;
|
521
|
+
debug_context->catch_iseq.local_iseq = &debug_context->catch_iseq;
|
522
|
+
debug_context->catch_iseq.self = (VALUE)&debug_context->catch_rdata;
|
523
|
+
debug_context->catch_iseq.cref_stack = &debug_context->catch_cref_stack;
|
524
|
+
debug_context->catch_info_entry.position = 0;
|
525
|
+
debug_context->catch_info_entry.line_no = 1;
|
526
|
+
#if defined HAVE_TYPE_STRUCT_ISEQ_INSN_INFO_ENTRY
|
527
|
+
debug_context->catch_info_entry.sp = 0;
|
528
|
+
#endif
|
529
|
+
debug_context->catch_cref_stack.flags = 1052;
|
530
|
+
|
531
|
+
entry->type = CATCH_TYPE_RESCUE;
|
532
|
+
entry->iseq = debug_context->catch_iseq.self;
|
533
|
+
entry->start = 0;
|
534
|
+
entry->end = ULONG_MAX;
|
535
|
+
entry->cont = 0;
|
536
|
+
entry->sp = 0;
|
537
|
+
|
538
|
+
rb_iseq_translate_threaded_code(&debug_context->catch_iseq);
|
539
|
+
}
|
540
|
+
|
541
|
+
/*
|
542
|
+
* call-seq:
|
543
|
+
* Debugger.started? -> bool
|
544
|
+
*
|
545
|
+
* Returns +true+ the debugger is started.
|
546
|
+
* Deprecated.
|
547
|
+
*/
|
548
|
+
static VALUE
|
549
|
+
debug_is_started(VALUE self)
|
550
|
+
{
|
551
|
+
return Qtrue;
|
552
|
+
}
|
553
|
+
|
554
|
+
static void
|
555
|
+
debug_context_mark(void *data)
|
556
|
+
{
|
557
|
+
debug_context_t *debug_context = (debug_context_t *)data;
|
558
|
+
rb_gc_mark(debug_context->breakpoint);
|
559
|
+
}
|
560
|
+
|
561
|
+
static void
|
562
|
+
debug_context_free(void *data)
|
563
|
+
{
|
564
|
+
debug_context_t *debug_context = (debug_context_t *)data;
|
565
|
+
}
|
566
|
+
|
567
|
+
static VALUE
|
568
|
+
debug_context_create(VALUE thread)
|
569
|
+
{
|
570
|
+
debug_context_t *debug_context;
|
571
|
+
|
572
|
+
debug_context = ALLOC(debug_context_t);
|
573
|
+
debug_context-> thnum = ++thnum_max;
|
574
|
+
|
575
|
+
debug_context->last_file = NULL;
|
576
|
+
debug_context->last_line = 0;
|
577
|
+
debug_context->flags = 0;
|
578
|
+
|
579
|
+
debug_context->stop_next = -1;
|
580
|
+
debug_context->dest_frame = -1;
|
581
|
+
debug_context->stop_line = -1;
|
582
|
+
debug_context->stop_frame = -1;
|
583
|
+
debug_context->stop_reason = CTX_STOP_NONE;
|
584
|
+
debug_context->thread_id = ref2id(thread);
|
585
|
+
debug_context->breakpoint = Qnil;
|
586
|
+
debug_context->jump_pc = NULL;
|
587
|
+
debug_context->jump_cfp = NULL;
|
588
|
+
debug_context->old_iseq_catch = NULL;
|
589
|
+
debug_context->last_exception = Qnil;
|
590
|
+
debug_context->thread_pause = 0;
|
591
|
+
debug_context->cfp_count = 0;
|
592
|
+
debug_context->start_cfp = NULL;
|
593
|
+
debug_context->cur_cfp = NULL;
|
594
|
+
debug_context->top_cfp = NULL;
|
595
|
+
debug_context->catch_cfp = NULL;
|
596
|
+
debug_context->saved_frames = NULL;
|
597
|
+
debug_context->saved_cfp = NULL;
|
598
|
+
debug_context->saved_cfp_count = 0;
|
599
|
+
debug_context->cfp = NULL;
|
600
|
+
if(rb_obj_class(thread) == cDebugThread)
|
601
|
+
CTX_FL_SET(debug_context, CTX_FL_IGNORE);
|
602
|
+
return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, debug_context);
|
603
|
+
}
|
604
|
+
|
605
|
+
static void
|
606
|
+
thread_context_lookup(VALUE thread, VALUE *context, debug_context_t **debug_context, int create)
|
607
|
+
{
|
608
|
+
threads_table_t *threads_table;
|
609
|
+
VALUE thread_id;
|
610
|
+
debug_context_t *l_debug_context;
|
611
|
+
|
612
|
+
if(last_thread == thread && last_context != Qnil)
|
613
|
+
{
|
614
|
+
*context = last_context;
|
615
|
+
if(debug_context)
|
616
|
+
*debug_context = last_debug_context;
|
617
|
+
return;
|
618
|
+
}
|
619
|
+
thread_id = ref2id(thread);
|
620
|
+
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
|
621
|
+
if(!st_lookup(threads_table->tbl, thread_id, context) || !*context)
|
622
|
+
{
|
623
|
+
if (create)
|
624
|
+
{
|
625
|
+
*context = debug_context_create(thread);
|
626
|
+
st_insert(threads_table->tbl, thread_id, *context);
|
627
|
+
}
|
628
|
+
else
|
629
|
+
{
|
630
|
+
*context = 0;
|
631
|
+
if (debug_context)
|
632
|
+
*debug_context = NULL;
|
633
|
+
return;
|
634
|
+
}
|
635
|
+
}
|
636
|
+
|
637
|
+
Data_Get_Struct(*context, debug_context_t, l_debug_context);
|
638
|
+
if (debug_context)
|
639
|
+
*debug_context = l_debug_context;
|
640
|
+
|
641
|
+
last_thread = thread;
|
642
|
+
last_context = *context;
|
643
|
+
last_debug_context = l_debug_context;
|
644
|
+
}
|
645
|
+
|
646
|
+
static VALUE
|
647
|
+
call_at_line_unprotected(VALUE args)
|
648
|
+
{
|
649
|
+
VALUE context;
|
650
|
+
context = *RARRAY_PTR(args);
|
651
|
+
return rb_funcall2(context, idAtLine, RARRAY_LEN(args) - 1, RARRAY_PTR(args) + 1);
|
652
|
+
}
|
653
|
+
|
654
|
+
static VALUE
|
655
|
+
call_at_line(VALUE context, debug_context_t *debug_context, VALUE file, VALUE line)
|
656
|
+
{
|
657
|
+
VALUE args;
|
658
|
+
|
659
|
+
last_debugged_thnum = debug_context->thnum;
|
660
|
+
save_current_position(debug_context);
|
661
|
+
|
662
|
+
args = rb_ary_new3(3, context, file, line);
|
663
|
+
return(rb_protect(call_at_line_unprotected, args, 0));
|
664
|
+
}
|
665
|
+
|
666
|
+
#if defined DOSISH
|
667
|
+
#define isdirsep(x) ((x) == '/' || (x) == '\\')
|
668
|
+
#else
|
669
|
+
#define isdirsep(x) ((x) == '/')
|
670
|
+
#endif
|
671
|
+
|
672
|
+
int
|
673
|
+
filename_cmp(VALUE source, const char *file)
|
674
|
+
{
|
675
|
+
const char *source_ptr, *file_ptr;
|
676
|
+
int s_len, f_len, min_len;
|
677
|
+
int s,f;
|
678
|
+
int dirsep_flag = 0;
|
679
|
+
|
680
|
+
s_len = RSTRING_LEN(source);
|
681
|
+
f_len = strlen(file);
|
682
|
+
min_len = min(s_len, f_len);
|
683
|
+
|
684
|
+
source_ptr = RSTRING_PTR(source);
|
685
|
+
file_ptr = file;
|
686
|
+
|
687
|
+
for( s = s_len - 1, f = f_len - 1; s >= s_len - min_len && f >= f_len - min_len; s--, f-- )
|
688
|
+
{
|
689
|
+
if((source_ptr[s] == '.' || file_ptr[f] == '.') && dirsep_flag)
|
690
|
+
return 1;
|
691
|
+
if(isdirsep(source_ptr[s]) && isdirsep(file_ptr[f]))
|
692
|
+
dirsep_flag = 1;
|
693
|
+
#ifdef DOSISH_DRIVE_LETTER
|
694
|
+
else if (s == 0)
|
695
|
+
return(toupper(source_ptr[s]) == toupper(file_ptr[f]));
|
696
|
+
#endif
|
697
|
+
else if(source_ptr[s] != file_ptr[f])
|
698
|
+
return 0;
|
699
|
+
}
|
700
|
+
return 1;
|
701
|
+
}
|
702
|
+
|
703
|
+
static VALUE
|
704
|
+
binding_alloc(VALUE klass)
|
705
|
+
{
|
706
|
+
VALUE obj;
|
707
|
+
rb_binding_t *bind;
|
708
|
+
static const rb_data_type_t *binding_data_type;
|
709
|
+
if (!binding_data_type) {
|
710
|
+
VALUE b = rb_binding_new();
|
711
|
+
binding_data_type = RTYPEDDATA_TYPE(b);
|
712
|
+
}
|
713
|
+
obj = TypedData_Make_Struct(klass, rb_binding_t, binding_data_type, bind);
|
714
|
+
return obj;
|
715
|
+
}
|
716
|
+
|
717
|
+
static void
|
718
|
+
save_current_position(debug_context_t *debug_context)
|
719
|
+
{
|
720
|
+
CTX_FL_UNSET(debug_context, CTX_FL_ENABLE_BKPT);
|
721
|
+
CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
|
722
|
+
CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
|
723
|
+
}
|
724
|
+
|
725
|
+
inline static int
|
726
|
+
c_call_new_frame_p(VALUE klass, ID mid)
|
727
|
+
{
|
728
|
+
klass = real_class(klass);
|
729
|
+
if ((klass == rb_mKernel) && (strcmp(rb_id2name(mid), "raise") == 0)) return 0;
|
730
|
+
if(rb_block_given_p()) return 1;
|
731
|
+
if(klass == rb_cProc || klass == rb_mKernel || klass == rb_cModule) return 1;
|
732
|
+
return 0;
|
733
|
+
}
|
734
|
+
|
735
|
+
static void
|
736
|
+
call_at_line_check(VALUE self, debug_context_t *debug_context, VALUE breakpoint, VALUE context, const char *file, int line)
|
737
|
+
{
|
738
|
+
debug_context->stop_reason = CTX_STOP_STEP;
|
739
|
+
|
740
|
+
/* check breakpoint expression */
|
741
|
+
if(breakpoint != Qnil)
|
742
|
+
{
|
743
|
+
if(!check_breakpoint_expression(breakpoint, rb_binding_new()))
|
744
|
+
return;
|
745
|
+
if(!check_breakpoint_hit_condition(breakpoint))
|
746
|
+
return;
|
747
|
+
if(breakpoint != debug_context->breakpoint)
|
748
|
+
{
|
749
|
+
debug_context->stop_reason = CTX_STOP_BREAKPOINT;
|
750
|
+
rb_funcall(context, idAtBreakpoint, 1, breakpoint);
|
751
|
+
}
|
752
|
+
else
|
753
|
+
debug_context->breakpoint = Qnil;
|
754
|
+
}
|
755
|
+
|
756
|
+
reset_stepping_stop_points(debug_context);
|
757
|
+
call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
|
758
|
+
}
|
759
|
+
|
760
|
+
static int
|
761
|
+
set_thread_event_flag_i(st_data_t key, st_data_t val, st_data_t flag)
|
762
|
+
{
|
763
|
+
VALUE thval = (VALUE)key;
|
764
|
+
rb_thread_t *th;
|
765
|
+
GetThreadPtr(thval, th);
|
766
|
+
th->event_flags |= RUBY_EVENT_VM;
|
767
|
+
|
768
|
+
return(ST_CONTINUE);
|
769
|
+
}
|
770
|
+
|
771
|
+
static int
|
772
|
+
find_prev_line_start(rb_control_frame_t *cfp)
|
773
|
+
{
|
774
|
+
#if defined HAVE_TYPE_STRUCT_ISEQ_LINE_INFO_ENTRY
|
775
|
+
const rb_iseq_t *iseq = cfp->iseq;
|
776
|
+
return rb_iseq_line_no(iseq, cfp->pc - iseq->iseq_encoded - 1);
|
777
|
+
#else
|
778
|
+
int i, pos, line_no;
|
779
|
+
pos = cfp->pc - cfp->iseq->iseq_encoded;
|
780
|
+
for (i = 0; i < cfp->iseq->insn_info_size; i++)
|
781
|
+
{
|
782
|
+
if (cfp->iseq->insn_info_table[i].position != pos)
|
783
|
+
continue;
|
784
|
+
|
785
|
+
if (i == 0) return(0); // TODO
|
786
|
+
|
787
|
+
pos = -1;
|
788
|
+
line_no = cfp->iseq->insn_info_table[--i].line_no;
|
789
|
+
do {
|
790
|
+
if (cfp->iseq->insn_info_table[i].line_no == line_no)
|
791
|
+
pos = cfp->iseq->insn_info_table[i].position;
|
792
|
+
else
|
793
|
+
break;
|
794
|
+
i--;
|
795
|
+
} while (i >= 0);
|
796
|
+
|
797
|
+
return(pos);
|
798
|
+
}
|
799
|
+
return(-1);
|
800
|
+
#endif
|
801
|
+
}
|
802
|
+
|
803
|
+
static rb_control_frame_t *
|
804
|
+
FUNC_FASTCALL(do_catch)(rb_thread_t *th, rb_control_frame_t *cfp)
|
805
|
+
{
|
806
|
+
VALUE context;
|
807
|
+
debug_context_t *debug_context;
|
808
|
+
|
809
|
+
thread_context_lookup(th->self, &context, &debug_context, 0);
|
810
|
+
if (debug_context == NULL)
|
811
|
+
debug_runtime_error(NULL, "Lost context in catch");
|
812
|
+
cfp->pc[-2] = debug_context->saved_jump_ins[0];
|
813
|
+
cfp->pc[-1] = debug_context->saved_jump_ins[1];
|
814
|
+
|
815
|
+
cfp->pc = debug_context->jump_pc;
|
816
|
+
|
817
|
+
debug_context->jump_pc = NULL;
|
818
|
+
debug_context->jump_cfp = NULL;
|
819
|
+
|
820
|
+
/* restore the proper catch table */
|
821
|
+
cfp->iseq->catch_table_size = debug_context->catch_table.old_catch_table_size;
|
822
|
+
cfp->iseq->catch_table = debug_context->catch_table.old_catch_table;
|
823
|
+
CTX_FL_SET(debug_context, CTX_FL_CATCHING);
|
824
|
+
th->cfp->sp--;
|
825
|
+
|
826
|
+
return(cfp);
|
827
|
+
}
|
828
|
+
|
829
|
+
static int
|
830
|
+
catch_exception(debug_context_t *debug_context, VALUE mod_name)
|
831
|
+
{
|
832
|
+
int prev_line_start;
|
833
|
+
rb_control_frame_t *cfp = GET_THREAD()->cfp;
|
834
|
+
while (cfp->pc == 0 || cfp->iseq == 0)
|
835
|
+
cfp++;
|
836
|
+
|
837
|
+
prev_line_start = find_prev_line_start(cfp);
|
838
|
+
if (prev_line_start < 0)
|
839
|
+
return(0);
|
840
|
+
|
841
|
+
/* save the current catch table */
|
842
|
+
debug_context->catch_table.old_catch_table_size = cfp->iseq->catch_table_size;
|
843
|
+
debug_context->catch_table.old_catch_table = cfp->iseq->catch_table;
|
844
|
+
debug_context->catch_table.mod_name = mod_name;
|
845
|
+
debug_context->catch_table.errinfo = rb_errinfo();
|
846
|
+
|
847
|
+
/* create a new catch table to catch this exception, and put it in the current iseq */
|
848
|
+
cfp->iseq->catch_table_size = 1;
|
849
|
+
cfp->iseq->catch_table = create_catch_table(debug_context, 0);
|
850
|
+
|
851
|
+
/* hijack the currently running code so that we can change the frame PC */
|
852
|
+
debug_context->saved_jump_ins[0] = cfp->iseq->iseq_encoded[0];
|
853
|
+
debug_context->saved_jump_ins[1] = cfp->iseq->iseq_encoded[1];
|
854
|
+
cfp->iseq->iseq_encoded[0] = bin_opt_call_c_function;
|
855
|
+
cfp->iseq->iseq_encoded[1] = (VALUE)do_catch;
|
856
|
+
debug_context->jump_pc = cfp->iseq->iseq_encoded + prev_line_start;
|
857
|
+
debug_context->jump_cfp = NULL;
|
858
|
+
return(1);
|
859
|
+
}
|
860
|
+
|
861
|
+
static void
|
862
|
+
set_cfp(debug_context_t *debug_context)
|
863
|
+
{
|
864
|
+
int cfp_count = 0;
|
865
|
+
int has_changed = 0;
|
866
|
+
rb_control_frame_t *cfp = debug_context->cur_cfp;
|
867
|
+
while (cfp <= debug_context->start_cfp)
|
868
|
+
{
|
869
|
+
if (cfp->iseq != NULL)
|
870
|
+
{
|
871
|
+
if (cfp->pc != NULL)
|
872
|
+
{
|
873
|
+
if ((cfp_count < debug_context->cfp_count) && (debug_context->cfp[cfp_count] != cfp))
|
874
|
+
has_changed = 1;
|
875
|
+
cfp_count++;
|
876
|
+
}
|
877
|
+
}
|
878
|
+
else if (RUBYVM_CFUNC_FRAME_P(cfp))
|
879
|
+
{
|
880
|
+
if ((cfp_count < debug_context->cfp_count) && (debug_context->cfp[cfp_count] != cfp))
|
881
|
+
has_changed = 1;
|
882
|
+
cfp_count++;
|
883
|
+
}
|
884
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
885
|
+
}
|
886
|
+
|
887
|
+
//if (cfp_count == debug_context->cfp_count && !has_changed)
|
888
|
+
// return;
|
889
|
+
|
890
|
+
ZFREE(debug_context->cfp);
|
891
|
+
debug_context->cfp = (rb_control_frame_t**)malloc(sizeof(rb_control_frame_t) * cfp_count);
|
892
|
+
debug_context->cfp_count = 0;
|
893
|
+
|
894
|
+
cfp = debug_context->cur_cfp;
|
895
|
+
while (cfp <= debug_context->start_cfp)
|
896
|
+
{
|
897
|
+
if (cfp->iseq != NULL)
|
898
|
+
if (cfp->pc != NULL) debug_context->cfp[debug_context->cfp_count++] = cfp;
|
899
|
+
else if (RUBYVM_CFUNC_FRAME_P(cfp))
|
900
|
+
debug_context->cfp[debug_context->cfp_count++] = cfp;
|
901
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
902
|
+
}
|
903
|
+
}
|
904
|
+
|
905
|
+
static void
|
906
|
+
save_frames(debug_context_t *debug_context)
|
907
|
+
{
|
908
|
+
int size;
|
909
|
+
VALUE thread;
|
910
|
+
rb_thread_t *th;
|
911
|
+
|
912
|
+
if (debug_context->cfp_count == 0)
|
913
|
+
return;
|
914
|
+
|
915
|
+
/* save the entire call frame state */
|
916
|
+
thread = rb_thread_current();
|
917
|
+
GetThreadPtr(thread, th);
|
918
|
+
for (size = 0; size < debug_context->cfp_count; size++)
|
919
|
+
{
|
920
|
+
rb_binding_t *bind;
|
921
|
+
VALUE bindval = binding_alloc(rb_cBinding);
|
922
|
+
GetBindingPtr(bindval, bind);
|
923
|
+
bind->env = rb_vm_make_env_object(th, debug_context->cfp[size]);
|
924
|
+
}
|
925
|
+
|
926
|
+
debug_context->catch_table.mod_name = rb_obj_class(rb_errinfo());
|
927
|
+
debug_context->catch_table.errinfo = rb_errinfo();
|
928
|
+
|
929
|
+
debug_context->catch_cfp = GET_THREAD()->cfp;
|
930
|
+
ZFREE(debug_context->saved_frames);
|
931
|
+
|
932
|
+
size = sizeof(rb_control_frame_t) *
|
933
|
+
((debug_context->cfp[debug_context->cfp_count-1] - debug_context->cfp[0]) + 1);
|
934
|
+
debug_context->saved_frames = (rb_control_frame_t*)malloc(size);
|
935
|
+
memcpy(debug_context->saved_frames, debug_context->cfp[0], size);
|
936
|
+
|
937
|
+
ZFREE(debug_context->saved_cfp);
|
938
|
+
size = sizeof(rb_control_frame_t*) * debug_context->cfp_count;
|
939
|
+
debug_context->saved_cfp = (rb_control_frame_t**)malloc(size);
|
940
|
+
memcpy(debug_context->saved_cfp, debug_context->cfp, size);
|
941
|
+
debug_context->saved_cfp_count = debug_context->cfp_count;
|
942
|
+
|
943
|
+
CTX_FL_SET(debug_context, CTX_FL_EXCEPTION_TEST);
|
944
|
+
}
|
945
|
+
|
946
|
+
static int
|
947
|
+
try_thread_lock(rb_thread_t *th, debug_context_t *debug_context)
|
948
|
+
{
|
949
|
+
while(1)
|
950
|
+
{
|
951
|
+
/* halt execution of the current thread if the debugger
|
952
|
+
is activated in another
|
953
|
+
*/
|
954
|
+
while(locker != Qnil && locker != th->self)
|
955
|
+
{
|
956
|
+
add_to_locked(th->self);
|
957
|
+
rb_thread_stop();
|
958
|
+
}
|
959
|
+
|
960
|
+
/* stop the current thread if it's marked as suspended */
|
961
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) && locker != th->self)
|
962
|
+
{
|
963
|
+
CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
|
964
|
+
rb_thread_stop();
|
965
|
+
}
|
966
|
+
else break;
|
967
|
+
}
|
968
|
+
|
969
|
+
/* return if the current thread is the locker */
|
970
|
+
if (locker != Qnil) return(0);
|
971
|
+
|
972
|
+
/* only the current thread can proceed */
|
973
|
+
locker = th->self;
|
974
|
+
return(1);
|
975
|
+
}
|
976
|
+
|
977
|
+
static int
|
978
|
+
handle_raise_event(rb_thread_t *th, debug_context_t *debug_context)
|
979
|
+
{
|
980
|
+
VALUE ancestors;
|
981
|
+
VALUE aclass;
|
982
|
+
int i;
|
983
|
+
|
984
|
+
ZFREE(debug_context->saved_frames);
|
985
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_RETHROW))
|
986
|
+
{
|
987
|
+
CTX_FL_UNSET(debug_context, CTX_FL_RETHROW);
|
988
|
+
return(0);
|
989
|
+
}
|
990
|
+
if (debug_context->cfp_count == 0 || !try_thread_lock(th, debug_context))
|
991
|
+
return(0);
|
992
|
+
|
993
|
+
if (skip_next_exception == Qtrue)
|
994
|
+
{
|
995
|
+
skip_next_exception = Qfalse;
|
996
|
+
debug_context->last_exception = rb_errinfo();
|
997
|
+
return(1);
|
998
|
+
}
|
999
|
+
|
1000
|
+
if (rb_errinfo() == debug_context->last_exception)
|
1001
|
+
return(1);
|
1002
|
+
|
1003
|
+
debug_context->last_exception = Qnil;
|
1004
|
+
|
1005
|
+
if (rdebug_catchpoints == Qnil ||
|
1006
|
+
(debug_context->cfp_count == 0) ||
|
1007
|
+
CTX_FL_TEST(debug_context, CTX_FL_CATCHING) ||
|
1008
|
+
#ifdef _ST_NEW_
|
1009
|
+
st_get_num_entries(RHASH_TBL(rdebug_catchpoints)) == 0
|
1010
|
+
#else
|
1011
|
+
(RHASH_TBL(rdebug_catchpoints)->num_entries) == 0
|
1012
|
+
#endif
|
1013
|
+
)
|
1014
|
+
{
|
1015
|
+
if (catchall == Qfalse) return(1);
|
1016
|
+
}
|
1017
|
+
|
1018
|
+
ancestors = rb_mod_ancestors(rb_obj_class(rb_errinfo()));
|
1019
|
+
for(i = 0; i < RARRAY_LEN(ancestors); i++)
|
1020
|
+
{
|
1021
|
+
VALUE mod_name;
|
1022
|
+
VALUE hit_count;
|
1023
|
+
|
1024
|
+
aclass = rb_ary_entry(ancestors, i);
|
1025
|
+
mod_name = rb_mod_name(aclass);
|
1026
|
+
hit_count = rb_hash_aref(rdebug_catchpoints, mod_name);
|
1027
|
+
if (hit_count != Qnil)
|
1028
|
+
{
|
1029
|
+
if (!catch_exception(debug_context, mod_name))
|
1030
|
+
debug_runtime_error(debug_context, "Could not catch exception");
|
1031
|
+
return(1);
|
1032
|
+
}
|
1033
|
+
}
|
1034
|
+
|
1035
|
+
if (catchall == Qtrue)
|
1036
|
+
save_frames(debug_context);
|
1037
|
+
return(1);
|
1038
|
+
}
|
1039
|
+
|
1040
|
+
static void
|
1041
|
+
debug_event_hook(rb_event_flag_t event, VALUE data, VALUE self, ID mid, VALUE klass)
|
1042
|
+
{
|
1043
|
+
VALUE context;
|
1044
|
+
VALUE breakpoint = Qnil;
|
1045
|
+
debug_context_t *debug_context;
|
1046
|
+
int moved = 0;
|
1047
|
+
rb_thread_t *th;
|
1048
|
+
struct rb_iseq_struct *iseq;
|
1049
|
+
const char *file;
|
1050
|
+
int line = 0;
|
1051
|
+
|
1052
|
+
if (hook_off == Qtrue)
|
1053
|
+
return;
|
1054
|
+
|
1055
|
+
th = GET_THREAD();
|
1056
|
+
iseq = th->cfp->iseq;
|
1057
|
+
hook_count++;
|
1058
|
+
thread_context_lookup(th->self, &context, &debug_context, 1);
|
1059
|
+
|
1060
|
+
/* return if thread is marked as 'ignored'.
|
1061
|
+
debugger's threads are marked this way
|
1062
|
+
*/
|
1063
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_IGNORE)) return;
|
1064
|
+
|
1065
|
+
if (debug_context->top_cfp && th->cfp >= debug_context->top_cfp)
|
1066
|
+
return;
|
1067
|
+
debug_context->catch_cfp = NULL;
|
1068
|
+
|
1069
|
+
if (event == RUBY_EVENT_RAISE)
|
1070
|
+
{
|
1071
|
+
if (handle_raise_event(th, debug_context))
|
1072
|
+
goto cleanup;
|
1073
|
+
return;
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
if (iseq == NULL || th->cfp->pc == NULL)
|
1077
|
+
return;
|
1078
|
+
|
1079
|
+
if (event == RUBY_EVENT_LINE && CTX_FL_TEST(debug_context, CTX_FL_EXCEPTION_TEST))
|
1080
|
+
{
|
1081
|
+
if (iseq->type == ISEQ_TYPE_ENSURE)
|
1082
|
+
{
|
1083
|
+
/* don't allow "ensure" blocks to execute yet: jump out of here by re-throwing exception */
|
1084
|
+
VALUE *jump_pc =
|
1085
|
+
iseq->iseq_encoded + iseq->iseq_size - insn_len(BIN(getdynamic)) - insn_len(BIN(throw));
|
1086
|
+
|
1087
|
+
if (jump_pc[0] != bin_getdynamic || jump_pc[insn_len(BIN(getdynamic))] != bin_throw)
|
1088
|
+
debug_runtime_error(debug_context, "Unexpected instructions in ENSURE block");
|
1089
|
+
|
1090
|
+
debug_context->jump_cfp = th->cfp;
|
1091
|
+
debug_context->jump_pc = jump_pc;
|
1092
|
+
debug_context->saved_jump_ins[0] = th->cfp->pc[0];
|
1093
|
+
debug_context->saved_jump_ins[1] = th->cfp->pc[1];
|
1094
|
+
th->cfp->pc[0] = bin_opt_call_c_function;
|
1095
|
+
th->cfp->pc[1] = (VALUE)do_jump;
|
1096
|
+
CTX_FL_SET(debug_context, CTX_FL_ENSURE_SKIPPED);
|
1097
|
+
return;
|
1098
|
+
}
|
1099
|
+
else if (iseq->type == ISEQ_TYPE_RESCUE)
|
1100
|
+
{
|
1101
|
+
CTX_FL_UNSET(debug_context, CTX_FL_EXCEPTION_TEST);
|
1102
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_ENSURE_SKIPPED))
|
1103
|
+
{
|
1104
|
+
/* exception was caught by the code; need to start the whole thing over */
|
1105
|
+
debug_context->saved_jump_ins[0] = th->cfp->pc[0];
|
1106
|
+
debug_context->saved_jump_ins[1] = th->cfp->pc[1];
|
1107
|
+
th->cfp->pc[0] = bin_opt_call_c_function;
|
1108
|
+
th->cfp->pc[1] = (VALUE)do_catchall;
|
1109
|
+
CTX_FL_UNSET(debug_context, CTX_FL_ENSURE_SKIPPED);
|
1110
|
+
CTX_FL_SET(debug_context, CTX_FL_RETHROW);
|
1111
|
+
return;
|
1112
|
+
}
|
1113
|
+
}
|
1114
|
+
}
|
1115
|
+
|
1116
|
+
if (iseq->defined_method_id == id_binding_n &&
|
1117
|
+
(self == rb_mKernel || self == mDebugger || klass == cContext || klass == 0))
|
1118
|
+
return;
|
1119
|
+
if (iseq->defined_method_id == id_frame_binding && (klass == cContext || klass == 0))
|
1120
|
+
return;
|
1121
|
+
|
1122
|
+
if (event == RUBY_EVENT_LINE || event == RUBY_EVENT_CALL)
|
1123
|
+
{
|
1124
|
+
mid = iseq->defined_method_id;
|
1125
|
+
klass = iseq->klass;
|
1126
|
+
}
|
1127
|
+
|
1128
|
+
if (mid == ID_ALLOCATOR) return;
|
1129
|
+
|
1130
|
+
if (!try_thread_lock(th, debug_context))
|
1131
|
+
return;
|
1132
|
+
|
1133
|
+
if (iseq->type != ISEQ_TYPE_RESCUE && iseq->type != ISEQ_TYPE_ENSURE)
|
1134
|
+
{
|
1135
|
+
if (debug_context->start_cfp == NULL || th->cfp > debug_context->start_cfp)
|
1136
|
+
create_exception_catchall(debug_context);
|
1137
|
+
}
|
1138
|
+
|
1139
|
+
/* restore catch tables removed for jump */
|
1140
|
+
if (debug_context->old_iseq_catch != NULL)
|
1141
|
+
{
|
1142
|
+
int i = 0;
|
1143
|
+
while (debug_context->old_iseq_catch[i].iseq != NULL)
|
1144
|
+
{
|
1145
|
+
debug_context->old_iseq_catch[i].iseq->catch_table = debug_context->old_iseq_catch[i].catch_table;
|
1146
|
+
debug_context->old_iseq_catch[i].iseq->catch_table_size = debug_context->old_iseq_catch[i].catch_table_size;
|
1147
|
+
i++;
|
1148
|
+
}
|
1149
|
+
ZFREE(debug_context->old_iseq_catch);
|
1150
|
+
}
|
1151
|
+
|
1152
|
+
/* make sure all threads have event flag set so we'll get its events */
|
1153
|
+
st_foreach(th->vm->living_threads, set_thread_event_flag_i, 0);
|
1154
|
+
|
1155
|
+
if (debug_context->thread_pause)
|
1156
|
+
{
|
1157
|
+
debug_context->thread_pause = 0;
|
1158
|
+
debug_context->stop_next = 1;
|
1159
|
+
debug_context->dest_frame = -1;
|
1160
|
+
moved = 1;
|
1161
|
+
}
|
1162
|
+
|
1163
|
+
/* ignore a skipped section of code */
|
1164
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_SKIPPED))
|
1165
|
+
goto cleanup;
|
1166
|
+
|
1167
|
+
debug_context->cur_cfp = th->cfp;
|
1168
|
+
if (iseq->type != ISEQ_TYPE_RESCUE && iseq->type != ISEQ_TYPE_ENSURE)
|
1169
|
+
set_cfp(debug_context);
|
1170
|
+
|
1171
|
+
/* There can be many event calls per line, but we only want
|
1172
|
+
*one* breakpoint per line. */
|
1173
|
+
line = rb_sourceline();
|
1174
|
+
file = RSTRING_PTR(iseq->filename);
|
1175
|
+
if(debug_context->last_line != line || debug_context->last_file == NULL ||
|
1176
|
+
strcmp(debug_context->last_file, file) != 0)
|
1177
|
+
{
|
1178
|
+
CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
|
1179
|
+
moved = 1;
|
1180
|
+
}
|
1181
|
+
|
1182
|
+
if(event != RUBY_EVENT_LINE)
|
1183
|
+
CTX_FL_SET(debug_context, CTX_FL_STEPPED);
|
1184
|
+
|
1185
|
+
switch(event)
|
1186
|
+
{
|
1187
|
+
case RUBY_EVENT_LINE:
|
1188
|
+
{
|
1189
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_CATCHING))
|
1190
|
+
{
|
1191
|
+
int hit_count;
|
1192
|
+
|
1193
|
+
/* send catchpoint notification */
|
1194
|
+
hit_count = INT2FIX(FIX2INT(rb_hash_aref(rdebug_catchpoints,
|
1195
|
+
debug_context->catch_table.mod_name)+1));
|
1196
|
+
rb_hash_aset(rdebug_catchpoints, debug_context->catch_table.mod_name, hit_count);
|
1197
|
+
debug_context->stop_reason = CTX_STOP_CATCHPOINT;
|
1198
|
+
rb_funcall(context, idAtCatchpoint, 1, debug_context->catch_table.errinfo);
|
1199
|
+
call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
|
1200
|
+
|
1201
|
+
/* now allow the next exception to be caught */
|
1202
|
+
CTX_FL_UNSET(debug_context, CTX_FL_CATCHING);
|
1203
|
+
break;
|
1204
|
+
}
|
1205
|
+
|
1206
|
+
if(RTEST(tracing) || CTX_FL_TEST(debug_context, CTX_FL_TRACING))
|
1207
|
+
rb_funcall(context, idAtTracing, 2, rb_str_new2(file), INT2FIX(line));
|
1208
|
+
|
1209
|
+
if(debug_context->dest_frame == -1 ||
|
1210
|
+
debug_context->cfp_count == debug_context->dest_frame)
|
1211
|
+
{
|
1212
|
+
if(moved || !CTX_FL_TEST(debug_context, CTX_FL_FORCE_MOVE))
|
1213
|
+
debug_context->stop_next--;
|
1214
|
+
if(debug_context->stop_next < 0)
|
1215
|
+
debug_context->stop_next = -1;
|
1216
|
+
if(moved || (CTX_FL_TEST(debug_context, CTX_FL_STEPPED) &&
|
1217
|
+
!CTX_FL_TEST(debug_context, CTX_FL_FORCE_MOVE)))
|
1218
|
+
{
|
1219
|
+
debug_context->stop_line--;
|
1220
|
+
CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
|
1221
|
+
}
|
1222
|
+
}
|
1223
|
+
else if(debug_context->cfp_count < debug_context->dest_frame)
|
1224
|
+
{
|
1225
|
+
debug_context->stop_next = 0;
|
1226
|
+
}
|
1227
|
+
|
1228
|
+
if(debug_context->stop_next == 0 || debug_context->stop_line == 0 ||
|
1229
|
+
(breakpoint = check_breakpoints_by_pos(debug_context, file, line)) != Qnil)
|
1230
|
+
{
|
1231
|
+
call_at_line_check(self, debug_context, breakpoint, context, file, line);
|
1232
|
+
}
|
1233
|
+
break;
|
1234
|
+
}
|
1235
|
+
case RUBY_EVENT_CALL:
|
1236
|
+
{
|
1237
|
+
breakpoint = check_breakpoints_by_method(debug_context, klass, mid, self);
|
1238
|
+
if(breakpoint != Qnil)
|
1239
|
+
{
|
1240
|
+
if(!check_breakpoint_expression(breakpoint, rb_binding_new()))
|
1241
|
+
break;
|
1242
|
+
if(!check_breakpoint_hit_condition(breakpoint))
|
1243
|
+
break;
|
1244
|
+
if(breakpoint != debug_context->breakpoint)
|
1245
|
+
{
|
1246
|
+
debug_context->stop_reason = CTX_STOP_BREAKPOINT;
|
1247
|
+
rb_funcall(context, idAtBreakpoint, 1, breakpoint);
|
1248
|
+
}
|
1249
|
+
else
|
1250
|
+
debug_context->breakpoint = Qnil;
|
1251
|
+
call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
|
1252
|
+
break;
|
1253
|
+
}
|
1254
|
+
breakpoint = check_breakpoints_by_pos(debug_context, file, line);
|
1255
|
+
if (breakpoint != Qnil)
|
1256
|
+
call_at_line_check(self, debug_context, breakpoint, context, file, line);
|
1257
|
+
break;
|
1258
|
+
}
|
1259
|
+
case RUBY_EVENT_C_RETURN:
|
1260
|
+
{
|
1261
|
+
/* note if a block is given we fall through! */
|
1262
|
+
if(!rb_method_boundp(klass, mid, 0) || !c_call_new_frame_p(klass, mid))
|
1263
|
+
break;
|
1264
|
+
}
|
1265
|
+
case RUBY_EVENT_RETURN:
|
1266
|
+
case RUBY_EVENT_END:
|
1267
|
+
{
|
1268
|
+
if(debug_context->cfp_count == debug_context->stop_frame)
|
1269
|
+
{
|
1270
|
+
debug_context->stop_next = 1;
|
1271
|
+
debug_context->stop_frame = 0;
|
1272
|
+
/* NOTE: can't use call_at_line function here to trigger a debugger event.
|
1273
|
+
this can lead to segfault. We should only unroll the stack on this event.
|
1274
|
+
*/
|
1275
|
+
}
|
1276
|
+
CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
|
1277
|
+
|
1278
|
+
break;
|
1279
|
+
}
|
1280
|
+
}
|
1281
|
+
|
1282
|
+
cleanup:
|
1283
|
+
|
1284
|
+
debug_context->stop_reason = CTX_STOP_NONE;
|
1285
|
+
|
1286
|
+
/* check that all contexts point to alive threads */
|
1287
|
+
if(hook_count - last_check > 3000)
|
1288
|
+
{
|
1289
|
+
check_thread_contexts();
|
1290
|
+
last_check = hook_count;
|
1291
|
+
}
|
1292
|
+
|
1293
|
+
/* release a lock */
|
1294
|
+
locker = Qnil;
|
1295
|
+
|
1296
|
+
/* let the next thread to run */
|
1297
|
+
{
|
1298
|
+
VALUE next_thread = remove_from_locked();
|
1299
|
+
if(next_thread != Qnil)
|
1300
|
+
rb_thread_run(next_thread);
|
1301
|
+
}
|
1302
|
+
}
|
1303
|
+
|
1304
|
+
/*
|
1305
|
+
* call-seq:
|
1306
|
+
* Debugger.start_ -> bool
|
1307
|
+
* Debugger.start_ { ... } -> bool
|
1308
|
+
*
|
1309
|
+
* Deprecated.
|
1310
|
+
*
|
1311
|
+
*/
|
1312
|
+
static VALUE
|
1313
|
+
debug_start(VALUE self)
|
1314
|
+
{
|
1315
|
+
hook_off = Qfalse;
|
1316
|
+
return(Qtrue);
|
1317
|
+
}
|
1318
|
+
|
1319
|
+
/*
|
1320
|
+
* call-seq:
|
1321
|
+
* Debugger.stop -> bool
|
1322
|
+
*
|
1323
|
+
* Deprecated.
|
1324
|
+
*/
|
1325
|
+
static VALUE
|
1326
|
+
debug_stop(VALUE self)
|
1327
|
+
{
|
1328
|
+
hook_off = Qtrue;
|
1329
|
+
return Qtrue;
|
1330
|
+
}
|
1331
|
+
|
1332
|
+
static int
|
1333
|
+
find_last_context_func(st_data_t key, st_data_t value, st_data_t result_arg)
|
1334
|
+
{
|
1335
|
+
debug_context_t *debug_context;
|
1336
|
+
VALUE *result = (VALUE *)result_arg;
|
1337
|
+
if(!value) {
|
1338
|
+
return ST_CONTINUE;
|
1339
|
+
}
|
1340
|
+
Data_Get_Struct((VALUE)value, debug_context_t, debug_context);
|
1341
|
+
if(debug_context->thnum == last_debugged_thnum)
|
1342
|
+
{
|
1343
|
+
*result = value;
|
1344
|
+
return ST_STOP;
|
1345
|
+
}
|
1346
|
+
return ST_CONTINUE;
|
1347
|
+
}
|
1348
|
+
|
1349
|
+
/*
|
1350
|
+
* call-seq:
|
1351
|
+
* Debugger.last_interrupted -> context
|
1352
|
+
*
|
1353
|
+
* Returns last debugged context.
|
1354
|
+
*/
|
1355
|
+
static VALUE
|
1356
|
+
debug_last_interrupted(VALUE self)
|
1357
|
+
{
|
1358
|
+
VALUE result = Qnil;
|
1359
|
+
threads_table_t *threads_table;
|
1360
|
+
|
1361
|
+
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
|
1362
|
+
|
1363
|
+
st_foreach(threads_table->tbl, find_last_context_func, (st_data_t)&result);
|
1364
|
+
return result;
|
1365
|
+
}
|
1366
|
+
|
1367
|
+
/*
|
1368
|
+
* call-seq:
|
1369
|
+
* Debugger.current_context -> context
|
1370
|
+
*
|
1371
|
+
* Returns current context.
|
1372
|
+
* <i>Note:</i> Debugger.current_context.thread == Thread.current
|
1373
|
+
*/
|
1374
|
+
static VALUE
|
1375
|
+
debug_current_context(VALUE self)
|
1376
|
+
{
|
1377
|
+
VALUE thread, context;
|
1378
|
+
|
1379
|
+
thread = rb_thread_current();
|
1380
|
+
thread_context_lookup(thread, &context, NULL, 1);
|
1381
|
+
|
1382
|
+
return context;
|
1383
|
+
}
|
1384
|
+
|
1385
|
+
/*
|
1386
|
+
* call-seq:
|
1387
|
+
* Debugger.thread_context(thread) -> context
|
1388
|
+
*
|
1389
|
+
* Returns context of the thread passed as an argument.
|
1390
|
+
*/
|
1391
|
+
static VALUE
|
1392
|
+
debug_thread_context(VALUE self, VALUE thread)
|
1393
|
+
{
|
1394
|
+
VALUE context;
|
1395
|
+
|
1396
|
+
thread_context_lookup(thread, &context, NULL, 1);
|
1397
|
+
return context;
|
1398
|
+
}
|
1399
|
+
|
1400
|
+
/*
|
1401
|
+
* call-seq:
|
1402
|
+
* Debugger.contexts -> array
|
1403
|
+
*
|
1404
|
+
* Returns an array of all contexts.
|
1405
|
+
*/
|
1406
|
+
static VALUE
|
1407
|
+
debug_contexts(VALUE self)
|
1408
|
+
{
|
1409
|
+
volatile VALUE list;
|
1410
|
+
volatile VALUE new_list;
|
1411
|
+
VALUE thread, context;
|
1412
|
+
threads_table_t *threads_table;
|
1413
|
+
debug_context_t *debug_context;
|
1414
|
+
int i;
|
1415
|
+
|
1416
|
+
new_list = rb_ary_new();
|
1417
|
+
list = rb_funcall(rb_cThread, idList, 0);
|
1418
|
+
for(i = 0; i < RARRAY_LEN(list); i++)
|
1419
|
+
{
|
1420
|
+
thread = rb_ary_entry(list, i);
|
1421
|
+
thread_context_lookup(thread, &context, NULL, 1);
|
1422
|
+
rb_ary_push(new_list, context);
|
1423
|
+
}
|
1424
|
+
threads_table_clear(rdebug_threads_tbl);
|
1425
|
+
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
|
1426
|
+
for(i = 0; i < RARRAY_LEN(new_list); i++)
|
1427
|
+
{
|
1428
|
+
context = rb_ary_entry(new_list, i);
|
1429
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
1430
|
+
st_insert(threads_table->tbl, debug_context->thread_id, context);
|
1431
|
+
}
|
1432
|
+
|
1433
|
+
return new_list;
|
1434
|
+
}
|
1435
|
+
|
1436
|
+
/*
|
1437
|
+
* call-seq:
|
1438
|
+
* Debugger.suspend -> Debugger
|
1439
|
+
*
|
1440
|
+
* Suspends all contexts.
|
1441
|
+
*/
|
1442
|
+
static VALUE
|
1443
|
+
debug_suspend(VALUE self)
|
1444
|
+
{
|
1445
|
+
VALUE current, context;
|
1446
|
+
VALUE context_list;
|
1447
|
+
debug_context_t *debug_context;
|
1448
|
+
int i;
|
1449
|
+
|
1450
|
+
context_list = debug_contexts(self);
|
1451
|
+
thread_context_lookup(rb_thread_current(), ¤t, NULL, 1);
|
1452
|
+
|
1453
|
+
for(i = 0; i < RARRAY_LEN(context_list); i++)
|
1454
|
+
{
|
1455
|
+
context = rb_ary_entry(context_list, i);
|
1456
|
+
if(current == context)
|
1457
|
+
continue;
|
1458
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
1459
|
+
context_suspend_0(debug_context);
|
1460
|
+
}
|
1461
|
+
|
1462
|
+
return self;
|
1463
|
+
}
|
1464
|
+
|
1465
|
+
/*
|
1466
|
+
* call-seq:
|
1467
|
+
* Debugger.resume -> Debugger
|
1468
|
+
*
|
1469
|
+
* Resumes all contexts.
|
1470
|
+
*/
|
1471
|
+
static VALUE
|
1472
|
+
debug_resume(VALUE self)
|
1473
|
+
{
|
1474
|
+
VALUE current, context;
|
1475
|
+
VALUE context_list;
|
1476
|
+
debug_context_t *debug_context;
|
1477
|
+
int i;
|
1478
|
+
|
1479
|
+
context_list = debug_contexts(self);
|
1480
|
+
|
1481
|
+
thread_context_lookup(rb_thread_current(), ¤t, NULL, 1);
|
1482
|
+
for(i = 0; i < RARRAY_LEN(context_list); i++)
|
1483
|
+
{
|
1484
|
+
context = rb_ary_entry(context_list, i);
|
1485
|
+
if(current == context)
|
1486
|
+
continue;
|
1487
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
1488
|
+
context_resume_0(debug_context);
|
1489
|
+
}
|
1490
|
+
|
1491
|
+
rb_thread_schedule();
|
1492
|
+
|
1493
|
+
return self;
|
1494
|
+
}
|
1495
|
+
|
1496
|
+
/*
|
1497
|
+
* call-seq:
|
1498
|
+
* Debugger.tracing -> bool
|
1499
|
+
*
|
1500
|
+
* Returns +true+ if the global tracing is activated.
|
1501
|
+
*/
|
1502
|
+
static VALUE
|
1503
|
+
debug_tracing(VALUE self)
|
1504
|
+
{
|
1505
|
+
return tracing;
|
1506
|
+
}
|
1507
|
+
|
1508
|
+
/*
|
1509
|
+
* call-seq:
|
1510
|
+
* Debugger.tracing = bool
|
1511
|
+
*
|
1512
|
+
* Sets the global tracing flag.
|
1513
|
+
*/
|
1514
|
+
static VALUE
|
1515
|
+
debug_set_tracing(VALUE self, VALUE value)
|
1516
|
+
{
|
1517
|
+
tracing = RTEST(value) ? Qtrue : Qfalse;
|
1518
|
+
return value;
|
1519
|
+
}
|
1520
|
+
|
1521
|
+
/* :nodoc: */
|
1522
|
+
static VALUE
|
1523
|
+
debug_debug(VALUE self)
|
1524
|
+
{
|
1525
|
+
return debug_flag;
|
1526
|
+
}
|
1527
|
+
|
1528
|
+
/* :nodoc: */
|
1529
|
+
static VALUE
|
1530
|
+
debug_set_debug(VALUE self, VALUE value)
|
1531
|
+
{
|
1532
|
+
debug_flag = RTEST(value) ? Qtrue : Qfalse;
|
1533
|
+
return value;
|
1534
|
+
}
|
1535
|
+
|
1536
|
+
/* :nodoc: */
|
1537
|
+
static VALUE
|
1538
|
+
debug_catchall(VALUE self)
|
1539
|
+
{
|
1540
|
+
return catchall;
|
1541
|
+
}
|
1542
|
+
|
1543
|
+
/* :nodoc: */
|
1544
|
+
static VALUE
|
1545
|
+
debug_set_catchall(VALUE self, VALUE value)
|
1546
|
+
{
|
1547
|
+
catchall = RTEST(value) ? Qtrue : Qfalse;
|
1548
|
+
return value;
|
1549
|
+
}
|
1550
|
+
|
1551
|
+
/* :nodoc: */
|
1552
|
+
static VALUE
|
1553
|
+
debug_skip_next_exception(VALUE self)
|
1554
|
+
{
|
1555
|
+
skip_next_exception = Qtrue;
|
1556
|
+
return(Qtrue);
|
1557
|
+
}
|
1558
|
+
|
1559
|
+
/* :nodoc: */
|
1560
|
+
static VALUE
|
1561
|
+
debug_thread_inherited(VALUE klass)
|
1562
|
+
{
|
1563
|
+
rb_raise(rb_eRuntimeError, "Can't inherit Debugger::DebugThread class");
|
1564
|
+
}
|
1565
|
+
|
1566
|
+
/*
|
1567
|
+
* call-seq:
|
1568
|
+
* Debugger.debug_load(file, stop = false, increment_start = false) -> nil
|
1569
|
+
*
|
1570
|
+
* Same as Kernel#load but resets current context's frames.
|
1571
|
+
* +stop+ parameter forces the debugger to stop at the first line of code in the +file+
|
1572
|
+
* +increment_start+ determines if start_count should be incremented. When
|
1573
|
+
* control threads are used, they have to be set up before loading the
|
1574
|
+
* debugger; so here +increment_start+ will be false.
|
1575
|
+
* FOR INTERNAL USE ONLY.
|
1576
|
+
*/
|
1577
|
+
static VALUE
|
1578
|
+
debug_debug_load(int argc, VALUE *argv, VALUE self)
|
1579
|
+
{
|
1580
|
+
VALUE file, stop, context, increment_start;
|
1581
|
+
debug_context_t *debug_context;
|
1582
|
+
int state = 0;
|
1583
|
+
|
1584
|
+
if(rb_scan_args(argc, argv, "12", &file, &stop, &increment_start) == 1)
|
1585
|
+
{
|
1586
|
+
stop = Qfalse;
|
1587
|
+
increment_start = Qtrue;
|
1588
|
+
}
|
1589
|
+
|
1590
|
+
context = debug_current_context(self);
|
1591
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
1592
|
+
debug_context->start_cfp = NULL;
|
1593
|
+
debug_context->top_cfp = GET_THREAD()->cfp;
|
1594
|
+
if(RTEST(stop))
|
1595
|
+
debug_context->stop_next = 1;
|
1596
|
+
/* Initializing $0 to the script's path */
|
1597
|
+
ruby_script(RSTRING_PTR(file));
|
1598
|
+
rb_load_protect(file, 0, &state);
|
1599
|
+
if (0 != state)
|
1600
|
+
{
|
1601
|
+
VALUE errinfo = rb_errinfo();
|
1602
|
+
debug_suspend(self);
|
1603
|
+
reset_stepping_stop_points(debug_context);
|
1604
|
+
rb_set_errinfo(Qnil);
|
1605
|
+
return errinfo;
|
1606
|
+
}
|
1607
|
+
|
1608
|
+
/* We should run all at_exit handler's in order to provide,
|
1609
|
+
* for instance, a chance to run all defined test cases */
|
1610
|
+
rb_exec_end_proc();
|
1611
|
+
|
1612
|
+
return Qnil;
|
1613
|
+
}
|
1614
|
+
|
1615
|
+
static VALUE
|
1616
|
+
set_current_skipped_status(VALUE status)
|
1617
|
+
{
|
1618
|
+
VALUE context;
|
1619
|
+
debug_context_t *debug_context;
|
1620
|
+
|
1621
|
+
context = debug_current_context(Qnil);
|
1622
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
1623
|
+
if(status)
|
1624
|
+
CTX_FL_SET(debug_context, CTX_FL_SKIPPED);
|
1625
|
+
else
|
1626
|
+
CTX_FL_UNSET(debug_context, CTX_FL_SKIPPED);
|
1627
|
+
return Qnil;
|
1628
|
+
}
|
1629
|
+
|
1630
|
+
/*
|
1631
|
+
* call-seq:
|
1632
|
+
* Debugger.skip { block } -> obj or nil
|
1633
|
+
*
|
1634
|
+
* The code inside of the block is escaped from the debugger.
|
1635
|
+
*/
|
1636
|
+
static VALUE
|
1637
|
+
debug_skip(VALUE self)
|
1638
|
+
{
|
1639
|
+
if (!rb_block_given_p()) {
|
1640
|
+
rb_raise(rb_eArgError, "called without a block");
|
1641
|
+
}
|
1642
|
+
set_current_skipped_status(Qtrue);
|
1643
|
+
return rb_ensure(rb_yield, Qnil, set_current_skipped_status, Qfalse);
|
1644
|
+
}
|
1645
|
+
|
1646
|
+
static VALUE
|
1647
|
+
debug_at_exit_c(VALUE proc)
|
1648
|
+
{
|
1649
|
+
return rb_funcall(proc, rb_intern("call"), 0);
|
1650
|
+
}
|
1651
|
+
|
1652
|
+
static void
|
1653
|
+
debug_at_exit_i(VALUE proc)
|
1654
|
+
{
|
1655
|
+
set_current_skipped_status(Qtrue);
|
1656
|
+
rb_ensure(debug_at_exit_c, proc, set_current_skipped_status, Qfalse);
|
1657
|
+
}
|
1658
|
+
|
1659
|
+
/*
|
1660
|
+
* call-seq:
|
1661
|
+
* Debugger.debug_at_exit { block } -> proc
|
1662
|
+
*
|
1663
|
+
* Register <tt>at_exit</tt> hook which is escaped from the debugger.
|
1664
|
+
* FOR INTERNAL USE ONLY.
|
1665
|
+
*/
|
1666
|
+
static VALUE
|
1667
|
+
debug_at_exit(VALUE self)
|
1668
|
+
{
|
1669
|
+
VALUE proc;
|
1670
|
+
if (!rb_block_given_p())
|
1671
|
+
rb_raise(rb_eArgError, "called without a block");
|
1672
|
+
proc = rb_block_proc();
|
1673
|
+
rb_set_end_proc(debug_at_exit_i, proc);
|
1674
|
+
return proc;
|
1675
|
+
}
|
1676
|
+
|
1677
|
+
/*
|
1678
|
+
* call-seq:
|
1679
|
+
* context.step(steps, force = false)
|
1680
|
+
*
|
1681
|
+
* Stops the current context after a number of +steps+ are made.
|
1682
|
+
* +force+ parameter (if true) ensures that the cursor moves from the current line.
|
1683
|
+
*/
|
1684
|
+
static VALUE
|
1685
|
+
context_stop_next(int argc, VALUE *argv, VALUE self)
|
1686
|
+
{
|
1687
|
+
VALUE steps, force;
|
1688
|
+
debug_context_t *debug_context;
|
1689
|
+
|
1690
|
+
rb_scan_args(argc, argv, "11", &steps, &force);
|
1691
|
+
if(FIX2INT(steps) < 0)
|
1692
|
+
rb_raise(rb_eRuntimeError, "Steps argument can't be negative.");
|
1693
|
+
|
1694
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1695
|
+
debug_context->stop_next = FIX2INT(steps);
|
1696
|
+
if(RTEST(force))
|
1697
|
+
CTX_FL_SET(debug_context, CTX_FL_FORCE_MOVE);
|
1698
|
+
else
|
1699
|
+
CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
|
1700
|
+
|
1701
|
+
return steps;
|
1702
|
+
}
|
1703
|
+
|
1704
|
+
/*
|
1705
|
+
* call-seq:
|
1706
|
+
* context.step_over(steps, frame = nil, force = false)
|
1707
|
+
*
|
1708
|
+
* Steps over a +steps+ number of times.
|
1709
|
+
* Make step over operation on +frame+, by default the current frame.
|
1710
|
+
* +force+ parameter (if true) ensures that the cursor moves from the current line.
|
1711
|
+
*/
|
1712
|
+
static VALUE
|
1713
|
+
context_step_over(int argc, VALUE *argv, VALUE self)
|
1714
|
+
{
|
1715
|
+
VALUE lines, frame, force;
|
1716
|
+
debug_context_t *debug_context;
|
1717
|
+
|
1718
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1719
|
+
if(debug_context->cfp_count == 0)
|
1720
|
+
rb_raise(rb_eRuntimeError, "No frames collected.");
|
1721
|
+
|
1722
|
+
rb_scan_args(argc, argv, "12", &lines, &frame, &force);
|
1723
|
+
debug_context->stop_line = FIX2INT(lines);
|
1724
|
+
CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
|
1725
|
+
if(frame == Qnil)
|
1726
|
+
{
|
1727
|
+
debug_context->dest_frame = debug_context->cfp_count;
|
1728
|
+
}
|
1729
|
+
else
|
1730
|
+
{
|
1731
|
+
if(FIX2INT(frame) < 0 && FIX2INT(frame) >= debug_context->cfp_count)
|
1732
|
+
rb_raise(rb_eRuntimeError, "Destination frame is out of range.");
|
1733
|
+
debug_context->dest_frame = debug_context->cfp_count - FIX2INT(frame);
|
1734
|
+
}
|
1735
|
+
if(RTEST(force))
|
1736
|
+
CTX_FL_SET(debug_context, CTX_FL_FORCE_MOVE);
|
1737
|
+
else
|
1738
|
+
CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
|
1739
|
+
|
1740
|
+
return Qnil;
|
1741
|
+
}
|
1742
|
+
|
1743
|
+
/*
|
1744
|
+
* call-seq:
|
1745
|
+
* context.stop_frame(frame)
|
1746
|
+
*
|
1747
|
+
* Stops when a frame with number +frame+ is activated. Implements +finish+ and +next+ commands.
|
1748
|
+
*/
|
1749
|
+
static VALUE
|
1750
|
+
context_stop_frame(VALUE self, VALUE frame)
|
1751
|
+
{
|
1752
|
+
debug_context_t *debug_context;
|
1753
|
+
|
1754
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1755
|
+
if(FIX2INT(frame) < 0 && FIX2INT(frame) >= debug_context->cfp_count)
|
1756
|
+
rb_raise(rb_eRuntimeError, "Stop frame is out of range.");
|
1757
|
+
debug_context->stop_frame = debug_context->cfp_count - FIX2INT(frame);
|
1758
|
+
|
1759
|
+
return frame;
|
1760
|
+
}
|
1761
|
+
|
1762
|
+
inline static int
|
1763
|
+
check_frame_number(debug_context_t *debug_context, VALUE frame)
|
1764
|
+
{
|
1765
|
+
int frame_n;
|
1766
|
+
|
1767
|
+
frame_n = FIX2INT(frame);
|
1768
|
+
if(frame_n < 0 || frame_n >= debug_context->cfp_count)
|
1769
|
+
rb_raise(rb_eArgError, "Invalid frame number %d, stack (0...%d)",
|
1770
|
+
frame_n, debug_context->cfp_count - 1);
|
1771
|
+
return frame_n;
|
1772
|
+
}
|
1773
|
+
|
1774
|
+
static int
|
1775
|
+
optional_frame_position(int argc, VALUE *argv)
|
1776
|
+
{
|
1777
|
+
unsigned int i_scanned;
|
1778
|
+
VALUE level;
|
1779
|
+
|
1780
|
+
if ((argc > 1) || (argc < 0))
|
1781
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 or 1)", argc);
|
1782
|
+
i_scanned = rb_scan_args(argc, argv, "01", &level);
|
1783
|
+
if (0 == i_scanned) {
|
1784
|
+
level = INT2FIX(0);
|
1785
|
+
}
|
1786
|
+
return level;
|
1787
|
+
}
|
1788
|
+
|
1789
|
+
/*
|
1790
|
+
* call-seq:
|
1791
|
+
* context.frame_binding(frame_position=0) -> binding
|
1792
|
+
*
|
1793
|
+
* Returns frame's binding.
|
1794
|
+
*/
|
1795
|
+
static VALUE
|
1796
|
+
context_frame_binding(int argc, VALUE *argv, VALUE self)
|
1797
|
+
{
|
1798
|
+
VALUE frame;
|
1799
|
+
debug_context_t *debug_context;
|
1800
|
+
rb_control_frame_t *cfp;
|
1801
|
+
rb_thread_t *th;
|
1802
|
+
VALUE bindval;
|
1803
|
+
rb_binding_t *bind;
|
1804
|
+
|
1805
|
+
frame = optional_frame_position(argc, argv);
|
1806
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1807
|
+
GetThreadPtr(context_thread_0(debug_context), th);
|
1808
|
+
cfp = GET_CFP;
|
1809
|
+
|
1810
|
+
bindval = binding_alloc(rb_cBinding);
|
1811
|
+
GetBindingPtr(bindval, bind);
|
1812
|
+
bind->env = rb_vm_make_env_object(th, cfp);
|
1813
|
+
return bindval;
|
1814
|
+
}
|
1815
|
+
|
1816
|
+
/*
|
1817
|
+
* call-seq:
|
1818
|
+
* context.frame_method(frame_position=0) -> sym
|
1819
|
+
*
|
1820
|
+
* Returns the sym of the called method.
|
1821
|
+
*/
|
1822
|
+
static VALUE
|
1823
|
+
context_frame_id(int argc, VALUE *argv, VALUE self)
|
1824
|
+
{
|
1825
|
+
VALUE frame;
|
1826
|
+
debug_context_t *debug_context;
|
1827
|
+
rb_control_frame_t *cfp;
|
1828
|
+
|
1829
|
+
frame = optional_frame_position(argc, argv);
|
1830
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1831
|
+
cfp = GET_CFP;
|
1832
|
+
if (cfp->iseq == NULL || cfp->iseq->defined_method_id == 0) return(Qnil);
|
1833
|
+
|
1834
|
+
#if defined HAVE_RB_CONTROL_FRAME_T_METHOD_ID
|
1835
|
+
return(RUBYVM_CFUNC_FRAME_P(cfp) ? ID2SYM(cfp->method_id) : ID2SYM(cfp->iseq->defined_method_id));
|
1836
|
+
#elif defined HAVE_RB_METHOD_ENTRY_T_CALLED_ID
|
1837
|
+
return(RUBYVM_CFUNC_FRAME_P(cfp) ? ID2SYM(cfp->me->called_id) : ID2SYM(cfp->iseq->defined_method_id));
|
1838
|
+
#endif
|
1839
|
+
}
|
1840
|
+
|
1841
|
+
/*
|
1842
|
+
* call-seq:
|
1843
|
+
* context.frame_line(frame_position) -> int
|
1844
|
+
*
|
1845
|
+
* Returns the line number in the file.
|
1846
|
+
*/
|
1847
|
+
static VALUE
|
1848
|
+
context_frame_line(int argc, VALUE *argv, VALUE self)
|
1849
|
+
{
|
1850
|
+
VALUE frame;
|
1851
|
+
debug_context_t *debug_context;
|
1852
|
+
rb_control_frame_t *cfp;
|
1853
|
+
|
1854
|
+
frame = optional_frame_position(argc, argv);
|
1855
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1856
|
+
cfp = GET_CFP;
|
1857
|
+
return(INT2FIX(rb_vm_get_sourceline(cfp)));
|
1858
|
+
}
|
1859
|
+
|
1860
|
+
/*
|
1861
|
+
* call-seq:
|
1862
|
+
* context.frame_file(frame_position) -> string
|
1863
|
+
*
|
1864
|
+
* Returns the name of the file.
|
1865
|
+
*/
|
1866
|
+
static VALUE
|
1867
|
+
context_frame_file(int argc, VALUE *argv, VALUE self)
|
1868
|
+
{
|
1869
|
+
VALUE frame;
|
1870
|
+
debug_context_t *debug_context;
|
1871
|
+
rb_control_frame_t *cfp;
|
1872
|
+
|
1873
|
+
frame = optional_frame_position(argc, argv);
|
1874
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1875
|
+
cfp = GET_CFP;
|
1876
|
+
while (cfp <= debug_context->start_cfp)
|
1877
|
+
{
|
1878
|
+
if (cfp->iseq != NULL)
|
1879
|
+
return(cfp->iseq->filename);
|
1880
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
1881
|
+
}
|
1882
|
+
return(Qnil);
|
1883
|
+
}
|
1884
|
+
|
1885
|
+
/*
|
1886
|
+
* call-seq:
|
1887
|
+
* context.frame_locals(frame) -> hash
|
1888
|
+
*
|
1889
|
+
* Returns frame's local variables.
|
1890
|
+
*/
|
1891
|
+
static VALUE
|
1892
|
+
context_frame_locals(int argc, VALUE *argv, VALUE self)
|
1893
|
+
{
|
1894
|
+
VALUE frame;
|
1895
|
+
debug_context_t *debug_context;
|
1896
|
+
int i;
|
1897
|
+
rb_control_frame_t *cfp;
|
1898
|
+
rb_iseq_t *iseq;
|
1899
|
+
VALUE hash;
|
1900
|
+
|
1901
|
+
frame = optional_frame_position(argc, argv);
|
1902
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1903
|
+
cfp = GET_CFP;
|
1904
|
+
iseq = cfp->iseq;
|
1905
|
+
hash = rb_hash_new();
|
1906
|
+
|
1907
|
+
if (iseq != NULL && iseq->local_table != NULL)
|
1908
|
+
{
|
1909
|
+
/* Note rb_iseq_disasm() is instructive in coming up with this code */
|
1910
|
+
for (i = 0; i < iseq->local_table_size; i++)
|
1911
|
+
{
|
1912
|
+
VALUE str = rb_id2str(iseq->local_table[i]);
|
1913
|
+
if (str != 0)
|
1914
|
+
rb_hash_aset(hash, str, *(cfp->dfp - iseq->local_size + i));
|
1915
|
+
}
|
1916
|
+
}
|
1917
|
+
|
1918
|
+
return(hash);
|
1919
|
+
}
|
1920
|
+
|
1921
|
+
/*
|
1922
|
+
* call-seq:
|
1923
|
+
* context.frame_args(frame_position=0) -> list
|
1924
|
+
*
|
1925
|
+
* Returns frame's argument parameters
|
1926
|
+
*/
|
1927
|
+
static VALUE
|
1928
|
+
context_frame_args(int argc, VALUE *argv, VALUE self)
|
1929
|
+
{
|
1930
|
+
VALUE frame;
|
1931
|
+
debug_context_t *debug_context;
|
1932
|
+
rb_control_frame_t *cfp;
|
1933
|
+
|
1934
|
+
frame = optional_frame_position(argc, argv);
|
1935
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1936
|
+
|
1937
|
+
cfp = GET_CFP;
|
1938
|
+
if (cfp && cfp->iseq && cfp->iseq->local_table && cfp->iseq->argc)
|
1939
|
+
{
|
1940
|
+
int i;
|
1941
|
+
VALUE list;
|
1942
|
+
|
1943
|
+
list = rb_ary_new2(cfp->iseq->argc);
|
1944
|
+
for (i = 0; i < cfp->iseq->argc; i++)
|
1945
|
+
{
|
1946
|
+
if (!rb_is_local_id(cfp->iseq->local_table[i])) continue; /* skip flip states */
|
1947
|
+
rb_ary_push(list, rb_id2str(cfp->iseq->local_table[i]));
|
1948
|
+
}
|
1949
|
+
|
1950
|
+
return(list);
|
1951
|
+
}
|
1952
|
+
return(rb_ary_new2(0));
|
1953
|
+
}
|
1954
|
+
|
1955
|
+
/*
|
1956
|
+
* call-seq:
|
1957
|
+
* context.frame_self(frame_postion=0) -> obj
|
1958
|
+
*
|
1959
|
+
* Returns self object of the frame.
|
1960
|
+
*/
|
1961
|
+
static VALUE
|
1962
|
+
context_frame_self(int argc, VALUE *argv, VALUE self)
|
1963
|
+
{
|
1964
|
+
VALUE frame;
|
1965
|
+
debug_context_t *debug_context;
|
1966
|
+
|
1967
|
+
frame = optional_frame_position(argc, argv);
|
1968
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1969
|
+
|
1970
|
+
return(GET_CFP->self);
|
1971
|
+
}
|
1972
|
+
|
1973
|
+
/*
|
1974
|
+
* call-seq:
|
1975
|
+
* context.frame_class(frame_position) -> obj
|
1976
|
+
*
|
1977
|
+
* Returns the real class of the frame.
|
1978
|
+
* It could be different than context.frame_self(frame).class
|
1979
|
+
*/
|
1980
|
+
static VALUE
|
1981
|
+
context_frame_class(int argc, VALUE *argv, VALUE self)
|
1982
|
+
{
|
1983
|
+
VALUE klass;
|
1984
|
+
VALUE frame;
|
1985
|
+
debug_context_t *debug_context;
|
1986
|
+
rb_control_frame_t *cfp;
|
1987
|
+
|
1988
|
+
frame = optional_frame_position(argc, argv);
|
1989
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
1990
|
+
cfp = GET_CFP;
|
1991
|
+
|
1992
|
+
if (cfp->iseq == NULL) return(Qnil);
|
1993
|
+
klass = real_class(cfp->iseq->klass);
|
1994
|
+
if(TYPE(klass) == T_CLASS || TYPE(klass) == T_MODULE)
|
1995
|
+
return klass;
|
1996
|
+
return Qnil;
|
1997
|
+
}
|
1998
|
+
|
1999
|
+
|
2000
|
+
/*
|
2001
|
+
* call-seq:
|
2002
|
+
* context.stack_size-> int
|
2003
|
+
*
|
2004
|
+
* Returns the size of the context stack.
|
2005
|
+
*/
|
2006
|
+
static VALUE
|
2007
|
+
context_stack_size(VALUE self)
|
2008
|
+
{
|
2009
|
+
debug_context_t *debug_context;
|
2010
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2011
|
+
return(INT2FIX(debug_context->cfp_count));
|
2012
|
+
}
|
2013
|
+
|
2014
|
+
/*
|
2015
|
+
* call-seq:
|
2016
|
+
* context.stack_inc-> bool
|
2017
|
+
*
|
2018
|
+
* Increments the top of the call stack to the previous valid frame, if possible.
|
2019
|
+
*/
|
2020
|
+
static VALUE
|
2021
|
+
context_stack_inc(VALUE self)
|
2022
|
+
{
|
2023
|
+
debug_context_t *debug_context;
|
2024
|
+
rb_control_frame_t *cfp;
|
2025
|
+
rb_thread_t *th;
|
2026
|
+
|
2027
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2028
|
+
GetThreadPtr(context_thread_0(debug_context), th);
|
2029
|
+
cfp = debug_context->start_cfp;
|
2030
|
+
|
2031
|
+
while (cfp < RUBY_VM_END_CONTROL_FRAME(th))
|
2032
|
+
{
|
2033
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
2034
|
+
if (cfp->iseq != NULL && cfp->pc != NULL)
|
2035
|
+
{
|
2036
|
+
debug_context->start_cfp = cfp;
|
2037
|
+
if (GET_THREAD() == th)
|
2038
|
+
set_cfp(debug_context);
|
2039
|
+
return(Qtrue);
|
2040
|
+
}
|
2041
|
+
}
|
2042
|
+
return(Qfalse);
|
2043
|
+
}
|
2044
|
+
|
2045
|
+
/*
|
2046
|
+
* call-seq:
|
2047
|
+
* context.stack_dec-> bool
|
2048
|
+
*
|
2049
|
+
* Decrements the top of the call stack to the next valid frame, if possible.
|
2050
|
+
*/
|
2051
|
+
static VALUE
|
2052
|
+
context_stack_dec(VALUE self)
|
2053
|
+
{
|
2054
|
+
debug_context_t *debug_context;
|
2055
|
+
rb_control_frame_t *cfp;
|
2056
|
+
rb_thread_t *th;
|
2057
|
+
|
2058
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2059
|
+
GetThreadPtr(context_thread_0(debug_context), th);
|
2060
|
+
cfp = debug_context->start_cfp;
|
2061
|
+
|
2062
|
+
while (cfp >= debug_context->cur_cfp)
|
2063
|
+
{
|
2064
|
+
cfp = RUBY_VM_NEXT_CONTROL_FRAME(cfp);
|
2065
|
+
if (cfp->iseq != NULL && cfp->pc != NULL)
|
2066
|
+
{
|
2067
|
+
debug_context->start_cfp = cfp;
|
2068
|
+
if (GET_THREAD() == th)
|
2069
|
+
set_cfp(debug_context);
|
2070
|
+
return(Qtrue);
|
2071
|
+
}
|
2072
|
+
}
|
2073
|
+
return(Qfalse);
|
2074
|
+
}
|
2075
|
+
|
2076
|
+
/*
|
2077
|
+
* call-seq:
|
2078
|
+
* context.thread -> thread
|
2079
|
+
*
|
2080
|
+
* Returns a thread this context is associated with.
|
2081
|
+
*/
|
2082
|
+
static VALUE
|
2083
|
+
context_thread(VALUE self)
|
2084
|
+
{
|
2085
|
+
debug_context_t *debug_context;
|
2086
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2087
|
+
return(id2ref(debug_context->thread_id));
|
2088
|
+
}
|
2089
|
+
|
2090
|
+
/*
|
2091
|
+
* call-seq:
|
2092
|
+
* context.thnum -> int
|
2093
|
+
*
|
2094
|
+
* Returns the context's number.
|
2095
|
+
*/
|
2096
|
+
static VALUE
|
2097
|
+
context_thnum(VALUE self)
|
2098
|
+
{
|
2099
|
+
debug_context_t *debug_context;
|
2100
|
+
|
2101
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2102
|
+
|
2103
|
+
return INT2FIX(debug_context->thnum);
|
2104
|
+
}
|
2105
|
+
|
2106
|
+
static void
|
2107
|
+
context_suspend_0(debug_context_t *debug_context)
|
2108
|
+
{
|
2109
|
+
VALUE status;
|
2110
|
+
|
2111
|
+
status = rb_funcall(context_thread_0(debug_context), rb_intern("status"), 0);
|
2112
|
+
if(rb_str_cmp(status, rb_str_new2("run")) == 0)
|
2113
|
+
CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
|
2114
|
+
else if(rb_str_cmp(status, rb_str_new2("sleep")) == 0)
|
2115
|
+
CTX_FL_UNSET(debug_context, CTX_FL_WAS_RUNNING);
|
2116
|
+
else
|
2117
|
+
return;
|
2118
|
+
CTX_FL_SET(debug_context, CTX_FL_SUSPEND);
|
2119
|
+
}
|
2120
|
+
|
2121
|
+
static void
|
2122
|
+
context_resume_0(debug_context_t *debug_context)
|
2123
|
+
{
|
2124
|
+
if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
|
2125
|
+
return;
|
2126
|
+
CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
|
2127
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_WAS_RUNNING))
|
2128
|
+
rb_thread_wakeup(context_thread_0(debug_context));
|
2129
|
+
}
|
2130
|
+
|
2131
|
+
/*
|
2132
|
+
* call-seq:
|
2133
|
+
* context.suspend -> nil
|
2134
|
+
*
|
2135
|
+
* Suspends the thread when it is running.
|
2136
|
+
*/
|
2137
|
+
static VALUE
|
2138
|
+
context_suspend(VALUE self)
|
2139
|
+
{
|
2140
|
+
debug_context_t *debug_context;
|
2141
|
+
|
2142
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2143
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
|
2144
|
+
rb_raise(rb_eRuntimeError, "Already suspended.");
|
2145
|
+
context_suspend_0(debug_context);
|
2146
|
+
return Qnil;
|
2147
|
+
}
|
2148
|
+
|
2149
|
+
/*
|
2150
|
+
* call-seq:
|
2151
|
+
* context.suspended? -> bool
|
2152
|
+
*
|
2153
|
+
* Returns +true+ if the thread is suspended by debugger.
|
2154
|
+
*/
|
2155
|
+
static VALUE
|
2156
|
+
context_is_suspended(VALUE self)
|
2157
|
+
{
|
2158
|
+
debug_context_t *debug_context;
|
2159
|
+
|
2160
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2161
|
+
return CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) ? Qtrue : Qfalse;
|
2162
|
+
}
|
2163
|
+
|
2164
|
+
/*
|
2165
|
+
* call-seq:
|
2166
|
+
* context.resume -> nil
|
2167
|
+
*
|
2168
|
+
* Resumes the thread from the suspended mode.
|
2169
|
+
*/
|
2170
|
+
static VALUE
|
2171
|
+
context_resume(VALUE self)
|
2172
|
+
{
|
2173
|
+
debug_context_t *debug_context;
|
2174
|
+
|
2175
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2176
|
+
if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
|
2177
|
+
rb_raise(rb_eRuntimeError, "Thread is not suspended.");
|
2178
|
+
context_resume_0(debug_context);
|
2179
|
+
return Qnil;
|
2180
|
+
}
|
2181
|
+
|
2182
|
+
/*
|
2183
|
+
* call-seq:
|
2184
|
+
* context.tracing -> bool
|
2185
|
+
*
|
2186
|
+
* Returns the tracing flag for the current context.
|
2187
|
+
*/
|
2188
|
+
static VALUE
|
2189
|
+
context_tracing(VALUE self)
|
2190
|
+
{
|
2191
|
+
debug_context_t *debug_context;
|
2192
|
+
|
2193
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2194
|
+
return CTX_FL_TEST(debug_context, CTX_FL_TRACING) ? Qtrue : Qfalse;
|
2195
|
+
}
|
2196
|
+
|
2197
|
+
/*
|
2198
|
+
* call-seq:
|
2199
|
+
* context.tracing = bool
|
2200
|
+
*
|
2201
|
+
* Controls the tracing for this context.
|
2202
|
+
*/
|
2203
|
+
static VALUE
|
2204
|
+
context_set_tracing(VALUE self, VALUE value)
|
2205
|
+
{
|
2206
|
+
debug_context_t *debug_context;
|
2207
|
+
|
2208
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2209
|
+
if(RTEST(value))
|
2210
|
+
CTX_FL_SET(debug_context, CTX_FL_TRACING);
|
2211
|
+
else
|
2212
|
+
CTX_FL_UNSET(debug_context, CTX_FL_TRACING);
|
2213
|
+
return value;
|
2214
|
+
}
|
2215
|
+
|
2216
|
+
/*
|
2217
|
+
* call-seq:
|
2218
|
+
* context.ignored? -> bool
|
2219
|
+
*
|
2220
|
+
* Returns the ignore flag for the current context.
|
2221
|
+
*/
|
2222
|
+
static VALUE
|
2223
|
+
context_ignored(VALUE self)
|
2224
|
+
{
|
2225
|
+
debug_context_t *debug_context;
|
2226
|
+
|
2227
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2228
|
+
return CTX_FL_TEST(debug_context, CTX_FL_IGNORE) ? Qtrue : Qfalse;
|
2229
|
+
}
|
2230
|
+
|
2231
|
+
/*
|
2232
|
+
* call-seq:
|
2233
|
+
* context.dead? -> bool
|
2234
|
+
*
|
2235
|
+
* Returns +true+ if context doesn't represent a live context and is created
|
2236
|
+
* during post-mortem exception handling.
|
2237
|
+
*/
|
2238
|
+
static VALUE
|
2239
|
+
context_dead(VALUE self)
|
2240
|
+
{
|
2241
|
+
debug_context_t *debug_context;
|
2242
|
+
|
2243
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2244
|
+
return CTX_FL_TEST(debug_context, CTX_FL_DEAD) ? Qtrue : Qfalse;
|
2245
|
+
}
|
2246
|
+
|
2247
|
+
/*
|
2248
|
+
* call-seq:
|
2249
|
+
* context.stop_reason -> sym
|
2250
|
+
*
|
2251
|
+
* Returns the reason for the stop. It maybe of the following values:
|
2252
|
+
* :initial, :step, :breakpoint, :catchpoint, :post-mortem
|
2253
|
+
*/
|
2254
|
+
static VALUE
|
2255
|
+
context_stop_reason(VALUE self)
|
2256
|
+
{
|
2257
|
+
debug_context_t *debug_context;
|
2258
|
+
const char * sym_name;
|
2259
|
+
|
2260
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2261
|
+
|
2262
|
+
switch(debug_context->stop_reason)
|
2263
|
+
{
|
2264
|
+
case CTX_STOP_STEP:
|
2265
|
+
sym_name = "step";
|
2266
|
+
break;
|
2267
|
+
case CTX_STOP_BREAKPOINT:
|
2268
|
+
sym_name = "breakpoint";
|
2269
|
+
break;
|
2270
|
+
case CTX_STOP_CATCHPOINT:
|
2271
|
+
sym_name = "catchpoint";
|
2272
|
+
break;
|
2273
|
+
case CTX_STOP_NONE:
|
2274
|
+
default:
|
2275
|
+
sym_name = "none";
|
2276
|
+
}
|
2277
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_DEAD))
|
2278
|
+
sym_name = "post-mortem";
|
2279
|
+
|
2280
|
+
return ID2SYM(rb_intern(sym_name));
|
2281
|
+
}
|
2282
|
+
|
2283
|
+
/*
|
2284
|
+
* call-seq:
|
2285
|
+
* context.jump(line, file) -> bool
|
2286
|
+
*
|
2287
|
+
* Returns +true+ if jump to +line+ in filename +file+ was successful.
|
2288
|
+
*/
|
2289
|
+
static VALUE
|
2290
|
+
context_jump(VALUE self, VALUE line, VALUE file)
|
2291
|
+
{
|
2292
|
+
debug_context_t *debug_context;
|
2293
|
+
int i;
|
2294
|
+
rb_thread_t *th;
|
2295
|
+
rb_control_frame_t *cfp;
|
2296
|
+
rb_control_frame_t *cfp_end;
|
2297
|
+
rb_control_frame_t *cfp_start = NULL;
|
2298
|
+
|
2299
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2300
|
+
GetThreadPtr(context_thread_0(debug_context), th);
|
2301
|
+
line = FIX2INT(line);
|
2302
|
+
|
2303
|
+
cfp_start = debug_context->cur_cfp;
|
2304
|
+
cfp_end = RUBY_VM_END_CONTROL_FRAME(th);
|
2305
|
+
cfp = cfp_start;
|
2306
|
+
if (cfp_start == NULL)
|
2307
|
+
return(INT2FIX(2)); /* couldn't find frame; should never happen */
|
2308
|
+
if ((cfp->pc - cfp->iseq->iseq_encoded) >= (cfp->iseq->iseq_size - 1))
|
2309
|
+
return(INT2FIX(1)); /* no space for opt_call_c_function hijack */
|
2310
|
+
|
2311
|
+
/* find target frame to jump to */
|
2312
|
+
while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, cfp_end))
|
2313
|
+
{
|
2314
|
+
if ((cfp->iseq != NULL) && (rb_str_cmp(file, cfp->iseq->filename) == 0))
|
2315
|
+
{
|
2316
|
+
for (i = 0; i < cfp->iseq->line_info_size; i++)
|
2317
|
+
{
|
2318
|
+
if (cfp->iseq->line_info_table[i].line_no != line)
|
2319
|
+
continue;
|
2320
|
+
|
2321
|
+
/* hijack the currently running code so that we can change the frame PC */
|
2322
|
+
debug_context->saved_jump_ins[0] = cfp_start->pc[0];
|
2323
|
+
debug_context->saved_jump_ins[1] = cfp_start->pc[1];
|
2324
|
+
cfp_start->pc[0] = bin_opt_call_c_function;
|
2325
|
+
cfp_start->pc[1] = (VALUE)do_jump;
|
2326
|
+
|
2327
|
+
debug_context->jump_cfp = cfp;
|
2328
|
+
debug_context->jump_pc =
|
2329
|
+
cfp->iseq->iseq_encoded + cfp->iseq->line_info_table[i].position;
|
2330
|
+
|
2331
|
+
return(INT2FIX(0)); /* success */
|
2332
|
+
}
|
2333
|
+
}
|
2334
|
+
|
2335
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
2336
|
+
}
|
2337
|
+
|
2338
|
+
return(INT2FIX(3)); /* couldn't find a line and file frame match */
|
2339
|
+
}
|
2340
|
+
|
2341
|
+
/*
|
2342
|
+
* call-seq:
|
2343
|
+
* context.break -> bool
|
2344
|
+
*
|
2345
|
+
* Returns +true+ if context is currently running and set flag to break it at next line
|
2346
|
+
*/
|
2347
|
+
static VALUE
|
2348
|
+
context_pause(VALUE self)
|
2349
|
+
{
|
2350
|
+
debug_context_t *debug_context;
|
2351
|
+
rb_thread_t *th;
|
2352
|
+
|
2353
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
2354
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_DEAD))
|
2355
|
+
return(Qfalse);
|
2356
|
+
|
2357
|
+
GetThreadPtr(context_thread_0(debug_context), th);
|
2358
|
+
if (th == GET_THREAD())
|
2359
|
+
return(Qfalse);
|
2360
|
+
|
2361
|
+
debug_context->thread_pause = 1;
|
2362
|
+
return(Qtrue);
|
2363
|
+
}
|
2364
|
+
|
2365
|
+
|
2366
|
+
/*
|
2367
|
+
* Document-class: Context
|
2368
|
+
*
|
2369
|
+
* == Summary
|
2370
|
+
*
|
2371
|
+
* Debugger keeps a single instance of this class for each Ruby thread.
|
2372
|
+
*/
|
2373
|
+
static void
|
2374
|
+
Init_context(void)
|
2375
|
+
{
|
2376
|
+
cContext = rb_define_class_under(mDebugger, "Context", rb_cObject);
|
2377
|
+
rb_define_method(cContext, "stop_next=", context_stop_next, -1);
|
2378
|
+
rb_define_method(cContext, "step", context_stop_next, -1);
|
2379
|
+
rb_define_method(cContext, "step_over", context_step_over, -1);
|
2380
|
+
rb_define_method(cContext, "stop_frame=", context_stop_frame, 1);
|
2381
|
+
rb_define_method(cContext, "thread", context_thread, 0);
|
2382
|
+
rb_define_method(cContext, "thnum", context_thnum, 0);
|
2383
|
+
rb_define_method(cContext, "stop_reason", context_stop_reason, 0);
|
2384
|
+
rb_define_method(cContext, "suspend", context_suspend, 0);
|
2385
|
+
rb_define_method(cContext, "suspended?", context_is_suspended, 0);
|
2386
|
+
rb_define_method(cContext, "resume", context_resume, 0);
|
2387
|
+
rb_define_method(cContext, "tracing", context_tracing, 0);
|
2388
|
+
rb_define_method(cContext, "tracing=", context_set_tracing, 1);
|
2389
|
+
rb_define_method(cContext, "ignored?", context_ignored, 0);
|
2390
|
+
rb_define_method(cContext, "frame_args", context_frame_args, -1);
|
2391
|
+
rb_define_method(cContext, "frame_binding", context_frame_binding, -1);
|
2392
|
+
rb_define_method(cContext, "frame_class", context_frame_class, -1);
|
2393
|
+
rb_define_method(cContext, "frame_file", context_frame_file, -1);
|
2394
|
+
rb_define_method(cContext, "frame_id", context_frame_id, -1);
|
2395
|
+
rb_define_method(cContext, "frame_line", context_frame_line, -1);
|
2396
|
+
rb_define_method(cContext, "frame_locals", context_frame_locals, -1);
|
2397
|
+
rb_define_method(cContext, "frame_method", context_frame_id, -1);
|
2398
|
+
rb_define_method(cContext, "frame_self", context_frame_self, -1);
|
2399
|
+
rb_define_method(cContext, "stack_size", context_stack_size, 0);
|
2400
|
+
rb_define_method(cContext, "stack_inc", context_stack_inc, 0);
|
2401
|
+
rb_define_method(cContext, "stack_dec", context_stack_dec, 0);
|
2402
|
+
rb_define_method(cContext, "dead?", context_dead, 0);
|
2403
|
+
rb_define_method(cContext, "breakpoint",
|
2404
|
+
context_breakpoint, 0); /* in breakpoint.c */
|
2405
|
+
rb_define_method(cContext, "set_breakpoint",
|
2406
|
+
context_set_breakpoint, -1); /* in breakpoint.c */
|
2407
|
+
rb_define_method(cContext, "jump", context_jump, 2);
|
2408
|
+
rb_define_method(cContext, "pause", context_pause, 0);
|
2409
|
+
}
|
2410
|
+
|
2411
|
+
/*
|
2412
|
+
* call-seq:
|
2413
|
+
* Debugger.breakpoints -> array
|
2414
|
+
*
|
2415
|
+
* Returns an array of breakpoints.
|
2416
|
+
*/
|
2417
|
+
static VALUE
|
2418
|
+
debug_breakpoints(VALUE self)
|
2419
|
+
{
|
2420
|
+
return rdebug_breakpoints;
|
2421
|
+
}
|
2422
|
+
|
2423
|
+
/*
|
2424
|
+
* call-seq:
|
2425
|
+
* Debugger.add_breakpoint(source, pos, condition = nil) -> breakpoint
|
2426
|
+
*
|
2427
|
+
* Adds a new breakpoint.
|
2428
|
+
* <i>source</i> is a name of a file or a class.
|
2429
|
+
* <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
|
2430
|
+
* <i>condition</i> is a string which is evaluated to +true+ when this breakpoint
|
2431
|
+
* is activated.
|
2432
|
+
*/
|
2433
|
+
static VALUE
|
2434
|
+
debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
|
2435
|
+
{
|
2436
|
+
VALUE result;
|
2437
|
+
|
2438
|
+
result = create_breakpoint_from_args(argc, argv, ++bkp_count);
|
2439
|
+
rb_ary_push(rdebug_breakpoints, result);
|
2440
|
+
return result;
|
2441
|
+
}
|
2442
|
+
|
2443
|
+
VALUE translate_insns(VALUE bin)
|
2444
|
+
{
|
2445
|
+
rb_iseq_t iseq;
|
2446
|
+
iseq.iseq = &bin;
|
2447
|
+
iseq.iseq_size = 1;
|
2448
|
+
iseq.iseq_encoded = NULL;
|
2449
|
+
|
2450
|
+
rb_iseq_translate_threaded_code(&iseq);
|
2451
|
+
if (iseq.iseq_encoded != iseq.iseq)
|
2452
|
+
{
|
2453
|
+
bin = iseq.iseq_encoded[0];
|
2454
|
+
xfree(iseq.iseq_encoded);
|
2455
|
+
}
|
2456
|
+
return(bin);
|
2457
|
+
}
|
2458
|
+
|
2459
|
+
#define TRANSLATE_INSNS(op) bin_##op = translate_insns(BIN(op))
|
2460
|
+
|
2461
|
+
/*
|
2462
|
+
* Document-class: Debugger
|
2463
|
+
*
|
2464
|
+
* == Summary
|
2465
|
+
*
|
2466
|
+
* This is a singleton class allows controlling the debugger. Use it to start/stop debugger,
|
2467
|
+
* set/remove breakpoints, etc.
|
2468
|
+
*/
|
2469
|
+
void
|
2470
|
+
Init_ruby_debug(void)
|
2471
|
+
{
|
2472
|
+
TRANSLATE_INSNS(opt_call_c_function);
|
2473
|
+
TRANSLATE_INSNS(getdynamic);
|
2474
|
+
TRANSLATE_INSNS(throw);
|
2475
|
+
|
2476
|
+
mDebugger = rb_define_module("Debugger");
|
2477
|
+
rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION));
|
2478
|
+
rb_define_module_function(mDebugger, "start_", debug_start, 0);
|
2479
|
+
rb_define_module_function(mDebugger, "stop", debug_stop, 0);
|
2480
|
+
rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
|
2481
|
+
rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
|
2482
|
+
rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1);
|
2483
|
+
rb_define_module_function(mDebugger, "remove_breakpoint",
|
2484
|
+
rdebug_remove_breakpoint,
|
2485
|
+
1); /* in breakpoint.c */
|
2486
|
+
rb_define_module_function(mDebugger, "add_catchpoint",
|
2487
|
+
rdebug_add_catchpoint, 1); /* in breakpoint.c */
|
2488
|
+
rb_define_module_function(mDebugger, "catchpoints",
|
2489
|
+
debug_catchpoints, 0); /* in breakpoint.c */
|
2490
|
+
rb_define_module_function(mDebugger, "last_context", debug_last_interrupted, 0);
|
2491
|
+
rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
|
2492
|
+
rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
|
2493
|
+
rb_define_module_function(mDebugger, "thread_context", debug_thread_context, 1);
|
2494
|
+
rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
|
2495
|
+
rb_define_module_function(mDebugger, "resume", debug_resume, 0);
|
2496
|
+
rb_define_module_function(mDebugger, "tracing", debug_tracing, 0);
|
2497
|
+
rb_define_module_function(mDebugger, "tracing=", debug_set_tracing, 1);
|
2498
|
+
rb_define_module_function(mDebugger, "debug_load", debug_debug_load, -1);
|
2499
|
+
rb_define_module_function(mDebugger, "skip", debug_skip, 0);
|
2500
|
+
rb_define_module_function(mDebugger, "debug_at_exit", debug_at_exit, 0);
|
2501
|
+
rb_define_module_function(mDebugger, "debug", debug_debug, 0);
|
2502
|
+
rb_define_module_function(mDebugger, "debug=", debug_set_debug, 1);
|
2503
|
+
rb_define_module_function(mDebugger, "catchall", debug_catchall, 0);
|
2504
|
+
rb_define_module_function(mDebugger, "catchall=", debug_set_catchall, 1);
|
2505
|
+
rb_define_module_function(mDebugger, "skip_next_exception", debug_skip_next_exception, 0);
|
2506
|
+
|
2507
|
+
cThreadsTable = rb_define_class_under(mDebugger, "ThreadsTable", rb_cObject);
|
2508
|
+
|
2509
|
+
cDebugThread = rb_define_class_under(mDebugger, "DebugThread", rb_cThread);
|
2510
|
+
rb_define_singleton_method(cDebugThread, "inherited",
|
2511
|
+
debug_thread_inherited, 1);
|
2512
|
+
|
2513
|
+
Init_context();
|
2514
|
+
Init_breakpoint();
|
2515
|
+
|
2516
|
+
idAtBreakpoint = rb_intern("at_breakpoint");
|
2517
|
+
idAtCatchpoint = rb_intern("at_catchpoint");
|
2518
|
+
idAtLine = rb_intern("at_line");
|
2519
|
+
idAtReturn = rb_intern("at_return");
|
2520
|
+
idAtTracing = rb_intern("at_tracing");
|
2521
|
+
idList = rb_intern("list");
|
2522
|
+
|
2523
|
+
rb_mObjectSpace = rb_const_get(rb_mKernel, rb_intern("ObjectSpace"));
|
2524
|
+
|
2525
|
+
rb_global_variable(&last_context);
|
2526
|
+
rb_global_variable(&last_thread);
|
2527
|
+
rb_global_variable(&locker);
|
2528
|
+
rb_global_variable(&rdebug_breakpoints);
|
2529
|
+
rb_global_variable(&rdebug_catchpoints);
|
2530
|
+
rb_global_variable(&rdebug_threads_tbl);
|
2531
|
+
|
2532
|
+
/* start the debugger hook */
|
2533
|
+
id_binding_n = rb_intern("binding_n");
|
2534
|
+
id_frame_binding = rb_intern("frame_binding");
|
2535
|
+
hook_off = Qfalse;
|
2536
|
+
locker = Qnil;
|
2537
|
+
rdebug_breakpoints = rb_ary_new();
|
2538
|
+
rdebug_catchpoints = rb_hash_new();
|
2539
|
+
rdebug_threads_tbl = threads_table_create();
|
2540
|
+
rb_add_event_hook(debug_event_hook, RUBY_EVENT_ALL, Qnil);
|
2541
|
+
}
|