ruby-debug 0.1.2

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.
Files changed (9) hide show
  1. data/CHANGES +4 -0
  2. data/LICENSE +23 -0
  3. data/README +63 -0
  4. data/Rakefile +70 -0
  5. data/bin/rdebug +13 -0
  6. data/ext/extconf.rb +18 -0
  7. data/ext/ruby_debug.c +922 -0
  8. data/lib/ruby-debug.rb +610 -0
  9. metadata +53 -0
data/ext/ruby_debug.c ADDED
@@ -0,0 +1,922 @@
1
+ #include <stdio.h>
2
+ #include <ruby.h>
3
+ #include <node.h>
4
+ #include <rubysig.h>
5
+ #include <st.h>
6
+
7
+ #define DEBUG_VERSION "0.1.2"
8
+
9
+ typedef struct {
10
+ int thnum;
11
+ int stop_next;
12
+ int dest_frame;
13
+ int src_line;
14
+ int stop_line;
15
+ int stop_frame;
16
+ int suspend;
17
+ int tracing;
18
+ VALUE frames;
19
+ VALUE thread;
20
+ } debug_context_t;
21
+
22
+ typedef struct {
23
+ VALUE file;
24
+ VALUE line;
25
+ VALUE binding;
26
+ ID id;
27
+ } debug_frame_t;
28
+
29
+ typedef struct {
30
+ VALUE source;
31
+ VALUE pos;
32
+ VALUE disabled;
33
+ } debug_breakpoint_t;
34
+
35
+ static VALUE threads_tbl = Qnil;
36
+ static VALUE breakpoints = Qnil;
37
+ static VALUE catchpoint = Qnil;
38
+ static VALUE waiting = Qnil;
39
+ static VALUE tracing = Qfalse;
40
+
41
+ static VALUE mDebugger;
42
+ static VALUE cContext;
43
+ static VALUE cFrame;
44
+ static VALUE cBreakpoint;
45
+
46
+ static ID idDebugCommand;
47
+ static ID idAtBreakpoint;
48
+ static ID idAtCatchpoint;
49
+ static ID idAtTracing;
50
+ static ID idBinding;
51
+
52
+ static int thnum_max = 0;
53
+ static int last_debugged_thnum = 0;
54
+
55
+ static VALUE debug_suspend(VALUE);
56
+ static VALUE create_binding(VALUE);
57
+ static VALUE debug_stop(VALUE);
58
+
59
+ static VALUE
60
+ debug_is_started(VALUE self)
61
+ {
62
+ return threads_tbl != Qnil ? Qtrue : Qfalse;
63
+ }
64
+
65
+ static VALUE
66
+ debug_check_started()
67
+ {
68
+ if(threads_tbl == Qnil)
69
+ {
70
+ rb_raise(rb_eRuntimeError, "Debugger.start is not called yet.");
71
+ }
72
+ }
73
+
74
+ static void
75
+ debug_context_mark(void* data)
76
+ {
77
+ debug_context_t *debug_context = (debug_context_t *)data;
78
+ rb_gc_mark(debug_context->frames);
79
+ rb_gc_mark(debug_context->thread);
80
+ }
81
+
82
+ static VALUE
83
+ debug_context_create(VALUE thread)
84
+ {
85
+ VALUE result;
86
+ debug_context_t *debug_context;
87
+
88
+ debug_context = ALLOC(debug_context_t);
89
+ debug_context-> thnum = ++thnum_max;
90
+ debug_context->stop_next = -1;
91
+ debug_context->dest_frame = -1;
92
+ debug_context->src_line = -1;
93
+ debug_context->stop_line = -1;
94
+ debug_context->stop_frame = -1;
95
+ debug_context->suspend = 0;
96
+ debug_context->tracing = 0;
97
+ debug_context->frames = rb_ary_new();
98
+ debug_context->thread = thread;
99
+ result = Data_Wrap_Struct(cContext, debug_context_mark, xfree, debug_context);
100
+ return result;
101
+ }
102
+
103
+ static VALUE
104
+ thread_context_lookup(VALUE thread)
105
+ {
106
+ VALUE context;
107
+
108
+ debug_check_started();
109
+
110
+ context = rb_hash_aref(threads_tbl, thread);
111
+ if(context == Qnil)
112
+ {
113
+ context = debug_context_create(thread);
114
+ rb_hash_aset(threads_tbl, thread, context);
115
+ }
116
+ return context;
117
+ }
118
+
119
+ static void
120
+ debug_frame_mark(void *data)
121
+ {
122
+ debug_frame_t *debug_frame = (debug_frame_t *)data;
123
+ rb_gc_mark(debug_frame->binding);
124
+ rb_gc_mark(debug_frame->file);
125
+ rb_gc_mark(debug_frame->line);
126
+ }
127
+
128
+ static VALUE
129
+ debug_frame_create(VALUE file, VALUE line, VALUE binding, ID id)
130
+ {
131
+ VALUE result;
132
+ debug_frame_t *debug_frame;
133
+
134
+ debug_frame = ALLOC(debug_frame_t);
135
+ debug_frame->file = file;
136
+ debug_frame->line = line;
137
+ debug_frame->binding = binding;
138
+ debug_frame->id = id;
139
+ result = Data_Wrap_Struct(cFrame, debug_frame_mark, xfree, debug_frame);
140
+
141
+ return result;
142
+ }
143
+
144
+ static VALUE
145
+ call_debug_command(VALUE context, int thnum, VALUE self, ID mid, VALUE file, VALUE line)
146
+ {
147
+ VALUE id, binding;
148
+
149
+ id = mid ? ID2SYM(mid) : Qnil;
150
+ binding = self? create_binding(self) : Qnil;
151
+
152
+ last_debugged_thnum = thnum;
153
+ debug_suspend(mDebugger);
154
+ return rb_funcall(context, idDebugCommand, 4, file, line, id, binding);
155
+ }
156
+
157
+ static void
158
+ set_frame_source(debug_context_t *debug_context, VALUE file, VALUE line)
159
+ {
160
+ VALUE frame;
161
+ debug_frame_t *top_frame;
162
+
163
+ if(RARRAY(debug_context->frames)->len > 0)
164
+ {
165
+ frame = *RARRAY(debug_context->frames)->ptr;
166
+ Data_Get_Struct(frame, debug_frame_t, top_frame);
167
+ top_frame->file = file;
168
+ top_frame->line = line;
169
+ }
170
+ }
171
+
172
+ static void
173
+ save_call_frame(VALUE self, VALUE file, VALUE line, ID mid, debug_context_t *debug_context)
174
+ {
175
+ VALUE frame, binding;
176
+
177
+ binding = self? create_binding(self) : Qnil;
178
+ frame = debug_frame_create(file, line, binding, mid);
179
+ rb_ary_unshift(debug_context->frames, frame);
180
+ }
181
+
182
+ static int
183
+ check_breakpoints(VALUE context, VALUE file, VALUE klass, VALUE pos, ID id)
184
+ {
185
+ VALUE breakpoint;
186
+ debug_breakpoint_t *debug_breakpoint;
187
+ int i;
188
+ int result = 0;
189
+
190
+ if(RARRAY(breakpoints)->len == 0)
191
+ return 0;
192
+ for(i = 0; i < RARRAY(breakpoints)->len; i++)
193
+ {
194
+ breakpoint = rb_ary_entry(breakpoints, i);
195
+ Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
196
+ if(debug_breakpoint->pos != pos && !(TYPE(pos) == T_STRING &&
197
+ TYPE(debug_breakpoint->pos) == T_STRING && rb_str_cmp(debug_breakpoint->pos, pos) == 0))
198
+ continue;
199
+ if(TYPE(debug_breakpoint->source) == T_STRING &&
200
+ rb_str_cmp(debug_breakpoint->source, file) == 0 ||
201
+ klass != Qnil && debug_breakpoint->source == klass)
202
+ {
203
+ result = 1;
204
+ rb_funcall(context, idAtBreakpoint, 2, breakpoint, id ? rb_str_new2(rb_id2name(id)) : Qnil);
205
+ continue;
206
+ }
207
+
208
+ }
209
+ return result;
210
+ }
211
+
212
+ /*
213
+ * This is a NASTY HACK. For some reasons rb_f_binding is decalred
214
+ * static in eval.c
215
+ */
216
+ static VALUE
217
+ create_binding(VALUE self)
218
+ {
219
+ typedef VALUE (*bind_func_t)(VALUE);
220
+ static bind_func_t f_binding = NULL;
221
+
222
+ if(f_binding == NULL)
223
+ {
224
+ ID idBinding = rb_intern("binding");
225
+ NODE *body, *method;
226
+ st_lookup(RCLASS(rb_mKernel)->m_tbl, idBinding, (st_data_t *)&body);
227
+ method = (NODE *)body->u2.value;
228
+ f_binding = (bind_func_t)method->u1.value;
229
+ }
230
+ return f_binding(self);
231
+ }
232
+
233
+ static void
234
+ check_suspend(debug_context_t *debug_context)
235
+ {
236
+ if(rb_thread_critical == Qtrue)
237
+ return;
238
+ while(1)
239
+ {
240
+ rb_thread_critical = Qtrue;
241
+ if(!debug_context->suspend)
242
+ break;
243
+ rb_ary_push(waiting, rb_thread_current());
244
+ debug_context->suspend = 0;
245
+ rb_thread_stop();
246
+ }
247
+ rb_thread_critical = Qfalse;
248
+ }
249
+
250
+ static void
251
+ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
252
+ {
253
+ VALUE thread;
254
+ VALUE context;
255
+ debug_context_t *debug_context;
256
+ VALUE file = Qnil, line = Qnil;
257
+
258
+ static int debugging = 0;
259
+
260
+ if(debugging) return;
261
+
262
+ debugging++;
263
+
264
+ thread = rb_thread_current();
265
+ context = thread_context_lookup(thread);
266
+ Data_Get_Struct(context, debug_context_t, debug_context);
267
+ check_suspend(debug_context);
268
+
269
+ if(node)
270
+ {
271
+ file = rb_str_new2(node->nd_file);
272
+ line = INT2FIX(nd_line(node));
273
+ }
274
+
275
+ switch(event)
276
+ {
277
+ case RUBY_EVENT_LINE:
278
+ {
279
+ set_frame_source(debug_context, file, line);
280
+
281
+ if(RTEST(tracing) || debug_context->tracing )
282
+ {
283
+ rb_funcall(context, idAtTracing, 2, file, line);
284
+ }
285
+
286
+ if(debug_context->dest_frame == -1 ||
287
+ RARRAY(debug_context->frames)->len == debug_context->dest_frame)
288
+ {
289
+ debug_context->stop_next--;
290
+ if(debug_context->stop_next < 0)
291
+ debug_context->stop_next = -1;
292
+ /* we check that we actualy moved to another line */
293
+ if(line != Qnil && debug_context->src_line != FIX2INT(line))
294
+ {
295
+ debug_context->stop_line--;
296
+ }
297
+ }
298
+ else if(RARRAY(debug_context->frames)->len < debug_context->dest_frame)
299
+ {
300
+ debug_context->stop_next = 0;
301
+ }
302
+
303
+ if(RARRAY(debug_context->frames)->len == 0)
304
+ {
305
+ save_call_frame(self, file, line, mid, debug_context);
306
+ }
307
+
308
+ if(debug_context->stop_next == 0 || debug_context->stop_line == 0 ||
309
+ check_breakpoints(context, file, klass, line, mid))
310
+ {
311
+ /* reset all pointers */
312
+ debug_context->dest_frame = -1;
313
+ debug_context->src_line = -1;
314
+ debug_context->stop_line = -1;
315
+ debug_context->stop_next = -1;
316
+
317
+ call_debug_command(context, debug_context->thnum, self, mid, file, line);
318
+ }
319
+ break;
320
+ }
321
+ case RUBY_EVENT_C_CALL:
322
+ {
323
+ if(node)
324
+ {
325
+ set_frame_source(debug_context, file, line);
326
+ }
327
+ break;
328
+ }
329
+ case RUBY_EVENT_CALL:
330
+ {
331
+ save_call_frame(self, file, line, mid, debug_context);
332
+ if(check_breakpoints(context, file, klass, rb_str_new2(rb_id2name(mid)), mid))
333
+ {
334
+ call_debug_command(context, debug_context->thnum, self, mid, file, line);
335
+ }
336
+ break;
337
+ }
338
+ case RUBY_EVENT_RETURN:
339
+ case RUBY_EVENT_END:
340
+ {
341
+ if(RARRAY(debug_context->frames)->len == debug_context->stop_frame)
342
+ {
343
+ debug_context->stop_next = 1;
344
+ debug_context->stop_frame = 0;
345
+ }
346
+ rb_ary_shift(debug_context->frames);
347
+ break;
348
+ }
349
+ case RUBY_EVENT_CLASS:
350
+ {
351
+ save_call_frame(self, file, line, mid, debug_context);
352
+ break;
353
+ }
354
+ case RUBY_EVENT_RAISE:
355
+ {
356
+ VALUE ancestors;
357
+ VALUE expn_class, aclass;
358
+ int i, found = 0;
359
+
360
+ expn_class = rb_obj_class(ruby_errinfo);
361
+ if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) )
362
+ {
363
+ debug_stop(mDebugger);
364
+ rb_exit(0);
365
+ }
366
+
367
+ if(catchpoint == Qnil)
368
+ break;
369
+
370
+ ancestors = rb_mod_ancestors(expn_class);
371
+ for(i = 0; i < RARRAY(ancestors)->len; i++)
372
+ {
373
+ aclass = rb_ary_entry(ancestors, i);
374
+ if(rb_str_cmp(rb_mod_name(aclass), catchpoint) == 0)
375
+ {
376
+ rb_funcall(context, idAtCatchpoint, 0);
377
+ call_debug_command(context, debug_context->thnum, self, mid, file, line);
378
+ break;
379
+ }
380
+ }
381
+
382
+ break;
383
+ }
384
+ case RUBY_EVENT_C_RETURN:
385
+ {
386
+ break;
387
+ }
388
+ }
389
+
390
+ debugging--;
391
+ }
392
+
393
+ static VALUE
394
+ debug_thnum(VALUE self)
395
+ {
396
+ VALUE thread, context;
397
+ debug_context_t *debug_context;
398
+
399
+ thread = rb_thread_current();
400
+ context = thread_context_lookup(thread);
401
+ Data_Get_Struct(context, debug_context_t, debug_context);
402
+ return INT2FIX(debug_context->thnum);
403
+ }
404
+
405
+ static VALUE
406
+ debug_start(VALUE self)
407
+ {
408
+ threads_tbl = rb_hash_new();
409
+ breakpoints = rb_ary_new();
410
+ waiting = rb_ary_new();
411
+
412
+ rb_add_event_hook(debug_event_hook,
413
+ RUBY_EVENT_LINE | RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN |
414
+ RUBY_EVENT_CALL | RUBY_EVENT_RETURN | RUBY_EVENT_CLASS |
415
+ RUBY_EVENT_END | RUBY_EVENT_RAISE
416
+ );
417
+ return Qnil;
418
+ }
419
+
420
+ static VALUE
421
+ debug_stop(VALUE self)
422
+ {
423
+ debug_check_started();
424
+ rb_remove_event_hook(debug_event_hook);
425
+
426
+ waiting = Qnil;
427
+ breakpoints = Qnil;
428
+ threads_tbl = Qnil;
429
+
430
+ return Qnil;
431
+ }
432
+
433
+ static void
434
+ breakpoint_mark(void *data)
435
+ {
436
+ debug_breakpoint_t *breakpoint;
437
+ breakpoint = (debug_breakpoint_t *)data;
438
+ rb_gc_mark(breakpoint->source);
439
+ rb_gc_mark(breakpoint->pos);
440
+ }
441
+
442
+ static VALUE
443
+ debug_add_breakpoint(VALUE self, VALUE source, VALUE pos)
444
+ {
445
+ VALUE result;
446
+ debug_breakpoint_t *breakpoint;
447
+
448
+ debug_check_started();
449
+
450
+ breakpoint = ALLOC(debug_breakpoint_t);
451
+ breakpoint->source = source;
452
+ breakpoint->pos = pos;
453
+ breakpoint->disabled = Qfalse;
454
+ result = Data_Wrap_Struct(cBreakpoint, breakpoint_mark, xfree, breakpoint);
455
+ rb_ary_push(breakpoints, result);
456
+ return result;
457
+ }
458
+
459
+ static VALUE
460
+ debug_breakpoints(VALUE self)
461
+ {
462
+ debug_check_started();
463
+
464
+ return breakpoints;
465
+ }
466
+
467
+ static VALUE
468
+ debug_catchpoint(VALUE self)
469
+ {
470
+ debug_check_started();
471
+
472
+ return catchpoint;
473
+ }
474
+
475
+ static VALUE
476
+ debug_set_catchpoint(VALUE self, VALUE value)
477
+ {
478
+ debug_check_started();
479
+
480
+ if (!NIL_P(value) && TYPE(value) != T_STRING) {
481
+ rb_raise(rb_eTypeError, "value of checkpoint must be String");
482
+ }
483
+ if(NIL_P(value))
484
+ catchpoint = Qnil;
485
+ else
486
+ catchpoint = rb_str_dup(value);
487
+ return value;
488
+ }
489
+
490
+ static VALUE
491
+ debug_interrupt(VALUE self)
492
+ {
493
+ VALUE thread, context;
494
+ debug_context_t *debug_context;
495
+
496
+ debug_check_started();
497
+
498
+ thread = rb_thread_current();
499
+ context = thread_context_lookup(thread);
500
+ Data_Get_Struct(context, debug_context_t, debug_context);
501
+ debug_context->stop_next = 1;
502
+
503
+ return Qnil;
504
+ }
505
+
506
+ static int
507
+ find_last_context_func(VALUE key, VALUE value, VALUE *result)
508
+ {
509
+ debug_context_t *debug_context;
510
+ Data_Get_Struct(value, debug_context_t, debug_context);
511
+ if(debug_context->thnum == last_debugged_thnum)
512
+ {
513
+ *result = value;
514
+ return ST_STOP;
515
+ }
516
+ return ST_CONTINUE;
517
+ }
518
+
519
+ static VALUE
520
+ find_last_context()
521
+ {
522
+ VALUE result = Qnil;
523
+ rb_hash_foreach(threads_tbl, find_last_context_func, (st_data_t)&result);
524
+ return result;
525
+ }
526
+
527
+ static VALUE
528
+ debug_interrupt_last(VALUE self)
529
+ {
530
+ VALUE thread, context = Qnil;
531
+ debug_context_t *debug_context;
532
+
533
+ debug_check_started();
534
+
535
+ context = find_last_context();
536
+ if(context != Qnil)
537
+ {
538
+ Data_Get_Struct(context, debug_context_t, debug_context);
539
+ debug_context->stop_next = 1;
540
+ }
541
+
542
+ return Qnil;
543
+ }
544
+
545
+ static VALUE
546
+ debug_current_context(VALUE self)
547
+ {
548
+ VALUE thread, context;
549
+
550
+ debug_check_started();
551
+
552
+ thread = rb_thread_current();
553
+ context = thread_context_lookup(thread);
554
+
555
+ return context;
556
+ }
557
+
558
+ static VALUE
559
+ debug_contexts(VALUE self)
560
+ {
561
+ volatile VALUE list;
562
+ volatile VALUE new_list;
563
+ VALUE thread, context;
564
+ debug_context_t *debug_context;
565
+ int i;
566
+
567
+ debug_check_started();
568
+
569
+ new_list = rb_ary_new();
570
+ list = rb_funcall(rb_cThread, rb_intern("list"), 0);
571
+ for(i = 0; i < RARRAY(list)->len; i++)
572
+ {
573
+ thread = rb_ary_entry(list, i);
574
+ context = thread_context_lookup(thread);
575
+ rb_ary_push(new_list, context);
576
+ }
577
+ /*
578
+ * I wonder why rb_hash_clear is declared static?
579
+ */
580
+ rb_funcall(threads_tbl, rb_intern("clear"), 0);
581
+ for(i = 0; i < RARRAY(new_list)->len; i++)
582
+ {
583
+ context = rb_ary_entry(new_list, i);
584
+ Data_Get_Struct(context, debug_context_t, debug_context);
585
+ rb_hash_aset(threads_tbl, debug_context->thread, context);
586
+ }
587
+
588
+ return new_list;
589
+ }
590
+
591
+ static VALUE
592
+ debug_suspend(VALUE self)
593
+ {
594
+ VALUE current, context;
595
+ VALUE saved_crit;
596
+ VALUE context_list;
597
+ debug_context_t *debug_context;
598
+ int i;
599
+
600
+ debug_check_started();
601
+
602
+ saved_crit = rb_thread_critical;
603
+ rb_thread_critical = Qtrue;
604
+ context_list = debug_contexts(self);
605
+ current = thread_context_lookup(rb_thread_current());
606
+
607
+ for(i = 0; i < RARRAY(context_list)->len; i++)
608
+ {
609
+ context = rb_ary_entry(context_list, i);
610
+ if(current == context)
611
+ continue;
612
+ Data_Get_Struct(context, debug_context_t, debug_context);
613
+ debug_context->suspend = 1;
614
+ }
615
+ rb_thread_critical = saved_crit;
616
+
617
+ if(rb_thread_critical == Qfalse)
618
+ rb_thread_schedule();
619
+
620
+ return context;
621
+ }
622
+
623
+ static VALUE
624
+ debug_resume(VALUE self)
625
+ {
626
+ VALUE current, context;
627
+ VALUE saved_crit;
628
+ VALUE thread;
629
+ VALUE context_list;
630
+ debug_context_t *debug_context;
631
+ int i;
632
+
633
+ debug_check_started();
634
+
635
+ saved_crit = rb_thread_critical;
636
+ rb_thread_critical = Qtrue;
637
+ context_list = debug_contexts(self);
638
+
639
+ current = thread_context_lookup(rb_thread_current());
640
+ for(i = 0; i < RARRAY(context_list)->len; i++)
641
+ {
642
+ context = rb_ary_entry(context_list, i);
643
+ if(current == context)
644
+ continue;
645
+ Data_Get_Struct(context, debug_context_t, debug_context);
646
+ debug_context->suspend = 0;
647
+ }
648
+ for(i = 0; i < RARRAY(waiting)->len; i++)
649
+ {
650
+ thread = rb_ary_entry(waiting, i);
651
+ rb_thread_run(thread);
652
+ }
653
+ rb_ary_clear(waiting);
654
+ rb_thread_critical = saved_crit;
655
+
656
+ rb_thread_schedule();
657
+
658
+ return context;
659
+ }
660
+
661
+ static VALUE
662
+ debug_tracing(VALUE self)
663
+ {
664
+ return tracing;
665
+ }
666
+
667
+ static VALUE
668
+ debug_set_tracing(VALUE self, VALUE value)
669
+ {
670
+ tracing = RTEST(value) ? Qtrue : Qfalse;
671
+ return value;
672
+ }
673
+
674
+ static VALUE
675
+ context_stop_next(VALUE self, VALUE steps)
676
+ {
677
+ debug_context_t *debug_context;
678
+
679
+ debug_check_started();
680
+
681
+ Data_Get_Struct(self, debug_context_t, debug_context);
682
+ if(FIX2INT(steps) < 0)
683
+ rb_raise(rb_eRuntimeError, "Steps argument can't be negative.");
684
+ debug_context->stop_next = FIX2INT(steps);
685
+
686
+ return steps;
687
+ }
688
+
689
+ static VALUE
690
+ context_step_over(int argc, VALUE *argv, VALUE self)
691
+ {
692
+ VALUE lines, frame, cur_frame;
693
+ debug_context_t *debug_context;
694
+ debug_frame_t *debug_frame;
695
+
696
+ debug_check_started();
697
+
698
+ Data_Get_Struct(self, debug_context_t, debug_context);
699
+ if(RARRAY(debug_context->frames)->len == 0)
700
+ rb_raise(rb_eRuntimeError, "No frames collected.");
701
+
702
+ rb_scan_args(argc, argv, "11", &lines, &frame);
703
+ debug_context->stop_line = FIX2INT(lines);
704
+ if(argc == 1)
705
+ {
706
+ debug_context->dest_frame = RARRAY(debug_context->frames)->len;
707
+ }
708
+ else
709
+ {
710
+ if(FIX2INT(frame) < 0 && FIX2INT(frame) >= RARRAY(debug_context->frames)->len)
711
+ rb_raise(rb_eRuntimeError, "Destination frame is out of range.");
712
+ debug_context->dest_frame = FIX2INT(frame);
713
+ }
714
+
715
+ cur_frame = *RARRAY(debug_context->frames)->ptr;
716
+ Data_Get_Struct(cur_frame, debug_frame_t, debug_frame);
717
+ debug_context->src_line = FIX2INT(debug_frame->line);
718
+
719
+ return Qnil;
720
+ }
721
+
722
+ static VALUE
723
+ context_stop_frame(VALUE self, VALUE frame)
724
+ {
725
+ debug_context_t *debug_context;
726
+
727
+ debug_check_started();
728
+
729
+ Data_Get_Struct(self, debug_context_t, debug_context);
730
+ if(FIX2INT(frame) < 0 && FIX2INT(frame) >= RARRAY(debug_context->frames)->len)
731
+ rb_raise(rb_eRuntimeError, "Stop frame is out of range.");
732
+ debug_context->stop_frame = FIX2INT(frame);
733
+
734
+ return frame;
735
+ }
736
+
737
+ static VALUE
738
+ context_frames(VALUE self)
739
+ {
740
+ debug_context_t *debug_context;
741
+
742
+ Data_Get_Struct(self, debug_context_t, debug_context);
743
+ return debug_context->frames;
744
+ }
745
+
746
+ static VALUE
747
+ context_thread(VALUE self)
748
+ {
749
+ debug_context_t *debug_context;
750
+
751
+ Data_Get_Struct(self, debug_context_t, debug_context);
752
+ return debug_context->thread;
753
+ }
754
+
755
+ static VALUE
756
+ context_thnum(VALUE self)
757
+ {
758
+ debug_context_t *debug_context;
759
+
760
+ Data_Get_Struct(self, debug_context_t, debug_context);
761
+ return INT2FIX(debug_context->thnum);
762
+ }
763
+
764
+ static VALUE
765
+ context_set_suspend(VALUE self)
766
+ {
767
+ debug_context_t *debug_context;
768
+
769
+ debug_check_started();
770
+
771
+ Data_Get_Struct(self, debug_context_t, debug_context);
772
+ debug_context->suspend = 1;
773
+ return Qnil;
774
+ }
775
+
776
+ static VALUE
777
+ context_clear_suspend(VALUE self)
778
+ {
779
+ debug_context_t *debug_context;
780
+
781
+ debug_check_started();
782
+
783
+ Data_Get_Struct(self, debug_context_t, debug_context);
784
+ debug_context->suspend = 0;
785
+ return Qnil;
786
+ }
787
+
788
+ static VALUE
789
+ context_tracing(VALUE self)
790
+ {
791
+ debug_context_t *debug_context;
792
+
793
+ debug_check_started();
794
+
795
+ Data_Get_Struct(self, debug_context_t, debug_context);
796
+ return debug_context->tracing ? Qtrue : Qfalse;
797
+ }
798
+
799
+ static VALUE
800
+ context_set_tracing(VALUE self, VALUE value)
801
+ {
802
+ debug_context_t *debug_context;
803
+
804
+ debug_check_started();
805
+
806
+ Data_Get_Struct(self, debug_context_t, debug_context);
807
+ debug_context->tracing = RTEST(value) ? 1 : 0;
808
+ return value;
809
+ }
810
+
811
+ static VALUE
812
+ frame_file(VALUE self)
813
+ {
814
+ debug_frame_t *debug_frame;
815
+
816
+ Data_Get_Struct(self, debug_frame_t, debug_frame);
817
+ return debug_frame->file;
818
+ }
819
+
820
+ static VALUE
821
+ frame_line(VALUE self)
822
+ {
823
+ debug_frame_t *debug_frame;
824
+
825
+ Data_Get_Struct(self, debug_frame_t, debug_frame);
826
+ return debug_frame->line;
827
+ }
828
+
829
+ static VALUE
830
+ frame_binding(VALUE self)
831
+ {
832
+ debug_frame_t *debug_frame;
833
+
834
+ Data_Get_Struct(self, debug_frame_t, debug_frame);
835
+ return debug_frame->binding;
836
+ }
837
+
838
+ static VALUE
839
+ frame_id(VALUE self)
840
+ {
841
+ debug_frame_t *debug_frame;
842
+
843
+ Data_Get_Struct(self, debug_frame_t, debug_frame);
844
+ return debug_frame->id ? ID2SYM(debug_frame->id): Qnil;
845
+ }
846
+
847
+ static VALUE
848
+ breakpoint_source(VALUE self)
849
+ {
850
+ debug_breakpoint_t *breakpoint;
851
+
852
+ Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
853
+ return breakpoint->source;
854
+ }
855
+
856
+ static VALUE
857
+ breakpoint_pos(VALUE self)
858
+ {
859
+ debug_breakpoint_t *breakpoint;
860
+
861
+ Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
862
+ return breakpoint->pos;
863
+ }
864
+
865
+ #if defined(_WIN32)
866
+ __declspec(dllexport)
867
+ #endif
868
+ void
869
+ Init_ruby_debug()
870
+ {
871
+ mDebugger = rb_define_module("Debugger");
872
+ rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION));
873
+ rb_define_module_function(mDebugger, "start", debug_start, 0);
874
+ rb_define_module_function(mDebugger, "stop", debug_stop, 0);
875
+ rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
876
+ rb_define_module_function(mDebugger, "thnum", debug_thnum, 0);
877
+ rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
878
+ rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, 2);
879
+ rb_define_module_function(mDebugger, "catchpoint", debug_catchpoint, 0);
880
+ rb_define_module_function(mDebugger, "catchpoint=", debug_set_catchpoint, 1);
881
+ rb_define_module_function(mDebugger, "interrupt", debug_interrupt, 0);
882
+ rb_define_module_function(mDebugger, "interrupt_last", debug_interrupt_last, 0);
883
+ rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
884
+ rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
885
+ rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
886
+ rb_define_module_function(mDebugger, "resume", debug_resume, 0);
887
+ rb_define_module_function(mDebugger, "tracing", debug_tracing, 0);
888
+ rb_define_module_function(mDebugger, "tracing=", debug_set_tracing, 1);
889
+
890
+ cContext = rb_define_class_under(mDebugger, "Context", rb_cObject);
891
+ rb_define_method(cContext, "stop_next=", context_stop_next, 1);
892
+ rb_define_method(cContext, "step_over", context_step_over, -1);
893
+ rb_define_method(cContext, "stop_frame=", context_stop_frame, 1);
894
+ rb_define_method(cContext, "frames", context_frames, 0);
895
+ rb_define_method(cContext, "thread", context_thread, 0);
896
+ rb_define_method(cContext, "thnum", context_thnum, 0);
897
+ rb_define_method(cContext, "set_suspend", context_set_suspend, 0);
898
+ rb_define_method(cContext, "clear_suspend", context_clear_suspend, 0);
899
+ rb_define_method(cContext, "tracing", context_tracing, 0);
900
+ rb_define_method(cContext, "tracing=", context_set_tracing, 1);
901
+
902
+ cFrame = rb_define_class_under(cContext, "Frame", rb_cObject);
903
+ rb_define_method(cFrame, "file", frame_file, 0);
904
+ rb_define_method(cFrame, "line", frame_line, 0);
905
+ rb_define_method(cFrame, "binding", frame_binding, 0);
906
+ rb_define_method(cFrame, "id", frame_id, 0);
907
+
908
+ cBreakpoint = rb_define_class_under(mDebugger, "Breakpoint", rb_cObject);
909
+ rb_define_method(cBreakpoint, "source", breakpoint_source, 0);
910
+ rb_define_method(cBreakpoint, "pos", breakpoint_pos, 0);
911
+
912
+ idDebugCommand = rb_intern("debug_command");
913
+ idAtBreakpoint = rb_intern("at_breakpoint");
914
+ idAtCatchpoint = rb_intern("at_catchpoint");
915
+ idAtTracing = rb_intern("at_tracing");
916
+ idBinding = rb_intern("binding");
917
+
918
+ rb_global_variable(&threads_tbl);
919
+ rb_global_variable(&breakpoints);
920
+ rb_global_variable(&catchpoint);
921
+ rb_global_variable(&waiting);
922
+ }