ruby-debug-base19x 0.11.25.jb3

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