tddium-ruby-debug-base19 0.12.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.
@@ -0,0 +1,41 @@
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
+ $INCFLAGS << " -I#{ENV['MY_RUBY_HOME']}/include/ruby-1.9.1/ruby-#{RUBY_VERSION}-p#{RUBY_PATCHLEVEL}"
10
+
11
+ hdrs = proc {
12
+ iseqs = %w[vm_core.h iseq.h]
13
+ begin
14
+ have_struct_member("rb_method_entry_t", "called_id", "method.h") or
15
+ have_struct_member("rb_control_frame_t", "method_id", "method.h")
16
+ end and
17
+ have_header("vm_core.h") and have_header("iseq.h") and have_header("insns.inc") and
18
+ have_header("insns_info.inc") and have_header("eval_intern.h") or break
19
+ have_type("struct iseq_line_info_entry", iseqs) or
20
+ have_type("struct iseq_insn_info_entry", iseqs) or
21
+ break
22
+ if checking_for(checking_message("if rb_iseq_compile_with_option was added an argument filepath")) do
23
+ try_compile(<<SRC)
24
+ #include <ruby.h>
25
+ #include "vm_core.h"
26
+ extern VALUE rb_iseq_new_main(NODE *node, VALUE filename, VALUE filepath);
27
+ SRC
28
+ end
29
+ $defs << '-DRB_ISEQ_COMPILE_5ARGS'
30
+ end
31
+ }
32
+
33
+ dir_config("ruby")
34
+ if !Ruby_core_source::create_makefile_with_core(hdrs, "ruby_debug")
35
+ STDERR.print("Makefile creation failed\n")
36
+ STDERR.print("*************************************************************\n\n")
37
+ STDERR.print(" NOTE: For Ruby 1.9 installation instructions, please see:\n\n")
38
+ STDERR.print(" http://wiki.github.com/mark-moseley/ruby-debug\n\n")
39
+ STDERR.print("*************************************************************\n\n")
40
+ exit(1)
41
+ 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(), &current, 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(), &current, 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
+ }