debugger 1.0.0.rc2 → 1.0.0

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