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