ruby-debug 0.5.3 → 0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES CHANGED
@@ -1,3 +1,11 @@
1
+ 0.6
2
+ - Added option to exclude collecting of frame bindings.
3
+ - Several performance optimizations.
4
+
5
+ 0.5.4
6
+ - Added -x/--trace option to rdebug script. Patch from R. Bernstein.
7
+ - Removed a live thread reference from the context's structure avoiding memory leakage.
8
+
1
9
  0.5.3
2
10
  - Added Module#post_mortem_method method, which wraps any method with Debugger.post_mortem block.
3
11
  - Added breakpoint id, which is not dependent on the breakpoint position in Debugger.breakpoints array.
data/bin/rdebug CHANGED
@@ -14,7 +14,9 @@ options = OpenStruct.new(
14
14
  'wait' => false,
15
15
  'nostop' => false,
16
16
  'post_mortem' => false,
17
- 'script' => nil
17
+ 'script' => nil,
18
+ 'tracing' => false,
19
+ 'frame_info' => false
18
20
  )
19
21
 
20
22
  opts = OptionParser.new do |opts|
@@ -25,11 +27,13 @@ EOB
25
27
  opts.separator ""
26
28
  opts.separator "Options:"
27
29
  opts.on("-s", "--server", "Listen for remote connections") {options.server = true}
30
+ opts.on("-c", "--client", "Connect to remote debugger") {options.client = true}
31
+ opts.on("-h", "--host HOST", "Host name used for remote debugging") {|options.host|}
28
32
  opts.on("-w", "--wait", "Wait for a client connection, implies -s option") {options.wait = true}
33
+ opts.on("-x", "--trace", "turn on line tracing") {options.tracing = true}
29
34
  opts.on("-n", "--nostop", "Do not stop when a client connects, implies -s option") {options.nostop = true}
35
+ opts.on("-f", "--keep-frame-state", "Keep frame state") {options.frame_info = true}
30
36
  opts.on("-m", "--post-mortem", "Activate post-mortem mode") {options.post_mortem = true}
31
- opts.on("-c", "--client", "Connect to remote debugger") {options.client = true}
32
- opts.on("-h", "--host HOST", "Host name used for remote debugging") {|options.host|}
33
37
  opts.on("-p", "--port PORT", Integer, "Port used for remote debugging") {|options.port|}
34
38
  opts.on("-I", "--include PATH", String, "Add PATH to $LOAD_PATH") do |path|
35
39
  $LOAD_PATH.unshift(path)
@@ -82,6 +86,7 @@ else
82
86
  trap('INT') { Debugger.interrupt_last }
83
87
  Debugger.stop_on_connect = !options.nostop
84
88
  Debugger.wait_connection = options.wait
89
+ Debugger.keep_frame_info = options.frame_info
85
90
  load "#{ENV["HOME"]}/.rdebugrc" if File.exists?("#{ENV["HOME"]}/.rdebugrc")
86
91
  if options.server
87
92
  Debugger.start_remote(options.host, [options.port, options.cport], options.post_mortem)
@@ -92,7 +97,11 @@ else
92
97
  Debugger.run_script(options.script)
93
98
  end
94
99
  Debugger.post_mortem if options.post_mortem
95
- debugger 2
100
+ if options.tracing
101
+ Debugger.tracing = true
102
+ else
103
+ debugger 2
104
+ end
96
105
  Debugger.debug_load Debugger::PROG_SCRIPT
97
106
  end
98
107
  end
@@ -4,7 +4,7 @@
4
4
  #include <rubysig.h>
5
5
  #include <st.h>
6
6
 
7
- #define DEBUG_VERSION "0.5.3"
7
+ #define DEBUG_VERSION "0.6"
8
8
 
9
9
  #define CTX_FL_MOVED (1<<1)
10
10
  #define CTX_FL_SUSPEND (1<<2)
@@ -17,11 +17,15 @@
17
17
  #define CTX_FL_UNSET(c,f) do { (c)->flags &= ~(f); } while (0)
18
18
 
19
19
  #define DID_MOVED (debug_context->last_line != line || \
20
- debug_context->last_file == Qnil || \
21
- rb_str_cmp(debug_context->last_file, file) != 0)
20
+ debug_context->last_file == NULL || \
21
+ strcmp(debug_context->last_file, file) != 0)
22
22
 
23
23
  #define IS_STARTED (threads_tbl != Qnil)
24
24
 
