ruby-debug 0.5.2-mswin32 → 0.6-mswin32

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGES CHANGED
@@ -1,3 +1,15 @@
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
+
9
+ 0.5.3
10
+ - Added Module#post_mortem_method method, which wraps any method with Debugger.post_mortem block.
11
+ - Added breakpoint id, which is not dependent on the breakpoint position in Debugger.breakpoints array.
12
+
1
13
  0.5.2
2
14
  - Fixes interoperability problems with rspec.
3
15
  - Made 'exit' as an alias to 'quit'
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
data/ext/ruby_debug.c CHANGED
@@ -4,7 +4,7 @@
4
4
  #include <rubysig.h>
5
5
  #include <st.h>
6
6
 
7
- #define DEBUG_VERSION "0.5.2"
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,22 +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 {
52
+ int id;
53
+ int type;
46
54
  VALUE source;
47
- VALUE pos;
55
+ union
56
+ {
57
+ int line;
58
+ ID mid;
59
+ } pos;
48
60
  VALUE expr;
49
61
  } debug_breakpoint_t;
50
62
 
@@ -52,12 +64,13 @@ typedef struct {
52
64
  st_table *tbl;
53
65
  } threads_table_t;
54
66
 
55
- static VALUE threads_tbl = Qnil;
56
- static VALUE breakpoints = Qnil;
57
- static VALUE catchpoint = Qnil;
58
- static VALUE tracing = Qfalse;
59
- static VALUE locker = Qnil;
60
- 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;
61
74
 
62
75
  static VALUE mDebugger;
63
76
  static VALUE cThreadsTable;
@@ -65,15 +78,12 @@ static VALUE cContext;
65
78
  static VALUE cFrame;
66
79
  static VALUE cBreakpoint;
67
80
 
68
- static VALUE file_separator;
69
- static VALUE alt_file_separator;
81
+ static VALUE rb_mObjectSpace;
70
82
 
71
83
  static ID idAtLine;
72
84
  static ID idAtBreakpoint;
73
85
  static ID idAtCatchpoint;
74
86
  static ID idAtTracing;
75
- static ID idBinding;
76
- static ID idBasename;
77
87
  static ID idEval;
78
88
  static ID idList;
79
89
  static ID idClear;
@@ -81,21 +91,69 @@ static ID idIndex;
81
91
 
82
92
  static int start_count = 0;
83
93
  static int thnum_max = 0;
94
+ static int bkp_count = 0;
84
95
  static int last_debugged_thnum = -1;
96
+ static unsigned long last_check = 0;
97
+ static unsigned long hook_count = 0;
85
98
 
86
99
  static VALUE create_binding(VALUE);
87
100
  static VALUE debug_stop(VALUE);
88
101
 
89
102
  typedef struct locked_thread_t {
90
- VALUE thread;
103
+ unsigned long thread_id;
91
104
  struct locked_thread_t *next;
92
105
  } locked_thread_t;
93
106
 
94
107
  static locked_thread_t *locked_head = NULL;
95
108
  static locked_thread_t *locked_tail = NULL;
96
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
+
97
155
  static int
98
- is_in_locked(VALUE thread)
156
+ is_in_locked(unsigned long thread_id)
99
157
  {
100
158
  locked_thread_t *node;
101
159
 
@@ -104,7 +162,7 @@ is_in_locked(VALUE thread)
104
162
 
105
163
  for(node = locked_head; node != locked_tail; node = node->next)
106
164
  {
107
- if(node->thread == thread) return 1;
165
+ if(node->thread_id == thread_id) return 1;
108
166
  }
109
167
  return 0;
110
168
  }
@@ -113,12 +171,13 @@ static void
113
171
  add_to_locked(VALUE thread)
114
172
  {
115
173
  locked_thread_t *node;
174
+ unsigned long thread_id = ref2id(thread);
116
175
 
117
- if(is_in_locked(thread))
176
+ if(is_in_locked(thread_id))
118
177
  return;
119
178
 
120
179
  node = ALLOC(locked_thread_t);
121
- node->thread = thread;
180
+ node->thread_id = thread_id;
122
181
  node->next = NULL;
123
182
  if(locked_tail)
124
183
  locked_tail->next = node;
@@ -139,15 +198,15 @@ remove_from_locked()
139
198
  locked_head = locked_head->next;
140
199
  if(locked_tail == node)
141
200
  locked_tail = NULL;
142
- thread = node->thread;
201
+ thread = id2ref(node->thread_id);
143
202
  xfree(node);
144
203
  return thread;
145
204
  }
