ruby-debug 0.5.3 → 0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGES +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: []
|