25
+ #ifndef min
26
+ #define min(x,y) ((x) < (y) ? (x) : (y))
27
+ #endif
28
+
25
29
  typedef struct {
26
30
  int thnum;
27
31
  int flags;
@@ -29,23 +33,30 @@ typedef struct {
29
33
  int dest_frame;
30
34
  int stop_line;
31
35
  int stop_frame;
36
+ unsigned long thread_id;
32
37
  VALUE frames;
33
- VALUE thread;
34
- VALUE last_file;
35
- VALUE last_line;
38
+ const char * last_file;
39
+ int last_line;
36
40
  } debug_context_t;
37
41
 
38
42
  typedef struct {
39
- VALUE file;
40
- VALUE line;
41
- VALUE binding;
42
43
  ID id;
44
+ VALUE binding;
45
+ int line;
46
+ const char * file;
43
47
  } debug_frame_t;
44
48
 
49
+ enum bp_type {BP_POS_TYPE, BP_METHOD_TYPE};
50
+
45
51
  typedef struct {
46
52
  int id;
53
+ int type;
47
54
  VALUE source;
48
- VALUE pos;
55
+ union
56
+ {
57
+ int line;
58
+ ID mid;
59
+ } pos;
49
60
  VALUE expr;
50
61
  } debug_breakpoint_t;
51
62
 
@@ -53,12 +64,13 @@ typedef struct {
53
64
  st_table *tbl;
54
65
  } threads_table_t;
55
66
 
56
- static VALUE threads_tbl = Qnil;
57
- static VALUE breakpoints = Qnil;
58
- static VALUE catchpoint = Qnil;
59
- static VALUE tracing = Qfalse;
60
- static VALUE locker = Qnil;
61
- static VALUE post_mortem = Qfalse;
67
+ static VALUE threads_tbl = Qnil;
68
+ static VALUE breakpoints = Qnil;
69
+ static VALUE catchpoint = Qnil;
70
+ static VALUE tracing = Qfalse;
71
+ static VALUE locker = Qnil;
72
+ static VALUE post_mortem = Qfalse;
73
+ static VALUE keep_frame_info = Qfalse;
62
74
 
63
75
  static VALUE mDebugger;
64
76
  static VALUE cThreadsTable;
@@ -66,15 +78,12 @@ static VALUE cContext;
66
78
  static VALUE cFrame;
67
79
  static VALUE cBreakpoint;
68
80
 
69
- static VALUE file_separator;
70
- static VALUE alt_file_separator;
81
+ static VALUE rb_mObjectSpace;
71
82
 
72
83
  static ID idAtLine;
73
84
  static ID idAtBreakpoint;
74
85
  static ID idAtCatchpoint;
75
86
  static ID idAtTracing;
76
- static ID idBinding;
77
- static ID idBasename;
78
87
  static ID idEval;
79
88
  static ID idList;
80
89
  static ID idClear;
@@ -84,20 +93,67 @@ static int start_count = 0;
84
93
  static int thnum_max = 0;
85
94
  static int bkp_count = 0;
86
95
  static int last_debugged_thnum = -1;
96
+ static unsigned long last_check = 0;
97
+ static unsigned long hook_count = 0;
87
98
 
88
99
  static VALUE create_binding(VALUE);
89
100
  static VALUE debug_stop(VALUE);
90
101
 
91
102
  typedef struct locked_thread_t {
92
- VALUE thread;
103
+ unsigned long thread_id;
93
104
  struct locked_thread_t *next;
94
105
  } locked_thread_t;
95
106
 
96
107
  static locked_thread_t *locked_head = NULL;
97
108
  static locked_thread_t *locked_tail = NULL;
98
109
 
110
+ inline static void *
111
+ ruby_method_ptr(VALUE class, ID meth_id)
112
+ {
113
+ NODE *body, *method;
114
+ st_lookup(RCLASS(class)->m_tbl, meth_id, (st_data_t *)&body);
115
+ method = (NODE *)body->u2.value;
116
+ return (void *)method->u1.value;
117
+ }
118
+
119
+ inline static unsigned long
120
+ ref2id(VALUE obj)
121
+ {
122
+ return NUM2ULONG(rb_obj_id(obj));
123
+ }
124
+
125
+ static VALUE
126
+ id2ref_unprotected(VALUE id)
127
+ {
128
+ typedef VALUE (*id2ref_func_t)(VALUE, VALUE);
129
+ static id2ref_func_t f_id2ref = NULL;
130
+ if(f_id2ref == NULL)
131
+ {
132
+ f_id2ref = ruby_method_ptr(rb_mObjectSpace, rb_intern("_id2ref"));
133
+ }
134
+ return f_id2ref(rb_mObjectSpace, id);
135
+ }
136
+
137
+ static VALUE
138
+ id2ref_error()
139
+ {
140
+ return Qnil;
141
+ }
142
+
143
+ static VALUE
144
+ id2ref(unsigned long id)
145
+ {
146
+ return rb_rescue(id2ref_unprotected, ULONG2NUM(id), id2ref_error, 0);
147
+ }
148
+
149
+ inline static VALUE
150
+ context_thread_0(debug_context_t *debug_context)
151
+ {
152
+ return id2ref(debug_context->thread_id);
153
+ }
154
+
99
155
  static int
100
- is_in_locked(VALUE thread)
156
+ is_in_locked(unsigned long thread_id)
101
157
  {
102
158
  locked_thread_t *node;
103
159
 
@@ -106,7 +162,7 @@ is_in_locked(VALUE thread)
106
162
 
107
163
  for(node = locked_head; node != locked_tail; node = node->next)
108
164
  {
109
- if(node->thread == thread) return 1;
165
+ if(node->thread_id == thread_id) return 1;
110
166
  }
111
167
  return 0;
112
168
  }
@@ -115,12 +171,13 @@ static void
115
171
  add_to_locked(VALUE thread)
116
172
  {
117
173
  locked_thread_t *node;
174
+ unsigned long thread_id = ref2id(thread);
118
175
 
119
- if(is_in_locked(thread))
176
+ if(is_in_locked(thread_id))
120
177
  return;
121
178
 
122
179
  node = ALLOC(locked_thread_t);
123
- node->thread = thread;
180
+ node->thread_id = thread_id;
124
181
  node->next = NULL;
125
182
  if(locked_tail)
126
183
  locked_tail->next = node;
@@ -141,15 +198,15 @@ remove_from_locked()
141
198
  locked_head = locked_head->next;
142
199
  if(locked_tail == node)
143
200
  locked_tail = NULL;
144
- thread = node->thread;
201
+ thread = id2ref(node->thread_id);
145
202
  xfree(node);
146
203
  return thread;
147
204
  }
148
205
 
149
206
  static int
150
- thread_hash(VALUE thread)
207
+ thread_hash(unsigned long thread_id)
151
208
  {
152
- return (int)FIX2LONG(rb_obj_id(thread));
209
+ return (int)thread_id;
153
210
  }
154
211
 
155
212
  static int
@@ -168,7 +225,6 @@ static struct st_hash_type st_thread_hash = {
168
225
  static int
169
226
  threads_table_mark_keyvalue(VALUE key, VALUE value, int dummy)
170
227
  {
171
- rb_gc_mark(key);
172
228
  rb_gc_mark(value);
173
229
  return ST_CONTINUE;
174
230
  }
@@ -213,6 +269,43 @@ threads_table_clear(VALUE table)
213
269
  st_foreach(threads_table->tbl, threads_table_clear_i, 0);
214
270
  }
215
271
 
272
+ static VALUE
273
+ is_thread_alive(VALUE thread)
274
+ {
275
+ static ID id_alive = 0;
276
+ if(!id_alive)
277
+ {
278
+ id_alive = rb_intern("alive?");
279
+ }
280
+ return rb_funcall(thread, id_alive, 0);
281
+ }
282
+
283
+ static int
284
+ threads_table_check_i(VALUE key, VALUE value, VALUE dummy)
285
+ {
286
+ VALUE thread;
287
+
288
+ thread = id2ref(key);
289
+ if(!rb_obj_is_kind_of(thread, rb_cThread))
290
+ {
291
+ return ST_DELETE;
292
+ }
293
+ if(rb_protect(is_thread_alive, thread, 0) != Qtrue)
294
+ {
295
+ return ST_DELETE;
296
+ }
297
+ return ST_CONTINUE;
298
+ }
299
+
300
+ static void
301
+ check_thread_contexts()
302
+ {
303
+ threads_table_t *threads_table;
304
+
305
+ Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
306
+ st_foreach(threads_table->tbl, threads_table_check_i, 0);
307
+ }
308
+
216
309
  /*
217
310
  * call-seq:
218
311
  * Debugger.started? -> bool
@@ -239,9 +332,6 @@ debug_context_mark(void* data)
239
332
  {
240
333
  debug_context_t *debug_context = (debug_context_t *)data;
241
334
  rb_gc_mark(debug_context->frames);
242
- rb_gc_mark(debug_context->thread);
243
- rb_gc_mark(debug_context->last_file);
244
- rb_gc_mark(debug_context->last_line);
245
335
  }
246
336
 
247
337
  static VALUE
@@ -253,8 +343,8 @@ debug_context_create(VALUE thread)
253
343
  debug_context = ALLOC(debug_context_t);
254
344
  debug_context-> thnum = ++thnum_max;
255
345
 
256
- debug_context->last_file = Qnil;
257
- debug_context->last_line = Qnil;
346
+ debug_context->last_file = NULL;
347
+ debug_context->last_line = 0;
258
348
  debug_context->flags = 0;
259
349
 
260
350
  debug_context->stop_next = -1;
@@ -262,7 +352,7 @@ debug_context_create(VALUE thread)
262
352
  debug_context->stop_line = -1;
263
353
  debug_context->stop_frame = -1;
264
354
  debug_context->frames = rb_ary_new();
265
- debug_context->thread = thread;
355
+ debug_context->thread_id = ref2id(thread);
266
356
  result = Data_Wrap_Struct(cContext, debug_context_mark, xfree, debug_context);
267
357
  return result;
268
358
  }
@@ -272,14 +362,15 @@ thread_context_lookup(VALUE thread)
272
362
  {
273
363
  VALUE context;
274
364
  threads_table_t *threads_table;
365
+ unsigned long thread_id = ref2id(thread);
275
366
 
276
367
  debug_check_started();
277
368
 
278
369
  Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
279
- if(!st_lookup(threads_table->tbl, thread, &context))
370
+ if(!st_lookup(threads_table->tbl, thread_id, &context))
280
371
  {
281
372
  context = debug_context_create(thread);
282
- st_insert(threads_table->tbl, thread, context);
373
+ st_insert(threads_table->tbl, thread_id, context);
283
374
  }
284
375
  return context;
285
376
  }
@@ -289,12 +380,10 @@ debug_frame_mark(void *data)
289
380
  {
290
381
  debug_frame_t *debug_frame = (debug_frame_t *)data;
291
382
  rb_gc_mark(debug_frame->binding);
292
- rb_gc_mark(debug_frame->file);
293
- rb_gc_mark(debug_frame->line);
294
383
  }
295
384
 
296
385
  static VALUE
297
- debug_frame_create(VALUE file, VALUE line, VALUE binding, ID id)
386
+ debug_frame_create(char *file, int line, VALUE binding, ID id)
298
387
  {
299
388
  VALUE result;
300
389
  debug_frame_t *debug_frame;
@@ -337,89 +426,112 @@ call_at_line_unprotected(VALUE args)
337
426
  }
338
427
 
339
428
  static VALUE
340
- call_at_line(VALUE context, int thnum, VALUE binding, VALUE file, VALUE line)
429
+ call_at_line(VALUE context, int thnum, VALUE file, VALUE line)
341
430
  {
342
431
  VALUE args;
343
432
 
344
433
  last_debugged_thnum = thnum;
345
434
  save_current_position(context);
346
435
 
347
- args = rb_ary_new3(4, context, file, line, binding);
436
+ args = rb_ary_new3(3, context, file, line);
348
437
  return rb_protect(call_at_line_unprotected, args, 0);
349
438
  }
350
439
 
351
440
  static void
352
- set_frame_source(debug_context_t *debug_context, VALUE file, VALUE line)
353
- {
354
- VALUE frame;
355
- debug_frame_t *top_frame;
356
-
357
- if(RARRAY(debug_context->frames)->len > 0)
358
- {
359
- frame = *RARRAY(debug_context->frames)->ptr;
360
- Data_Get_Struct(frame, debug_frame_t, top_frame);
361
- top_frame->file = file;
362
- top_frame->line = line;
363
- }
364
- }
365
-
366
- static void
367
- save_call_frame(VALUE self, VALUE file, VALUE line, ID mid, debug_context_t *debug_context)
441
+ save_call_frame(VALUE self, char *file, int line, ID mid, debug_context_t *debug_context)
368
442
  {
369
443
  VALUE frame, binding;
370
444
 
371
- binding = self? create_binding(self) : Qnil;
445
+ binding = self && RTEST(keep_frame_info)? create_binding(self) : Qnil;
372
446
  frame = debug_frame_create(file, line, binding, mid);
373
447
  rb_ary_unshift(debug_context->frames, frame);
374
448
  }
375
449
 
376
- static VALUE
377
- basename(VALUE filename)
450
+ #if defined DOSISH
451
+ #define isdirsep(x) ((x) == '/' || (x) == '\\')
452
+ #else
453
+ #define isdirsep(x) ((x) == '/')
454
+ #endif
455
+
456
+ static int
457
+ filename_cmp(VALUE source, char *file)
378
458
  {
379
- return rb_funcall(rb_cFile, idBasename, 1, filename);
459
+ char *source_ptr, *file_ptr;
460
+ int s_len, f_len, min_len;
461
+ int s,f;
462
+ int dirsep_flag = 0;
463
+
464
+ s_len = RSTRING(source)->len;
465
+ f_len = strlen(file);
466
+ min_len = min(s_len, f_len);
467
+
468
+ source_ptr = RSTRING(source)->ptr;
469
+ file_ptr = file;
470
+
471
+ for( s = s_len - 1, f = f_len - 1; s >= s_len - min_len && f >= f_len - min_len; s--, f-- )
472
+ {
473
+ if((source_ptr[s] == '.' || file_ptr[f] == '.') && dirsep_flag)
474
+ return 1;
475
+ if(source_ptr[s] != file_ptr[f])
476
+ return 0;
477
+ if(isdirsep(source_ptr[s]))
478
+ dirsep_flag = 1;
479
+ }
480
+ return 1;
380
481
  }
381
482
 
382
483
  static int
383
- filename_cmp(debug_breakpoint_t *debug_breakpoint, VALUE file)
484
+ check_breakpoints_by_pos(debug_context_t *debug_context, char *file, int line)
384
485
  {
385
- VALUE flag = Qnil;
486
+ VALUE breakpoint;
487
+ debug_breakpoint_t *debug_breakpoint;
488
+ int i;
489
+
490
+ if(RARRAY(breakpoints)->len == 0)
491
+ return -1;
492
+ if(!CTX_FL_TEST(debug_context, CTX_FL_MOVED))
493
+ return -1;
386
494
 
387
- flag = rb_funcall(debug_breakpoint->source, idIndex, 1, file_separator);
388
- if(alt_file_separator != Qnil && flag == Qnil)
389
- flag = rb_funcall(debug_breakpoint->source, idIndex, 1, alt_file_separator);
390
- if(flag == Qnil)
391
- file = basename(file);
392
- else
393
- file = rb_file_expand_path(file, Qnil);
394
- return(rb_str_cmp(debug_breakpoint->source, file) == 0);
495
+ for(i = 0; i < RARRAY(breakpoints)->len; i++)
496
+ {
497
+ breakpoint = rb_ary_entry(breakpoints, i);
498
+ Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
499
+ if(debug_breakpoint->type != BP_POS_TYPE)
500
+ continue;
501
+ if(debug_breakpoint->pos.line != line)
502
+ continue;
503
+ if(filename_cmp(debug_breakpoint->source, file))
504
+ return i;
505
+ }
506
+ return -1;
395
507
  }
396
508
 
397
- static int
398
- classname_cmp(debug_breakpoint_t *debug_breakpoint, VALUE klass)
509
+ inline static int
510
+ classname_cmp(VALUE name, VALUE klass)
399
511
  {
400
- return (klass != Qnil && rb_str_cmp(debug_breakpoint->source, rb_mod_name(klass)) == 0);
512
+ return (klass != Qnil && rb_str_cmp(name, rb_mod_name(klass)) == 0);
401
513
  }
402
514
 
403
515
  static int
404
- check_breakpoints(debug_context_t *debug_context, VALUE file, VALUE klass, VALUE pos)
516
+ check_breakpoints_by_method(debug_context_t *debug_context, VALUE klass, ID mid)
405
517
  {
406
518
  VALUE breakpoint;
407
519
  debug_breakpoint_t *debug_breakpoint;
408
520
  int i;
409
-
521
+
410
522
  if(RARRAY(breakpoints)->len == 0)
411
523
  return -1;
412
524
  if(!CTX_FL_TEST(debug_context, CTX_FL_MOVED))
413
525
  return -1;
414
-
415
526
  for(i = 0; i < RARRAY(breakpoints)->len; i++)
416
527
  {
417
528
  breakpoint = rb_ary_entry(breakpoints, i);
418
529
  Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
419
- if(debug_breakpoint->pos != pos && !(TYPE(pos) == T_STRING &&
420
- TYPE(debug_breakpoint->pos) == T_STRING && rb_str_cmp(debug_breakpoint->pos, pos) == 0))
530
+ if(debug_breakpoint->type != BP_METHOD_TYPE)
421
531
  continue;
422
- if(filename_cmp(debug_breakpoint, file) || classname_cmp(debug_breakpoint, klass))
532
+ if(debug_breakpoint->pos.mid != mid)
533
+ continue;
534
+ if(classname_cmp(debug_breakpoint->source, klass))
423
535
  return i;
424
536
  }
425
537
  return -1;
@@ -437,10 +549,7 @@ create_binding(VALUE self)
437
549
 
438
550
  if(f_binding == NULL)
439
551
  {
440
- NODE *body, *method;
441
- st_lookup(RCLASS(rb_mKernel)->m_tbl, idBinding, (st_data_t *)&body);
442
- method = (NODE *)body->u2.value;
443
- f_binding = (bind_func_t)method->u1.value;
552
+ f_binding = (bind_func_t)ruby_method_ptr(rb_mKernel, rb_intern("binding"));
444
553
  }
445
554
  return f_binding(self);
446
555
  }
@@ -457,7 +566,7 @@ eval_expression(VALUE args)
457
566
  return rb_funcall2(rb_mKernel, idEval, 2, RARRAY(args)->ptr);
458
567
  }
459
568
 
460
- static int
569
+ inline static int
461
570
  check_breakpoint_expression(VALUE breakpoint, VALUE binding)
462
571
  {
463
572
  debug_breakpoint_t *debug_breakpoint;
@@ -472,15 +581,53 @@ check_breakpoint_expression(VALUE breakpoint, VALUE binding)
472
581
  return RTEST(expr_result);
473
582
  }
474
583
 
584
+ inline static debug_frame_t *
585
+ get_top_frame(debug_context_t *debug_context)
586
+ {
587
+ VALUE frame;
588
+ debug_frame_t *debug_frame;
589
+ if(RARRAY(debug_context->frames)->len == 0)
590
+ return NULL;
591
+ else {
592
+ frame = RARRAY(debug_context->frames)->ptr[0];
593
+ Data_Get_Struct(frame, debug_frame_t, debug_frame);
594
+ return debug_frame;
595
+ }
596
+ }
597
+
598
+ inline static void
599
+ save_top_binding(debug_context_t *debug_context, VALUE binding)
600
+ {
601
+ debug_frame_t *debug_frame;
602
+ debug_frame = get_top_frame(debug_context);
603
+ if(debug_frame)
604
+ debug_frame->binding = binding;
605
+ }
606
+
607
+ static void
608
+ set_frame_source(debug_context_t *debug_context, char *file, int line)
609
+ {
610
+ debug_frame_t *top_frame;
611
+ top_frame = get_top_frame(debug_context);
612
+ if(top_frame)
613
+ {
614
+ top_frame->file = file;
615
+ top_frame->line = line;
616
+ }
617
+ }
618
+
475
619
  static void
476
620
  debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
477
621
  {
478
622
  VALUE thread, context, breakpoint;
479
623
  VALUE binding = Qnil;
480
624
  debug_context_t *debug_context;
481
- VALUE file = Qnil, line = Qnil;
625
+ char *file;
626
+ int line;
482
627
  int breakpoint_index = -1;
483
628
 
629
+ hook_count++;
630
+
484
631
  if (mid == ID_ALLOCATOR) return;
485
632
  if(!node) return;
486
633
 
@@ -519,8 +666,8 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
519
666
  /* ignore a skipped section of code */
520
667
  if(CTX_FL_TEST(debug_context, CTX_FL_SKIPPED)) goto cleanup;
521
668
 
522
- file = rb_str_new2(node->nd_file);
523
- line = INT2FIX(nd_line(node));
669
+ file = node->nd_file;
670
+ line = nd_line(node);
524
671
 
525
672
  if(DID_MOVED)
526
673
  CTX_FL_SET(debug_context, CTX_FL_MOVED);
@@ -533,7 +680,7 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
533
680
 
534
681
  if(RTEST(tracing) || CTX_FL_TEST(debug_context, CTX_FL_TRACING))
535
682
  {
536
- rb_funcall(context, idAtTracing, 2, file, line);
683
+ rb_funcall(context, idAtTracing, 2, rb_str_new2(file), INT2FIX(line));
537
684
  }
538
685
 
539
686
  if(debug_context->dest_frame == -1 ||
@@ -559,7 +706,7 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
559
706
  }
560
707
 
561
708
  if(debug_context->stop_next == 0 || debug_context->stop_line == 0 ||
562
- (breakpoint_index = check_breakpoints(debug_context, file, klass, line)) != -1)
709
+ (breakpoint_index = check_breakpoints_by_pos(debug_context, file, line)) != -1)
563
710
  {
564
711
  binding = self? create_binding(self) : Qnil;
565
712
  /* check breakpoint expression */
@@ -579,7 +726,8 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
579
726
  debug_context->stop_line = -1;
580
727
  debug_context->stop_next = -1;
581
728
 
582
- call_at_line(context, debug_context->thnum, binding, file, line);
729
+ save_top_binding(debug_context, binding);
730
+ call_at_line(context, debug_context->thnum, rb_str_new2(file), INT2FIX(line));
583
731
  }