146
205
 
147
206
  static int
148
- thread_hash(VALUE thread)
207
+ thread_hash(unsigned long thread_id)
149
208
  {
150
- return (int)FIX2LONG(rb_obj_id(thread));
209
+ return (int)thread_id;
151
210
  }
152
211
 
153
212
  static int
@@ -166,7 +225,6 @@ static struct st_hash_type st_thread_hash = {
166
225
  static int
167
226
  threads_table_mark_keyvalue(VALUE key, VALUE value, int dummy)
168
227
  {
169
- rb_gc_mark(key);
170
228
  rb_gc_mark(value);
171
229
  return ST_CONTINUE;
172
230
  }
@@ -211,6 +269,43 @@ threads_table_clear(VALUE table)
211
269
  st_foreach(threads_table->tbl, threads_table_clear_i, 0);
212
270
  }
213
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
+
214
309
  /*
215
310
  * call-seq:
216
311
  * Debugger.started? -> bool
@@ -237,9 +332,6 @@ debug_context_mark(void* data)
237
332
  {
238
333
  debug_context_t *debug_context = (debug_context_t *)data;
239
334
  rb_gc_mark(debug_context->frames);
240
- rb_gc_mark(debug_context->thread);
241
- rb_gc_mark(debug_context->last_file);
242
- rb_gc_mark(debug_context->last_line);
243
335
  }
244
336
 
245
337
  static VALUE
@@ -251,8 +343,8 @@ debug_context_create(VALUE thread)
251
343
  debug_context = ALLOC(debug_context_t);
252
344
  debug_context-> thnum = ++thnum_max;
253
345
 
254
- debug_context->last_file = Qnil;
255
- debug_context->last_line = Qnil;
346
+ debug_context->last_file = NULL;
347
+ debug_context->last_line = 0;
256
348
  debug_context->flags = 0;
257
349
 
258
350
  debug_context->stop_next = -1;
@@ -260,7 +352,7 @@ debug_context_create(VALUE thread)
260
352
  debug_context->stop_line = -1;
261
353
  debug_context->stop_frame = -1;
262
354
  debug_context->frames = rb_ary_new();
263
- debug_context->thread = thread;
355
+ debug_context->thread_id = ref2id(thread);
264
356
  result = Data_Wrap_Struct(cContext, debug_context_mark, xfree, debug_context);
265
357
  return result;
266
358
  }
@@ -270,14 +362,15 @@ thread_context_lookup(VALUE thread)
270
362
  {
271
363
  VALUE context;
272
364
  threads_table_t *threads_table;
365
+ unsigned long thread_id = ref2id(thread);
273
366
 
274
367
  debug_check_started();
275
368
 
276
369
  Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
277
- if(!st_lookup(threads_table->tbl, thread, &context))
370
+ if(!st_lookup(threads_table->tbl, thread_id, &context))
278
371
  {
279
372
  context = debug_context_create(thread);
280
- st_insert(threads_table->tbl, thread, context);
373
+ st_insert(threads_table->tbl, thread_id, context);
281
374
  }
282
375
  return context;
283
376
  }
@@ -287,12 +380,10 @@ debug_frame_mark(void *data)
287
380
  {
288
381
  debug_frame_t *debug_frame = (debug_frame_t *)data;
289
382
  rb_gc_mark(debug_frame->binding);
290
- rb_gc_mark(debug_frame->file);
291
- rb_gc_mark(debug_frame->line);
292
383
  }
293
384
 
294
385
  static VALUE
295
- debug_frame_create(VALUE file, VALUE line, VALUE binding, ID id)
386
+ debug_frame_create(char *file, int line, VALUE binding, ID id)
296
387
  {
297
388
  VALUE result;
298
389
  debug_frame_t *debug_frame;
@@ -335,89 +426,112 @@ call_at_line_unprotected(VALUE args)
335
426
  }
336
427
 
337
428
  static VALUE
338
- 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)
339
430
  {
340
431
  VALUE args;
341
432
 
342
433
  last_debugged_thnum = thnum;
343
434
  save_current_position(context);
344
435
 
345
- args = rb_ary_new3(4, context, file, line, binding);
436
+ args = rb_ary_new3(3, context, file, line);
346
437
  return rb_protect(call_at_line_unprotected, args, 0);
347
438
  }
348
439
 
349
440
  static void
350
- set_frame_source(debug_context_t *debug_context, VALUE file, VALUE line)
351
- {
352
- VALUE frame;
353
- debug_frame_t *top_frame;
354
-
355
- if(RARRAY(debug_context->frames)->len > 0)
356
- {
357
- frame = *RARRAY(debug_context->frames)->ptr;
358
- Data_Get_Struct(frame, debug_frame_t, top_frame);
359
- top_frame->file = file;
360
- top_frame->line = line;
361
- }
362
- }
363
-
364
- static void
365
- 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)
366
442
  {
367
443
  VALUE frame, binding;
368
444
 
369
- binding = self? create_binding(self) : Qnil;
445
+ binding = self && RTEST(keep_frame_info)? create_binding(self) : Qnil;
370
446
  frame = debug_frame_create(file, line, binding, mid);
371
447
  rb_ary_unshift(debug_context->frames, frame);
372
448
  }
373
449
 
374
- static VALUE
375
- 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)
376
458
  {
377
- 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;
378
481
  }
379
482
 
380
483
  static int
381
- filename_cmp(debug_breakpoint_t *debug_breakpoint, VALUE file)
484
+ check_breakpoints_by_pos(debug_context_t *debug_context, char *file, int line)
382
485
  {
383
- 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;
384
494
 
385
- flag = rb_funcall(debug_breakpoint->source, idIndex, 1, file_separator);
386
- if(alt_file_separator != Qnil && flag == Qnil)
387
- flag = rb_funcall(debug_breakpoint->source, idIndex, 1, alt_file_separator);
388
- if(flag == Qnil)
389
- file = basename(file);
390
- else
391
- file = rb_file_expand_path(file, Qnil);
392
- 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;
393
507
  }
394
508
 
395
- static int
396
- classname_cmp(debug_breakpoint_t *debug_breakpoint, VALUE klass)
509
+ inline static int
510
+ classname_cmp(VALUE name, VALUE klass)
397
511
  {
398
- 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);
399
513
  }
400
514
 
401
515
  static int
402
- 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)
403
517
  {
404
518
  VALUE breakpoint;
405
519
  debug_breakpoint_t *debug_breakpoint;
406
520
  int i;
407
-
521
+
408
522
  if(RARRAY(breakpoints)->len == 0)
409
523
  return -1;
410
524
  if(!CTX_FL_TEST(debug_context, CTX_FL_MOVED))
411
525
  return -1;
412
-
413
526
  for(i = 0; i < RARRAY(breakpoints)->len; i++)
414
527
  {
415
528
  breakpoint = rb_ary_entry(breakpoints, i);
416
529
  Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
417
- if(debug_breakpoint->pos != pos && !(TYPE(pos) == T_STRING &&
418
- TYPE(debug_breakpoint->pos) == T_STRING && rb_str_cmp(debug_breakpoint->pos, pos) == 0))
530
+ if(debug_breakpoint->type != BP_METHOD_TYPE)
419
531
  continue;
420
- 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))
421
535
  return i;
422
536
  }
423
537
  return -1;
@@ -435,10 +549,7 @@ create_binding(VALUE self)
435
549
 
436
550
  if(f_binding == NULL)
437
551
  {
438
- NODE *body, *method;
439
- st_lookup(RCLASS(rb_mKernel)->m_tbl, idBinding, (st_data_t *)&body);
440
- method = (NODE *)body->u2.value;
441
- f_binding = (bind_func_t)method->u1.value;
552
+ f_binding = (bind_func_t)ruby_method_ptr(rb_mKernel, rb_intern("binding"));
442
553
  }
443
554
  return f_binding(self);
444
555
  }
@@ -455,7 +566,7 @@ eval_expression(VALUE args)
455
566
  return rb_funcall2(rb_mKernel, idEval, 2, RARRAY(args)->ptr);
456
567
  }
457
568
 
458
- static int
569
+ inline static int
459
570
  check_breakpoint_expression(VALUE breakpoint, VALUE binding)
460
571
  {
461
572
  debug_breakpoint_t *debug_breakpoint;
@@ -470,15 +581,53 @@ check_breakpoint_expression(VALUE breakpoint, VALUE binding)
470
581
  return RTEST(expr_result);
471
582
  }
472
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
+
473
619
  static void
474
620
  debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
475
621
  {
476
622
  VALUE thread, context, breakpoint;
477
623
  VALUE binding = Qnil;
478
624
  debug_context_t *debug_context;
479
- VALUE file = Qnil, line = Qnil;
625
+ char *file;
626
+ int line;
480
627
  int breakpoint_index = -1;
481
628
 
629
+ hook_count++;
630
+
482
631
  if (mid == ID_ALLOCATOR) return;
483
632
  if(!node) return;
484
633
 
@@ -517,8 +666,8 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
517
666
  /* ignore a skipped section of code */
