ruby-debug 0.1.2

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