584
732
  break;
585
733
  }
@@ -591,15 +739,21 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
591
739
  case RUBY_EVENT_CALL:
592
740
  {
593
741
  save_call_frame(self, file, line, mid, debug_context);
594
- breakpoint_index = check_breakpoints(debug_context, file, klass, rb_str_new2(rb_id2name(mid)));
742
+ breakpoint_index = check_breakpoints_by_method(debug_context, klass, mid);
595
743
  if(breakpoint_index != -1)
596
744
  {
597
- binding = self? create_binding(self) : Qnil;
745
+ debug_frame_t *debug_frame;
746
+ debug_frame = get_top_frame(debug_context);
747
+ if(debug_frame)
748
+ binding = debug_frame->binding;
749
+ if(NIL_P(binding) && self)
750
+ binding = create_binding(self);
598
751
  breakpoint = get_breakpoint_at(breakpoint_index);
599
752
  if(check_breakpoint_expression(breakpoint, binding))
600
753
  {
754
+ save_top_binding(debug_context, binding);
601
755
  rb_funcall(context, idAtBreakpoint, 1, breakpoint);
602
- call_at_line(context, debug_context->thnum, binding, file, line);
756
+ call_at_line(context, debug_context->thnum, rb_str_new2(file), INT2FIX(line));
603
757
  }
604
758
  }
605
759
  break;
@@ -629,8 +783,8 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
629
783
  if(post_mortem == Qtrue && self)
630
784
  {
631
785
  binding = create_binding(self);
632
- rb_ivar_set(ruby_errinfo, rb_intern("@__debug_file"), file);
633
- rb_ivar_set(ruby_errinfo, rb_intern("@__debug_line"), line);
786
+ rb_ivar_set(ruby_errinfo, rb_intern("@__debug_file"), rb_str_new2(file));
787
+ rb_ivar_set(ruby_errinfo, rb_intern("@__debug_line"), INT2FIX(line));
634
788
  rb_ivar_set(ruby_errinfo, rb_intern("@__debug_binding"), binding);
635
789
  rb_ivar_set(ruby_errinfo, rb_intern("@__debug_frames"), rb_obj_dup(debug_context->frames));
636
790
  }
@@ -654,7 +808,8 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
654
808
  rb_funcall(context, idAtCatchpoint, 1, ruby_errinfo);
655
809
  if(self && binding == Qnil)
656
810
  binding = create_binding(self);
657
- call_at_line(context, debug_context->thnum, binding, file, line);
811
+ save_top_binding(debug_context, binding);
812
+ call_at_line(context, debug_context->thnum, rb_str_new2(file), INT2FIX(line));
658
813
  break;
659
814
  }