518
667
  if(CTX_FL_TEST(debug_context, CTX_FL_SKIPPED)) goto cleanup;
519
668
 
520
- file = rb_str_new2(node->nd_file);
521
- line = INT2FIX(nd_line(node));
669
+ file = node->nd_file;
670
+ line = nd_line(node);
522
671
 
523
672
  if(DID_MOVED)
524
673
  CTX_FL_SET(debug_context, CTX_FL_MOVED);
@@ -531,7 +680,7 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
531
680
 
532
681
  if(RTEST(tracing) || CTX_FL_TEST(debug_context, CTX_FL_TRACING))
533
682
  {
534
- rb_funcall(context, idAtTracing, 2, file, line);
683
+ rb_funcall(context, idAtTracing, 2, rb_str_new2(file), INT2FIX(line));
535
684
  }
536
685
 
537
686
  if(debug_context->dest_frame == -1 ||
@@ -557,7 +706,7 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
557
706
  }
558
707
 
559
708
  if(debug_context->stop_next == 0 || debug_context->stop_line == 0 ||
560
- (breakpoint_index = check_breakpoints(debug_context, file, klass, line)) != -1)
709
+ (breakpoint_index = check_breakpoints_by_pos(debug_context, file, line)) != -1)
561
710
  {
562
711
  binding = self? create_binding(self) : Qnil;
563
712
  /* check breakpoint expression */
@@ -577,7 +726,8 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
577
726
  debug_context->stop_line = -1;
578
727
  debug_context->stop_next = -1;
579
728
 
580
- 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));
581
731
  }
