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 +12 -0
- data/bin/rdebug +13 -4
- data/ext/ruby_debug.c +360 -116
- data/ext/tags +90 -0
- data/lib/ruby-debug.rb +29 -5
- data/lib/ruby-debug/command.rb +8 -3
- data/lib/ruby-debug/commands/breakpoints.rb +6 -6
- data/lib/ruby-debug/commands/irb.rb +5 -1
- data/lib/ruby-debug/commands/variables.rb +12 -5
- data/lib/ruby-debug/interface.rb +1 -1
- data/lib/ruby-debug/processor.rb +19 -16
- data/lib/ruby_debug.so +0 -0
- metadata +4 -3
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
|
-
|
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.
|
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 ==
|
21
|
-
|
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
|
-
|
34
|
-
|
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
|
-
|
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
|
56
|
-
static VALUE breakpoints
|
57
|
-
static VALUE catchpoint
|
58
|
-
static VALUE tracing
|
59
|
-
static VALUE locker
|
60
|
-
static VALUE post_mortem
|
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
|
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
|
-
|
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(
|
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->
|
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(
|
176
|
+
if(is_in_locked(thread_id))
|
118
177
|
return;
|
119
178
|
|
120
179
|
node = ALLOC(locked_thread_t);
|
121
|
-
node->
|
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->
|
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(
|
207
|
+
thread_hash(unsigned long thread_id)
|
149
208
|
{
|
150
|
-
return (int)
|
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 =
|
255
|
-
debug_context->last_line =
|
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->
|
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,
|
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,
|
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(
|
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
|
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(
|
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
|
-
|
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
|
-
|
375
|
-
|
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
|
-
|
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
|
-
|
484
|
+
check_breakpoints_by_pos(debug_context_t *debug_context, char *file, int line)
|
382
485
|
{
|
383
|
-
VALUE
|
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
|
-
|
386
|
-
|
387
|
-
|
388
|
-
|
389
|
-
|
390
|
-
|
391
|
-
|
392
|
-
|
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(
|
509
|
+
inline static int
|
510
|
+
classname_cmp(VALUE name, VALUE klass)
|
397
511
|
{
|
398
|
-
return (klass != Qnil && rb_str_cmp(
|
512
|
+
return (klass != Qnil && rb_str_cmp(name, rb_mod_name(klass)) == 0);
|
399
513
|
}
|
400
514
|
|
401
515
|
static int
|
402
|
-
|
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->
|
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(
|
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
|
-
|
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
|
-
|
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 =
|
521
|
-
line =
|
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 =
|
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
|
-
|
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 =
|
742
|
+
breakpoint_index = check_breakpoints_by_method(debug_context, klass, mid);
|
593
743
|
if(breakpoint_index != -1)
|
594
744
|
{
|
595
|
-
|
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,
|
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("@
|
631
|
-
rb_ivar_set(ruby_errinfo, rb_intern("@
|
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
|
-
|
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->
|
787
|
-
breakpoint->
|
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->
|
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
|
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
|
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
|
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
|
-
|
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
|
-
|
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
|
}
|