660
815
  }
@@ -669,6 +824,13 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
669
824
 
670
825
  cleanup:
671
826
 
827
+ /* check that all contexts point to alive threads */
828
+ if(hook_count - last_check > 1000)
829
+ {
830
+ check_thread_contexts();
831
+ last_check = hook_count;
832
+ }
833
+
672
834
  /* release a lock */
673
835
  locker = Qnil;
674
836
  /* let the next thread to run */
@@ -755,7 +917,6 @@ breakpoint_mark(void *data)
755
917
  debug_breakpoint_t *breakpoint;
756
918
  breakpoint = (debug_breakpoint_t *)data;
757
919
  rb_gc_mark(breakpoint->source);
758
- rb_gc_mark(breakpoint->pos);
759
920
  rb_gc_mark(breakpoint->expr);
760
921
  }
761
922
 
@@ -775,6 +936,7 @@ debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
775
936
  VALUE source, pos, expr;
776
937
  VALUE result;
777
938
  debug_breakpoint_t *breakpoint;
939
+ int type;
778
940
 
779
941
  debug_check_started();
780
942
 
@@ -782,11 +944,19 @@ debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
782
944
  {
783
945
  expr = Qnil;
784
946
  }
785
-
947
+ type = FIXNUM_P(pos) ? BP_POS_TYPE : BP_METHOD_TYPE;
948
+ if(type == BP_POS_TYPE)
949
+ source = StringValue(source);
950
+ else
951
+ pos = StringValue(pos);
786
952
  breakpoint = ALLOC(debug_breakpoint_t);