582
732
  break;
583
733
  }
@@ -589,15 +739,21 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
589
739
  case RUBY_EVENT_CALL:
590
740
  {
591
741
  save_call_frame(self, file, line, mid, debug_context);
592
- 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);
593
743
  if(breakpoint_index != -1)
594
744
  {
595
- 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);
596
751
  breakpoint = get_breakpoint_at(breakpoint_index);
597
752
  if(check_breakpoint_expression(breakpoint, binding))
598
753
  {
754
+ save_top_binding(debug_context, binding);
599
755
  rb_funcall(context, idAtBreakpoint, 1, breakpoint);
600
- call_at_line(context, debug_context->thnum, binding, file, line);
756
+ call_at_line(context, debug_context->thnum, rb_str_new2(file), INT2FIX(line));
601
757
  }
602
758
  }
603
759
  break;
@@ -627,9 +783,8 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
627
783
  if(post_mortem == Qtrue && self)
628
784
  {
629
785
  binding = create_binding(self);
630
- rb_ivar_set(ruby_errinfo, rb_intern("@__debug_binding"), binding);
631
- rb_ivar_set(ruby_errinfo, rb_intern("@__debug_file"), file);
632
- 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));
633
788
  rb_ivar_set(ruby_errinfo, rb_intern("@__debug_binding"), binding);
634
789
  rb_ivar_set(ruby_errinfo, rb_intern("@__debug_frames"), rb_obj_dup(debug_context->frames));
635
790
  }
@@ -653,7 +808,8 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
653
808
  rb_funcall(context, idAtCatchpoint, 1, ruby_errinfo);
654
809
  if(self && binding == Qnil)
655
810
  binding = create_binding(self);
656
- 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));
657
813
  break;
658
814
  }
659
815
  }
@@ -668,6 +824,13 @@ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
668
824
 
669
825
  cleanup:
670
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
+
671
834
  /* release a lock */
672
835
  locker = Qnil;
673
836
  /* let the next thread to run */
@@ -754,7 +917,6 @@ breakpoint_mark(void *data)
754
917
  debug_breakpoint_t *breakpoint;
755
918
  breakpoint = (debug_breakpoint_t *)data;
756
919
  rb_gc_mark(breakpoint->source);
