ruby-debug 0.5.2-mswin32 → 0.6-mswin32
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 +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
|
}
|