787
- breakpoint->source = StringValue(source);
788
953
  breakpoint->id = ++bkp_count;
789
- breakpoint->pos = pos;
954
+ breakpoint->source = source;
955
+ breakpoint->type = type;
956
+ if(type == BP_POS_TYPE)
957
+ breakpoint->pos.line = FIX2INT(pos);
958
+ else
959
+ breakpoint->pos.mid = rb_intern(RSTRING(pos)->ptr);
790
960
  breakpoint->expr = NIL_P(expr) ? expr: StringValue(expr);
791
961
  result = Data_Wrap_Struct(cBreakpoint, breakpoint_mark, xfree, breakpoint);
792
962
  rb_ary_push(breakpoints, result);
@@ -958,7 +1128,7 @@ debug_contexts(VALUE self)
958
1128
  {
959
1129
  context = rb_ary_entry(new_list, i);
960
1130
  Data_Get_Struct(context, debug_context_t, debug_context);
961
- st_insert(threads_table->tbl, debug_context->thread, context);
1131
+ st_insert(threads_table->tbl, debug_context->thread_id, context);
962
1132
  }
963
1133
 
964
1134
  return new_list;
@@ -1033,7 +1203,7 @@ debug_resume(VALUE self)
1033
1203
  if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
1034
1204
  {
1035
1205
  CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
1036
- rb_thread_run(debug_context->thread);
1206
+ rb_thread_run(context_thread_0(debug_context));
1037
1207
  }
1038
1208
  }
1039
1209
  rb_thread_critical = saved_crit;
@@ -1096,6 +1266,31 @@ debug_set_post_mortem(VALUE self, VALUE value)
1096
1266
  return value;
1097
1267
  }
1098
1268
 