757
- rb_gc_mark(breakpoint->pos);
758
920
  rb_gc_mark(breakpoint->expr);
759
921
  }
760
922
 
@@ -774,6 +936,7 @@ debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
774
936
  VALUE source, pos, expr;
775
937
  VALUE result;
776
938
  debug_breakpoint_t *breakpoint;
939
+ int type;
777
940
 
778
941
  debug_check_started();
779
942
 
@@ -781,16 +944,55 @@ debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
781
944
  {
782
945
  expr = Qnil;
783
946
  }
784
-
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);
785
952
  breakpoint = ALLOC(debug_breakpoint_t);
786
- breakpoint->source = StringValue(source);
787
- breakpoint->pos = pos;
953
+ breakpoint->id = ++bkp_count;
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);
788
960
  breakpoint->expr = NIL_P(expr) ? expr: StringValue(expr);
789
961
  result = Data_Wrap_Struct(cBreakpoint, breakpoint_mark, xfree, breakpoint);
790
962
  rb_ary_push(breakpoints, result);
791
963
  return result;
792
964
  }
793
965
 
966
+ /*
967
+ * call-seq:
968
+ * Debugger.remove_breakpoint(id) -> breakpoint
969
+ *
970
+ * Removes breakpoint by its id.
971
+ * <i>id</i> is an identificator of a breakpoint.
972
+ */
973
+ static VALUE
974
+ debug_remove_breakpoint(VALUE self, VALUE id_value)
975
+ {
976
+ int i;
977
+ int id;
978
+ VALUE breakpoint;
979
+ debug_breakpoint_t *debug_breakpoint;
980
+
981
+ id = FIX2INT(id_value);
982
+
983
+ for( i = 0; i < RARRAY(breakpoints)->len; i += 1 )
984
+ {
985
+ breakpoint = rb_ary_entry(breakpoints, i);
986
+ Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
987
+ if(debug_breakpoint->id == id)
988
+ {
989
+ rb_ary_delete_at(breakpoints, i);
990
+ return breakpoint;
991
+ }
992
+ }
993
+ return Qnil;
994
+ }
995
+
794
996
  /*
795
997
  * call-seq:
796
998
  * Debugger.breakpoints -> array
@@ -926,7 +1128,7 @@ debug_contexts(VALUE self)
926
1128
  {
927
1129
  context = rb_ary_entry(new_list, i);
928
1130
  Data_Get_Struct(context, debug_context_t, debug_context);
929
- st_insert(threads_table->tbl, debug_context->thread, context);
1131
+ st_insert(threads_table->tbl, debug_context->thread_id, context);
930
1132
  }
931
1133
 
932
1134
  return new_list;
@@ -1001,7 +1203,7 @@ debug_resume(VALUE self)
1001
1203
  if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
1002
1204
  {
1003
1205
  CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
1004
- rb_thread_run(debug_context->thread);
1206
+ rb_thread_run(context_thread_0(debug_context));
1005
1207
  }
1006
1208
  }
1007
1209
  rb_thread_critical = saved_crit;
@@ -1064,6 +1266,31 @@ debug_set_post_mortem(VALUE self, VALUE value)
1064
1266
  return value;
1065
1267
  }
1066
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
+
1067
1294
  /*
1068
1295
  * call-seq:
1069
1296
  * Debugger.debug_load(file) -> nil
@@ -1263,7 +1490,7 @@ context_thread(VALUE self)
1263
1490
  debug_context_t *debug_context;
1264
1491
 
1265
1492
  Data_Get_Struct(self, debug_context_t, debug_context);
1266
- return debug_context->thread;
1493
+ return context_thread_0(debug_context);
1267
1494
  }
1268
1495
 
1269
1496
  /*
@@ -1318,7 +1545,7 @@ context_resume(VALUE self)
1318
1545
  if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
1319
1546
  rb_raise(rb_eRuntimeError, "Thread is not suspended.");
1320
1547
  CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
1321
- rb_thread_run(debug_context->thread);
1548
+ rb_thread_run(context_thread_0(debug_context));
1322
1549
  return Qnil;
1323
1550
  }
1324
1551
 
@@ -1410,7 +1637,7 @@ frame_file(VALUE self)
1410
1637
  debug_frame_t *debug_frame;
1411
1638
 
1412
1639
  Data_Get_Struct(self, debug_frame_t, debug_frame);
1413
- return debug_frame->file;
1640
+ return rb_str_new2(debug_frame->file);
1414
1641
  }
1415
1642
 
1416
1643
  /*
@@ -1425,7 +1652,7 @@ frame_line(VALUE self)
1425
1652
  debug_frame_t *debug_frame;
1426
1653
 
1427
1654
  Data_Get_Struct(self, debug_frame_t, debug_frame);
1428
- return debug_frame->line;
1655
+ return INT2FIX(debug_frame->line);
1429
1656
  }
1430
1657
 
1431
1658
  /*
@@ -1485,7 +1712,10 @@ breakpoint_pos(VALUE self)
1485
1712
  debug_breakpoint_t *breakpoint;
1486
1713
 
1487
1714
  Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
1488
- 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);
1489
1719
  }
1490
1720
 
1491
1721
  /*
@@ -1503,6 +1733,21 @@ breakpoint_expr(VALUE self)
1503
1733
  return breakpoint->expr;
1504
1734
  }
1505
1735
 
1736
+ /*
1737
+ * call-seq:
1738
+ * breakpoint.id -> int
1739
+ *
1740
+ * Returns id of the breakpoint.
1741
+ */
1742
+ static VALUE
1743
+ breakpoint_id(VALUE self)
1744
+ {
1745
+ debug_breakpoint_t *breakpoint;
1746
+
1747
+ Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
1748
+ return INT2FIX(breakpoint->id);
1749
+ }
1750
+
1506
1751
  /*
1507
1752
  * Document-class: Context
1508
1753
  *
@@ -1562,6 +1807,7 @@ Init_breakpoint()
1562
1807
  rb_define_method(cBreakpoint, "source", breakpoint_source, 0);
1563
1808
  rb_define_method(cBreakpoint, "pos", breakpoint_pos, 0);
1564
1809
  rb_define_method(cBreakpoint, "expr", breakpoint_expr, 0);
1810
+ rb_define_method(cBreakpoint, "id", breakpoint_id, 0);
1565
1811
  }
1566
1812
 
1567
1813
  /*
@@ -1585,6 +1831,7 @@ Init_ruby_debug()
1585
1831
  rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
1586
1832
  rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
1587
1833
  rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1);
1834
+ rb_define_module_function(mDebugger, "remove_breakpoint", debug_remove_breakpoint, 1);
1588
1835
  rb_define_module_function(mDebugger, "catchpoint", debug_catchpoint, 0);
1589
1836
  rb_define_module_function(mDebugger, "catchpoint=", debug_set_catchpoint, 1);
1590
1837
  rb_define_module_function(mDebugger, "last_context", debug_last_interrupted, 0);
@@ -1599,6 +1846,8 @@ Init_ruby_debug()
1599
1846
  rb_define_module_function(mDebugger, "debug_at_exit", debug_at_exit, 0);
1600
1847
  rb_define_module_function(mDebugger, "post_mortem?", debug_post_mortem, 0);
1601
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);
1602
1851
 
1603
1852
  cThreadsTable = rb_define_class_under(mDebugger, "ThreadsTable", rb_cObject);
1604
1853
 
@@ -1610,20 +1859,15 @@ Init_ruby_debug()
1610
1859
  idAtBreakpoint = rb_intern("at_breakpoint");
1611
1860
  idAtCatchpoint = rb_intern("at_catchpoint");
1612
1861
  idAtTracing = rb_intern("at_tracing");
1613
- idBinding = rb_intern("binding");
1614
- idBasename = rb_intern("basename");
1615
1862
  idEval = rb_intern("eval");
1616
1863
  idList = rb_intern("list");
1617
1864
  idClear = rb_intern("clear");
1618
1865
  idIndex = rb_intern("index");
1619
1866
 
1620
- file_separator = rb_eval_string("File::SEPARATOR");
1621
- alt_file_separator = rb_eval_string("File::ALT_SEPARATOR");
1867
+ rb_mObjectSpace = rb_const_get(rb_mKernel, rb_intern("ObjectSpace"));
1622
1868
 
1623
1869
  rb_global_variable(&threads_tbl);
1624
1870
  rb_global_variable(&breakpoints);
1625
1871
  rb_global_variable(&catchpoint);
1626
1872
  rb_global_variable(&locker);
1627
- rb_global_variable(&file_separator);
1628
- rb_global_variable(&alt_file_separator);
1629
1873
  }