ruby-debug-base193 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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(), &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
+ }