1269
+ /*
1270
+ * call-seq:
1271
+ * Debugger.post_mortem? -> bool
1272
+ *
1273
+ * Returns +true+ if the debugger will collect frame bindings.
1274
+ */
1275
+ static VALUE
1276
+ debug_keep_frame_info(VALUE self)
1277
+ {
1278
+ return keep_frame_info;
1279
+ }
1280
+
1281
+ /*
1282
+ * call-seq:
1283
+ * Debugger.post_mortem = bool
1284
+ *
1285
+ * Setting to +true+ will make the debugger keep frame bindings.
1286
+ */
1287
+ static VALUE
1288
+ debug_set_keep_frame_info(VALUE self, VALUE value)
1289
+ {
1290
+ keep_frame_info = RTEST(value) ? Qtrue : Qfalse;
1291
+ return value;
1292
+ }
1293
+
1099
1294
  /*
1100
1295
  * call-seq:
1101
1296
  * Debugger.debug_load(file) -> nil
@@ -1295,7 +1490,7 @@ context_thread(VALUE self)
1295
1490
  debug_context_t *debug_context;
1296
1491
 
1297
1492
  Data_Get_Struct(self, debug_context_t, debug_context);
1298
- return debug_context->thread;
1493
+ return context_thread_0(debug_context);
1299
1494
  }
1300
1495
 
1301
1496
  /*
@@ -1350,7 +1545,7 @@ context_resume(VALUE self)
1350
1545
  if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
1351
1546
  rb_raise(rb_eRuntimeError, "Thread is not suspended.");
1352
1547
  CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
1353
- rb_thread_run(debug_context->thread);
1548
+ rb_thread_run(context_thread_0(debug_context));
1354
1549
  return Qnil;
1355
1550
  }
1356
1551
 
@@ -1442,7 +1637,7 @@ frame_file(VALUE self)
1442
1637
  debug_frame_t *debug_frame;
1443
1638
 
1444
1639
  Data_Get_Struct(self, debug_frame_t, debug_frame);
1445
- return debug_frame->file;
1640
+ return rb_str_new2(debug_frame->file);
1446
1641
  }
1447
1642
 
1448
1643
  /*
@@ -1457,7 +1652,7 @@ frame_line(VALUE self)
1457
1652
  debug_frame_t *debug_frame;
1458
1653
 
1459
1654
  Data_Get_Struct(self, debug_frame_t, debug_frame);
1460
- return debug_frame->line;
1655
+ return INT2FIX(debug_frame->line);
1461
1656
  }
1462
1657
 
1463
1658
  /*
@@ -1517,7 +1712,10 @@ breakpoint_pos(VALUE self)
1517
1712
  debug_breakpoint_t *breakpoint;
1518
1713
 
1519
1714
  Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
1520
- return breakpoint->pos;
1715
+ if(breakpoint->type == BP_METHOD_TYPE)
1716
+ return rb_str_new2(rb_id2name(breakpoint->pos.mid));
1717
+ else
1718
+ return INT2FIX(breakpoint->pos.line);
1521
1719
  }
1522
1720
 
1523
1721
  /*
@@ -1648,6 +1846,8 @@ Init_ruby_debug()
1648
1846
  rb_define_module_function(mDebugger, "debug_at_exit", debug_at_exit, 0);
1649
1847
  rb_define_module_function(mDebugger, "post_mortem?", debug_post_mortem, 0);
1650
1848
  rb_define_module_function(mDebugger, "post_mortem=", debug_set_post_mortem, 1);
1849
+ rb_define_module_function(mDebugger, "keep_frame_info?", debug_keep_frame_info, 0);
1850
+ rb_define_module_function(mDebugger, "keep_frame_info=", debug_set_keep_frame_info, 1);
1651
1851
 
1652
1852
  cThreadsTable = rb_define_class_under(mDebugger, "ThreadsTable", rb_cObject);
1653
1853
 
@@ -1659,20 +1859,15 @@ Init_ruby_debug()
1659
1859
  idAtBreakpoint = rb_intern("at_breakpoint");
1660
1860
  idAtCatchpoint = rb_intern("at_catchpoint");
1661
1861
  idAtTracing = rb_intern("at_tracing");
1662
- idBinding = rb_intern("binding");
1663
- idBasename = rb_intern("basename");
1664
1862
  idEval = rb_intern("eval");
1665
1863
  idList = rb_intern("list");
1666
1864
  idClear = rb_intern("clear");
1667
1865
  idIndex = rb_intern("index");
1668
1866
 
1669
- file_separator = rb_eval_string("File::SEPARATOR");
1670
- alt_file_separator = rb_eval_string("File::ALT_SEPARATOR");
1867
+ rb_mObjectSpace = rb_const_get(rb_mKernel, rb_intern("ObjectSpace"));
1671
1868
 
1672
1869
  rb_global_variable(&threads_tbl);
1673
1870
  rb_global_variable(&breakpoints);
1674
1871
  rb_global_variable(&catchpoint);
1675
1872
  rb_global_variable(&locker);
1676
- rb_global_variable(&file_separator);
1677
- rb_global_variable(&alt_file_separator);
1678
1873
  }
@@ -0,0 +1,90 @@
1
+ CTX_FL_SET ruby_debug.c /^#define CTX_FL_SET(c,f) do { (c)->flags |= (f); } /
2
+ CTX_FL_TEST ruby_debug.c /^#define CTX_FL_TEST(c,f) ((c)->flags & (f))$/
3
+ CTX_FL_UNSET ruby_debug.c /^#define CTX_FL_UNSET(c,f) do { (c)->flags &= ~(f);/
4
+ Init_breakpoint ruby_debug.c /^Init_breakpoint()$/
5
+ Init_context ruby_debug.c /^Init_context()$/
6
+ Init_frame ruby_debug.c /^Init_frame()$/
7
+ Init_ruby_debug ruby_debug.c /^Init_ruby_debug()$/
8
+ VALUE ruby_debug.c /^ typedef VALUE (*bind_func_t)(VALUE);$/
9
+ add_to_locked ruby_debug.c /^add_to_locked(VALUE thread)$/
10
+ basename ruby_debug.c /^basename(VALUE filename)$/
11
+ breakpoint_expr ruby_debug.c /^breakpoint_expr(VALUE self)$/
12
+ breakpoint_id ruby_debug.c /^breakpoint_id(VALUE self)$/
13
+ breakpoint_mark ruby_debug.c /^breakpoint_mark(void *data)$/
14
+ breakpoint_pos ruby_debug.c /^breakpoint_pos(VALUE self)$/
15
+ breakpoint_source ruby_debug.c /^breakpoint_source(VALUE self)$/
16
+ call_at_line ruby_debug.c /^call_at_line(VALUE context, int thnum, VALUE bindi/
17
+ call_at_line_unprotected ruby_debug.c /^call_at_line_unprotected(VALUE args)$/
18
+ check_breakpoint_expression ruby_debug.c /^check_breakpoint_expression(VALUE breakpoint, VALU/
19
+ check_breakpoints ruby_debug.c /^check_breakpoints(debug_context_t *debug_context, /
20
+ classname_cmp ruby_debug.c /^classname_cmp(debug_breakpoint_t *debug_breakpoint/
21
+ context_frames ruby_debug.c /^context_frames(VALUE self)$/
22
+ context_ignore ruby_debug.c /^context_ignore(VALUE self)$/
23
+ context_resume ruby_debug.c /^context_resume(VALUE self)$/
24
+ context_set_ignore ruby_debug.c /^context_set_ignore(VALUE self, VALUE value)$/
25
+ context_set_tracing ruby_debug.c /^context_set_tracing(VALUE self, VALUE value)$/
26
+ context_step_over ruby_debug.c /^context_step_over(int argc, VALUE *argv, VALUE sel/
27
+ context_stop_frame ruby_debug.c /^context_stop_frame(VALUE self, VALUE frame)$/
28
+ context_stop_next ruby_debug.c /^context_stop_next(VALUE self, VALUE steps)$/
29
+ context_suspend ruby_debug.c /^context_suspend(VALUE self)$/
30
+ context_thnum ruby_debug.c /^context_thnum(VALUE self)$/
31
+ context_thread ruby_debug.c /^context_thread(VALUE self)$/
32
+ context_tracing ruby_debug.c /^context_tracing(VALUE self)$/
33
+ create_binding ruby_debug.c /^create_binding(VALUE self)$/
34
+ debug_add_breakpoint ruby_debug.c /^debug_add_breakpoint(int argc, VALUE *argv, VALUE /
35
+ debug_at_exit ruby_debug.c /^debug_at_exit(VALUE self)$/
36
+ debug_at_exit_c ruby_debug.c /^debug_at_exit_c(VALUE proc)$/
37
+ debug_at_exit_i ruby_debug.c /^debug_at_exit_i(VALUE proc)$/
38
+ debug_breakpoint_t ruby_debug.c /^} debug_breakpoint_t;$/
39
+ debug_breakpoints ruby_debug.c /^debug_breakpoints(VALUE self)$/
40
+ debug_catchpoint ruby_debug.c /^debug_catchpoint(VALUE self)$/
41
+ debug_check_started ruby_debug.c /^debug_check_started()$/
42
+ debug_context_create ruby_debug.c /^debug_context_create(VALUE thread)$/
43
+ debug_context_mark ruby_debug.c /^debug_context_mark(void* data)$/
44
+ debug_context_t ruby_debug.c /^} debug_context_t;$/
45
+ debug_contexts ruby_debug.c /^debug_contexts(VALUE self)$/
46
+ debug_current_context ruby_debug.c /^debug_current_context(VALUE self)$/
47
+ debug_debug_load ruby_debug.c /^debug_debug_load(VALUE self, VALUE file)$/
48
+ debug_event_hook ruby_debug.c /^debug_event_hook(rb_event_t event, NODE *node, VAL/
49
+ debug_frame_create ruby_debug.c /^debug_frame_create(VALUE file, VALUE line, VALUE b/
50
+ debug_frame_mark ruby_debug.c /^debug_frame_mark(void *data)$/
51
+ debug_frame_t ruby_debug.c /^} debug_frame_t;$/
52
+ debug_is_started ruby_debug.c /^debug_is_started(VALUE self)$/
53
+ debug_last_interrupted ruby_debug.c /^debug_last_interrupted(VALUE self)$/
54
+ debug_post_mortem ruby_debug.c /^debug_post_mortem(VALUE self)$/
55
+ debug_remove_breakpoint ruby_debug.c /^debug_remove_breakpoint(VALUE self, VALUE id_value/
56
+ debug_resume ruby_debug.c /^debug_resume(VALUE self)$/
57
+ debug_set_catchpoint ruby_debug.c /^debug_set_catchpoint(VALUE self, VALUE value)$/
58
+ debug_set_post_mortem ruby_debug.c /^debug_set_post_mortem(VALUE self, VALUE value)$/
59
+ debug_set_tracing ruby_debug.c /^debug_set_tracing(VALUE self, VALUE value)$/
60
+ debug_skip ruby_debug.c /^debug_skip(VALUE self)$/
61
+ debug_start ruby_debug.c /^debug_start(VALUE self)$/
62
+ debug_stop ruby_debug.c /^debug_stop(VALUE self)$/
63
+ debug_stop_i ruby_debug.c /^debug_stop_i(VALUE self)$/
64
+ debug_suspend ruby_debug.c /^debug_suspend(VALUE self)$/
65
+ debug_tracing ruby_debug.c /^debug_tracing(VALUE self)$/
66
+ eval_expression ruby_debug.c /^eval_expression(VALUE args)$/
67
+ filename_cmp ruby_debug.c /^filename_cmp(debug_breakpoint_t *debug_breakpoint,/
68
+ find_last_context_func ruby_debug.c /^find_last_context_func(VALUE key, VALUE value, VAL/
69
+ frame_binding ruby_debug.c /^frame_binding(VALUE self)$/
70
+ frame_file ruby_debug.c /^frame_file(VALUE self)$/
71
+ frame_id ruby_debug.c /^frame_id(VALUE self)$/
72
+ frame_line ruby_debug.c /^frame_line(VALUE self)$/
73
+ get_breakpoint_at ruby_debug.c /^get_breakpoint_at(int index) $/
74
+ is_in_locked ruby_debug.c /^is_in_locked(VALUE thread)$/
75
+ locked_thread_t ruby_debug.c /^} locked_thread_t;$/
76
+ remove_from_locked ruby_debug.c /^remove_from_locked()$/
77
+ save_call_frame ruby_debug.c /^save_call_frame(VALUE self, VALUE file, VALUE line/
78
+ save_current_position ruby_debug.c /^save_current_position(VALUE context)$/
79
+ set_current_skipped_status ruby_debug.c /^set_current_skipped_status(VALUE status)$/
80
+ set_frame_source ruby_debug.c /^set_frame_source(debug_context_t *debug_context, V/
81
+ thread_cmp ruby_debug.c /^thread_cmp(VALUE a, VALUE b)$/
82
+ thread_context_lookup ruby_debug.c /^thread_context_lookup(VALUE thread)$/
83
+ thread_hash ruby_debug.c /^thread_hash(VALUE thread)$/
84
+ threads_table_clear ruby_debug.c /^threads_table_clear(VALUE table)$/
85
+ threads_table_clear_i ruby_debug.c /^threads_table_clear_i(VALUE key, VALUE value, VALU/
86
+ threads_table_create ruby_debug.c /^threads_table_create()$/
87
+ threads_table_free ruby_debug.c /^threads_table_free(void* data)$/
88
+ threads_table_mark ruby_debug.c /^threads_table_mark(void* data)$/
89
+ threads_table_mark_keyvalue ruby_debug.c /^threads_table_mark_keyvalue(VALUE key, VALUE value/
90
+ threads_table_t ruby_debug.c /^} threads_table_t;$/
@@ -36,8 +36,8 @@ module Debugger
36
36
  processor.at_tracing(self, file, line)
37
37
  end
38
38
 
39
- def at_line(file, line, binding)
40
- processor.at_line(self, file, line, binding)
39
+ def at_line(file, line)
40
+ processor.at_line(self, file, line)
41
41
  end
42
42
  end
43
43
 
@@ -170,7 +170,7 @@ module Debugger
170
170
  private :stop_main_thread
171
171
 
172
172
  def source_for(file) # :nodoc:
173
- Dir.chdir File.dirname($0) do
173
+ finder = lambda do
174
174
  unless File.exists?(file)
175
175
  return (SCRIPT_LINES__[file] == true ? nil : SCRIPT_LINES__[file])
176
176
  end
@@ -187,6 +187,7 @@ module Debugger
187
187
 
188
188
  SCRIPT_LINES__[file]
189
189
  end
190
+ Dir.chdir(File.dirname($0)){finder.call} || finder.call
190
191
  end
191
192
 
192
193
  def source_reload
@@ -257,7 +258,11 @@ module Debugger
257
258
 
258
259
  def handle_post_mortem(exp)
259
260
  return if exp.__debug_frames.empty?
260
- processor.at_line(nil, exp.__debug_file, exp.__debug_line, exp.__debug_frames.first.binding, exp.__debug_frames)
261
+ orig_tracing = Debugger.tracing, Debugger.current_context.tracing
262
+ Debugger.tracing = Debugger.current_context.tracing = false
263
+ processor.at_line(nil, exp.__debug_file, exp.__debug_line, exp.__debug_frames)
264
+ ensure
265
+ Debugger.tracing, Debugger.current_context.tracing = orig_tracing
261
266
  end
262
267
  private :handle_post_mortem
263
268
  end
@@ -62,11 +62,15 @@ module Debugger
62
62
  @state.confirm(msg) == 'y'
63
63
  end
64
64
 
65
- def debug_eval(str)
65
+ def debug_eval(str, b = @state.binding)
66
+ unless b
67
+ print "Can't evaluate in the current context.\n"
68
+ throw :debug_error
69
+ end
66
70
  begin
67
- val = eval(str, @state.binding)
71
+ val = eval(str, b)
68
72
  rescue StandardError, ScriptError => e
69
- at = eval("caller(1)", @state.binding)
73
+ at = eval("caller(1)", b)
70
74
  print "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
71
75
  for i in at
72
76
  print "\tfrom %s\n", i
@@ -76,6 +80,7 @@ module Debugger
76
80
  end
77
81
 
78
82
  def debug_silent_eval(str)
83
+ return nil unless @state.binding
79
84
  begin
80
85
  eval(str, @state.binding)
81
86
  rescue StandardError, ScriptError
@@ -35,7 +35,11 @@ module Debugger
35
35
  def execute
36
36
  unless @state.interface.kind_of?(LocalInterface)
37
37
  print "Command is available only in local mode.\n"
38
- return
38
+ throw :debug_error
39
+ end
40
+ unless @state.binding
41
+ print "Can't evaluate in the current context.\n"
42
+ throw :debug_error
39
43
  end
40
44
  IRB.start_session(@state.binding)
41
45
  end
@@ -1,10 +1,17 @@
1
1
  module Debugger
2
2
  module VarFunctions # :nodoc:
3
- def var_list(ary, bind = nil)
4
- bind ||= @state.binding
3
+ def var_list(ary, bind = @state.binding)
5
4
  ary.sort!
6
5
  for v in ary
7
- print " %s => %s\n", v, eval(v, bind).inspect
6
+ print " %s => %s\n", v, debug_eval(v, bind).inspect
7
+ end
8
+ end
9
+
10
+ def var_consts(mod)
11
+ constants = mod.constants
12
+ constants.sort!
13
+ for c in constants
14
+ print " %s => %s\n", c, mod.const_get(c)
8
15
  end
9
16
  end
10
17
  end
@@ -21,7 +28,7 @@ module Debugger
21
28
  unless obj.kind_of? Module
22
29
  print "Should be Class/Module: %s\n", @match.post_match
23
30
  else
24
- var_list(obj.constants, obj.module_eval{binding()})
31
+ var_consts(obj)
25
32
  end
26
33
  end
27
34
 
@@ -95,7 +102,7 @@ module Debugger
95
102
  end
96
103
 
97
104
  def execute
98
- var_list(eval("local_variables", @state.binding))
105
+ var_list(debug_eval("local_variables"))
99
106
  end
100
107
 
101
108
  class << self
@@ -62,9 +62,9 @@ module Debugger
62
62
  end
63
63
  protect :at_tracing
64
64
 
65
- def at_line(context, file, line, binding, frames = context.frames)
65
+ def at_line(context, file, line, frames = context.frames)
66
66
  print "%s:%d: %s", file, line, Debugger.line_at(file, line)
67
- process_commands(context, file, line, binding, frames)
67
+ process_commands(context, file, line, frames)
68
68
  end
69
69
  protect :at_line
70
70
 
@@ -82,13 +82,13 @@ module Debugger
82
82
  end
83
83
  end
84
84
 
85
- def process_commands(context, file, line, binding, frames)
85
+ def process_commands(context, file, line, frames)
86
86
  event_cmds = Command.commands.select{|cmd| cmd.event }
87
87
  state = State.new do |s|
88
88
  s.context = context
89
89
  s.file = file
90
90
  s.line = line
91
- s.binding = binding
91
+ s.binding = frames.first.binding
92
92
  s.display = display
93
93
  s.interface = interface
94
94
  s.commands = event_cmds
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.1
3
3
  specification_version: 1
4
4
  name: ruby-debug
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.5.3
7
- date: 2007-01-21 03:38:07 -05:00
6
+ version: "0.6"
7
+ date: 2007-01-26 15:22:30 -05:00
8
8
  summary: Fast Ruby debugger
9
9
  require_paths:
10
10
  - lib
@@ -57,6 +57,7 @@ files:
57
57
  - lib/ruby-debug/commands/variables.rb
58
58
  - ext/extconf.rb
59
59
  - ext/ruby_debug.c
60
+ - ext/tags
60
61
  - ext/win32
61
62
  - bin/rdebug
62
63
  test_files: []