ruby-debug 0.1.2
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 +4 -0
- data/LICENSE +23 -0
- data/README +63 -0
- data/Rakefile +70 -0
- data/bin/rdebug +13 -0
- data/ext/extconf.rb +18 -0
- data/ext/ruby_debug.c +922 -0
- data/lib/ruby-debug.rb +610 -0
- metadata +53 -0
data/ext/ruby_debug.c
ADDED
@@ -0,0 +1,922 @@
|
|
1
|
+
#include <stdio.h>
|
2
|
+
#include <ruby.h>
|
3
|
+
#include <node.h>
|
4
|
+
#include <rubysig.h>
|
5
|
+
#include <st.h>
|
6
|
+
|
7
|
+
#define DEBUG_VERSION "0.1.2"
|
8
|
+
|
9
|
+
typedef struct {
|
10
|
+
int thnum;
|
11
|
+
int stop_next;
|
12
|
+
int dest_frame;
|
13
|
+
int src_line;
|
14
|
+
int stop_line;
|
15
|
+
int stop_frame;
|
16
|
+
int suspend;
|
17
|
+
int tracing;
|
18
|
+
VALUE frames;
|
19
|
+
VALUE thread;
|
20
|
+
} debug_context_t;
|
21
|
+
|
22
|
+
typedef struct {
|
23
|
+
VALUE file;
|
24
|
+
VALUE line;
|
25
|
+
VALUE binding;
|
26
|
+
ID id;
|
27
|
+
} debug_frame_t;
|
28
|
+
|
29
|
+
typedef struct {
|
30
|
+
VALUE source;
|
31
|
+
VALUE pos;
|
32
|
+
VALUE disabled;
|
33
|
+
} debug_breakpoint_t;
|
34
|
+
|
35
|
+
static VALUE threads_tbl = Qnil;
|
36
|
+
static VALUE breakpoints = Qnil;
|
37
|
+
static VALUE catchpoint = Qnil;
|
38
|
+
static VALUE waiting = Qnil;
|
39
|
+
static VALUE tracing = Qfalse;
|
40
|
+
|
41
|
+
static VALUE mDebugger;
|
42
|
+
static VALUE cContext;
|
43
|
+
static VALUE cFrame;
|
44
|
+
static VALUE cBreakpoint;
|
45
|
+
|
46
|
+
static ID idDebugCommand;
|
47
|
+
static ID idAtBreakpoint;
|
48
|
+
static ID idAtCatchpoint;
|
49
|
+
static ID idAtTracing;
|
50
|
+
static ID idBinding;
|
51
|
+
|
52
|
+
static int thnum_max = 0;
|
53
|
+
static int last_debugged_thnum = 0;
|
54
|
+
|
55
|
+
static VALUE debug_suspend(VALUE);
|
56
|
+
static VALUE create_binding(VALUE);
|
57
|
+
static VALUE debug_stop(VALUE);
|
58
|
+
|
59
|
+
static VALUE
|
60
|
+
debug_is_started(VALUE self)
|
61
|
+
{
|
62
|
+
return threads_tbl != Qnil ? Qtrue : Qfalse;
|
63
|
+
}
|
64
|
+
|
65
|
+
static VALUE
|
66
|
+
debug_check_started()
|
67
|
+
{
|
68
|
+
if(threads_tbl == Qnil)
|
69
|
+
{
|
70
|
+
rb_raise(rb_eRuntimeError, "Debugger.start is not called yet.");
|
71
|
+
}
|
72
|
+
}
|
73
|
+
|
74
|
+
static void
|
75
|
+
debug_context_mark(void* data)
|
76
|
+
{
|
77
|
+
debug_context_t *debug_context = (debug_context_t *)data;
|
78
|
+
rb_gc_mark(debug_context->frames);
|
79
|
+
rb_gc_mark(debug_context->thread);
|
80
|
+
}
|
81
|
+
|
82
|
+
static VALUE
|
83
|
+
debug_context_create(VALUE thread)
|
84
|
+
{
|
85
|
+
VALUE result;
|
86
|
+
debug_context_t *debug_context;
|
87
|
+
|
88
|
+
debug_context = ALLOC(debug_context_t);
|
89
|
+
debug_context-> thnum = ++thnum_max;
|
90
|
+
debug_context->stop_next = -1;
|
91
|
+
debug_context->dest_frame = -1;
|
92
|
+
debug_context->src_line = -1;
|
93
|
+
debug_context->stop_line = -1;
|
94
|
+
debug_context->stop_frame = -1;
|
95
|
+
debug_context->suspend = 0;
|
96
|
+
debug_context->tracing = 0;
|
97
|
+
debug_context->frames = rb_ary_new();
|
98
|
+
debug_context->thread = thread;
|
99
|
+
result = Data_Wrap_Struct(cContext, debug_context_mark, xfree, debug_context);
|
100
|
+
return result;
|
101
|
+
}
|
102
|
+
|
103
|
+
static VALUE
|
104
|
+
thread_context_lookup(VALUE thread)
|
105
|
+
{
|
106
|
+
VALUE context;
|
107
|
+
|
108
|
+
debug_check_started();
|
109
|
+
|
110
|
+
context = rb_hash_aref(threads_tbl, thread);
|
111
|
+
if(context == Qnil)
|
112
|
+
{
|
113
|
+
context = debug_context_create(thread);
|
114
|
+
rb_hash_aset(threads_tbl, thread, context);
|
115
|
+
}
|
116
|
+
return context;
|
117
|
+
}
|
118
|
+
|
119
|
+
static void
|
120
|
+
debug_frame_mark(void *data)
|
121
|
+
{
|
122
|
+
debug_frame_t *debug_frame = (debug_frame_t *)data;
|
123
|
+
rb_gc_mark(debug_frame->binding);
|
124
|
+
rb_gc_mark(debug_frame->file);
|
125
|
+
rb_gc_mark(debug_frame->line);
|
126
|
+
}
|
127
|
+
|
128
|
+
static VALUE
|
129
|
+
debug_frame_create(VALUE file, VALUE line, VALUE binding, ID id)
|
130
|
+
{
|
131
|
+
VALUE result;
|
132
|
+
debug_frame_t *debug_frame;
|
133
|
+
|
134
|
+
debug_frame = ALLOC(debug_frame_t);
|
135
|
+
debug_frame->file = file;
|
136
|
+
debug_frame->line = line;
|
137
|
+
debug_frame->binding = binding;
|
138
|
+
debug_frame->id = id;
|
139
|
+
result = Data_Wrap_Struct(cFrame, debug_frame_mark, xfree, debug_frame);
|
140
|
+
|
141
|
+
return result;
|
142
|
+
}
|
143
|
+
|
144
|
+
static VALUE
|
145
|
+
call_debug_command(VALUE context, int thnum, VALUE self, ID mid, VALUE file, VALUE line)
|
146
|
+
{
|
147
|
+
VALUE id, binding;
|
148
|
+
|
149
|
+
id = mid ? ID2SYM(mid) : Qnil;
|
150
|
+
binding = self? create_binding(self) : Qnil;
|
151
|
+
|
152
|
+
last_debugged_thnum = thnum;
|
153
|
+
debug_suspend(mDebugger);
|
154
|
+
return rb_funcall(context, idDebugCommand, 4, file, line, id, binding);
|
155
|
+
}
|
156
|
+
|
157
|
+
static void
|
158
|
+
set_frame_source(debug_context_t *debug_context, VALUE file, VALUE line)
|
159
|
+
{
|
160
|
+
VALUE frame;
|
161
|
+
debug_frame_t *top_frame;
|
162
|
+
|
163
|
+
if(RARRAY(debug_context->frames)->len > 0)
|
164
|
+
{
|
165
|
+
frame = *RARRAY(debug_context->frames)->ptr;
|
166
|
+
Data_Get_Struct(frame, debug_frame_t, top_frame);
|
167
|
+
top_frame->file = file;
|
168
|
+
top_frame->line = line;
|
169
|
+
}
|
170
|
+
}
|
171
|
+
|
172
|
+
static void
|
173
|
+
save_call_frame(VALUE self, VALUE file, VALUE line, ID mid, debug_context_t *debug_context)
|
174
|
+
{
|
175
|
+
VALUE frame, binding;
|
176
|
+
|
177
|
+
binding = self? create_binding(self) : Qnil;
|
178
|
+
frame = debug_frame_create(file, line, binding, mid);
|
179
|
+
rb_ary_unshift(debug_context->frames, frame);
|
180
|
+
}
|
181
|
+
|
182
|
+
static int
|
183
|
+
check_breakpoints(VALUE context, VALUE file, VALUE klass, VALUE pos, ID id)
|
184
|
+
{
|
185
|
+
VALUE breakpoint;
|
186
|
+
debug_breakpoint_t *debug_breakpoint;
|
187
|
+
int i;
|
188
|
+
int result = 0;
|
189
|
+
|
190
|
+
if(RARRAY(breakpoints)->len == 0)
|
191
|
+
return 0;
|
192
|
+
for(i = 0; i < RARRAY(breakpoints)->len; i++)
|
193
|
+
{
|
194
|
+
breakpoint = rb_ary_entry(breakpoints, i);
|
195
|
+
Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
|
196
|
+
if(debug_breakpoint->pos != pos && !(TYPE(pos) == T_STRING &&
|
197
|
+
TYPE(debug_breakpoint->pos) == T_STRING && rb_str_cmp(debug_breakpoint->pos, pos) == 0))
|
198
|
+
continue;
|
199
|
+
if(TYPE(debug_breakpoint->source) == T_STRING &&
|
200
|
+
rb_str_cmp(debug_breakpoint->source, file) == 0 ||
|
201
|
+
klass != Qnil && debug_breakpoint->source == klass)
|
202
|
+
{
|
203
|
+
result = 1;
|
204
|
+
rb_funcall(context, idAtBreakpoint, 2, breakpoint, id ? rb_str_new2(rb_id2name(id)) : Qnil);
|
205
|
+
continue;
|
206
|
+
}
|
207
|
+
|
208
|
+
}
|
209
|
+
return result;
|
210
|
+
}
|
211
|
+
|
212
|
+
/*
|
213
|
+
* This is a NASTY HACK. For some reasons rb_f_binding is decalred
|
214
|
+
* static in eval.c
|
215
|
+
*/
|
216
|
+
static VALUE
|
217
|
+
create_binding(VALUE self)
|
218
|
+
{
|
219
|
+
typedef VALUE (*bind_func_t)(VALUE);
|
220
|
+
static bind_func_t f_binding = NULL;
|
221
|
+
|
222
|
+
if(f_binding == NULL)
|
223
|
+
{
|
224
|
+
ID idBinding = rb_intern("binding");
|
225
|
+
NODE *body, *method;
|
226
|
+
st_lookup(RCLASS(rb_mKernel)->m_tbl, idBinding, (st_data_t *)&body);
|
227
|
+
method = (NODE *)body->u2.value;
|
228
|
+
f_binding = (bind_func_t)method->u1.value;
|
229
|
+
}
|
230
|
+
return f_binding(self);
|
231
|
+
}
|
232
|
+
|
233
|
+
static void
|
234
|
+
check_suspend(debug_context_t *debug_context)
|
235
|
+
{
|
236
|
+
if(rb_thread_critical == Qtrue)
|
237
|
+
return;
|
238
|
+
while(1)
|
239
|
+
{
|
240
|
+
rb_thread_critical = Qtrue;
|
241
|
+
if(!debug_context->suspend)
|
242
|
+
break;
|
243
|
+
rb_ary_push(waiting, rb_thread_current());
|
244
|
+
debug_context->suspend = 0;
|
245
|
+
rb_thread_stop();
|
246
|
+
}
|
247
|
+
rb_thread_critical = Qfalse;
|
248
|
+
}
|
249
|
+
|
250
|
+
static void
|
251
|
+
debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
|
252
|
+
{
|
253
|
+
VALUE thread;
|
254
|
+
VALUE context;
|
255
|
+
debug_context_t *debug_context;
|
256
|
+
VALUE file = Qnil, line = Qnil;
|
257
|
+
|
258
|
+
static int debugging = 0;
|
259
|
+
|
260
|
+
if(debugging) return;
|
261
|
+
|
262
|
+
debugging++;
|
263
|
+
|
264
|
+
thread = rb_thread_current();
|
265
|
+
context = thread_context_lookup(thread);
|
266
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
267
|
+
check_suspend(debug_context);
|
268
|
+
|
269
|
+
if(node)
|
270
|
+
{
|
271
|
+
file = rb_str_new2(node->nd_file);
|
272
|
+
line = INT2FIX(nd_line(node));
|
273
|
+
}
|
274
|
+
|
275
|
+
switch(event)
|
276
|
+
{
|
277
|
+
case RUBY_EVENT_LINE:
|
278
|
+
{
|
279
|
+
set_frame_source(debug_context, file, line);
|
280
|
+
|
281
|
+
if(RTEST(tracing) || debug_context->tracing )
|
282
|
+
{
|
283
|
+
rb_funcall(context, idAtTracing, 2, file, line);
|
284
|
+
}
|
285
|
+
|
286
|
+
if(debug_context->dest_frame == -1 ||
|
287
|
+
RARRAY(debug_context->frames)->len == debug_context->dest_frame)
|
288
|
+
{
|
289
|
+
debug_context->stop_next--;
|
290
|
+
if(debug_context->stop_next < 0)
|
291
|
+
debug_context->stop_next = -1;
|
292
|
+
/* we check that we actualy moved to another line */
|
293
|
+
if(line != Qnil && debug_context->src_line != FIX2INT(line))
|
294
|
+
{
|
295
|
+
debug_context->stop_line--;
|
296
|
+
}
|
297
|
+
}
|
298
|
+
else if(RARRAY(debug_context->frames)->len < debug_context->dest_frame)
|
299
|
+
{
|
300
|
+
debug_context->stop_next = 0;
|
301
|
+
}
|
302
|
+
|
303
|
+
if(RARRAY(debug_context->frames)->len == 0)
|
304
|
+
{
|
305
|
+
save_call_frame(self, file, line, mid, debug_context);
|
306
|
+
}
|
307
|
+
|
308
|
+
if(debug_context->stop_next == 0 || debug_context->stop_line == 0 ||
|
309
|
+
check_breakpoints(context, file, klass, line, mid))
|
310
|
+
{
|
311
|
+
/* reset all pointers */
|
312
|
+
debug_context->dest_frame = -1;
|
313
|
+
debug_context->src_line = -1;
|
314
|
+
debug_context->stop_line = -1;
|
315
|
+
debug_context->stop_next = -1;
|
316
|
+
|
317
|
+
call_debug_command(context, debug_context->thnum, self, mid, file, line);
|
318
|
+
}
|
319
|
+
break;
|
320
|
+
}
|
321
|
+
case RUBY_EVENT_C_CALL:
|
322
|
+
{
|
323
|
+
if(node)
|
324
|
+
{
|
325
|
+
set_frame_source(debug_context, file, line);
|
326
|
+
}
|
327
|
+
break;
|
328
|
+
}
|
329
|
+
case RUBY_EVENT_CALL:
|
330
|
+
{
|
331
|
+
save_call_frame(self, file, line, mid, debug_context);
|
332
|
+
if(check_breakpoints(context, file, klass, rb_str_new2(rb_id2name(mid)), mid))
|
333
|
+
{
|
334
|
+
call_debug_command(context, debug_context->thnum, self, mid, file, line);
|
335
|
+
}
|
336
|
+
break;
|
337
|
+
}
|
338
|
+
case RUBY_EVENT_RETURN:
|
339
|
+
case RUBY_EVENT_END:
|
340
|
+
{
|
341
|
+
if(RARRAY(debug_context->frames)->len == debug_context->stop_frame)
|
342
|
+
{
|
343
|
+
debug_context->stop_next = 1;
|
344
|
+
debug_context->stop_frame = 0;
|
345
|
+
}
|
346
|
+
rb_ary_shift(debug_context->frames);
|
347
|
+
break;
|
348
|
+
}
|
349
|
+
case RUBY_EVENT_CLASS:
|
350
|
+
{
|
351
|
+
save_call_frame(self, file, line, mid, debug_context);
|
352
|
+
break;
|
353
|
+
}
|
354
|
+
case RUBY_EVENT_RAISE:
|
355
|
+
{
|
356
|
+
VALUE ancestors;
|
357
|
+
VALUE expn_class, aclass;
|
358
|
+
int i, found = 0;
|
359
|
+
|
360
|
+
expn_class = rb_obj_class(ruby_errinfo);
|
361
|
+
if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) )
|
362
|
+
{
|
363
|
+
debug_stop(mDebugger);
|
364
|
+
rb_exit(0);
|
365
|
+
}
|
366
|
+
|
367
|
+
if(catchpoint == Qnil)
|
368
|
+
break;
|
369
|
+
|
370
|
+
ancestors = rb_mod_ancestors(expn_class);
|
371
|
+
for(i = 0; i < RARRAY(ancestors)->len; i++)
|
372
|
+
{
|
373
|
+
aclass = rb_ary_entry(ancestors, i);
|
374
|
+
if(rb_str_cmp(rb_mod_name(aclass), catchpoint) == 0)
|
375
|
+
{
|
376
|
+
rb_funcall(context, idAtCatchpoint, 0);
|
377
|
+
call_debug_command(context, debug_context->thnum, self, mid, file, line);
|
378
|
+
break;
|
379
|
+
}
|
380
|
+
}
|
381
|
+
|
382
|
+
break;
|
383
|
+
}
|
384
|
+
case RUBY_EVENT_C_RETURN:
|
385
|
+
{
|
386
|
+
break;
|
387
|
+
}
|
388
|
+
}
|
389
|
+
|
390
|
+
debugging--;
|
391
|
+
}
|
392
|
+
|
393
|
+
static VALUE
|
394
|
+
debug_thnum(VALUE self)
|
395
|
+
{
|
396
|
+
VALUE thread, context;
|
397
|
+
debug_context_t *debug_context;
|
398
|
+
|
399
|
+
thread = rb_thread_current();
|
400
|
+
context = thread_context_lookup(thread);
|
401
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
402
|
+
return INT2FIX(debug_context->thnum);
|
403
|
+
}
|
404
|
+
|
405
|
+
static VALUE
|
406
|
+
debug_start(VALUE self)
|
407
|
+
{
|
408
|
+
threads_tbl = rb_hash_new();
|
409
|
+
breakpoints = rb_ary_new();
|
410
|
+
waiting = rb_ary_new();
|
411
|
+
|
412
|
+
rb_add_event_hook(debug_event_hook,
|
413
|
+
RUBY_EVENT_LINE | RUBY_EVENT_C_CALL | RUBY_EVENT_C_RETURN |
|
414
|
+
RUBY_EVENT_CALL | RUBY_EVENT_RETURN | RUBY_EVENT_CLASS |
|
415
|
+
RUBY_EVENT_END | RUBY_EVENT_RAISE
|
416
|
+
);
|
417
|
+
return Qnil;
|
418
|
+
}
|
419
|
+
|
420
|
+
static VALUE
|
421
|
+
debug_stop(VALUE self)
|
422
|
+
{
|
423
|
+
debug_check_started();
|
424
|
+
rb_remove_event_hook(debug_event_hook);
|
425
|
+
|
426
|
+
waiting = Qnil;
|
427
|
+
breakpoints = Qnil;
|
428
|
+
threads_tbl = Qnil;
|
429
|
+
|
430
|
+
return Qnil;
|
431
|
+
}
|
432
|
+
|
433
|
+
static void
|
434
|
+
breakpoint_mark(void *data)
|
435
|
+
{
|
436
|
+
debug_breakpoint_t *breakpoint;
|
437
|
+
breakpoint = (debug_breakpoint_t *)data;
|
438
|
+
rb_gc_mark(breakpoint->source);
|
439
|
+
rb_gc_mark(breakpoint->pos);
|
440
|
+
}
|
441
|
+
|
442
|
+
static VALUE
|
443
|
+
debug_add_breakpoint(VALUE self, VALUE source, VALUE pos)
|
444
|
+
{
|
445
|
+
VALUE result;
|
446
|
+
debug_breakpoint_t *breakpoint;
|
447
|
+
|
448
|
+
debug_check_started();
|
449
|
+
|
450
|
+
breakpoint = ALLOC(debug_breakpoint_t);
|
451
|
+
breakpoint->source = source;
|
452
|
+
breakpoint->pos = pos;
|
453
|
+
breakpoint->disabled = Qfalse;
|
454
|
+
result = Data_Wrap_Struct(cBreakpoint, breakpoint_mark, xfree, breakpoint);
|
455
|
+
rb_ary_push(breakpoints, result);
|
456
|
+
return result;
|
457
|
+
}
|
458
|
+
|
459
|
+
static VALUE
|
460
|
+
debug_breakpoints(VALUE self)
|
461
|
+
{
|
462
|
+
debug_check_started();
|
463
|
+
|
464
|
+
return breakpoints;
|
465
|
+
}
|
466
|
+
|
467
|
+
static VALUE
|
468
|
+
debug_catchpoint(VALUE self)
|
469
|
+
{
|
470
|
+
debug_check_started();
|
471
|
+
|
472
|
+
return catchpoint;
|
473
|
+
}
|
474
|
+
|
475
|
+
static VALUE
|
476
|
+
debug_set_catchpoint(VALUE self, VALUE value)
|
477
|
+
{
|
478
|
+
debug_check_started();
|
479
|
+
|
480
|
+
if (!NIL_P(value) && TYPE(value) != T_STRING) {
|
481
|
+
rb_raise(rb_eTypeError, "value of checkpoint must be String");
|
482
|
+
}
|
483
|
+
if(NIL_P(value))
|
484
|
+
catchpoint = Qnil;
|
485
|
+
else
|
486
|
+
catchpoint = rb_str_dup(value);
|
487
|
+
return value;
|
488
|
+
}
|
489
|
+
|
490
|
+
static VALUE
|
491
|
+
debug_interrupt(VALUE self)
|
492
|
+
{
|
493
|
+
VALUE thread, context;
|
494
|
+
debug_context_t *debug_context;
|
495
|
+
|
496
|
+
debug_check_started();
|
497
|
+
|
498
|
+
thread = rb_thread_current();
|
499
|
+
context = thread_context_lookup(thread);
|
500
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
501
|
+
debug_context->stop_next = 1;
|
502
|
+
|
503
|
+
return Qnil;
|
504
|
+
}
|
505
|
+
|
506
|
+
static int
|
507
|
+
find_last_context_func(VALUE key, VALUE value, VALUE *result)
|
508
|
+
{
|
509
|
+
debug_context_t *debug_context;
|
510
|
+
Data_Get_Struct(value, debug_context_t, debug_context);
|
511
|
+
if(debug_context->thnum == last_debugged_thnum)
|
512
|
+
{
|
513
|
+
*result = value;
|
514
|
+
return ST_STOP;
|
515
|
+
}
|
516
|
+
return ST_CONTINUE;
|
517
|
+
}
|
518
|
+
|
519
|
+
static VALUE
|
520
|
+
find_last_context()
|
521
|
+
{
|
522
|
+
VALUE result = Qnil;
|
523
|
+
rb_hash_foreach(threads_tbl, find_last_context_func, (st_data_t)&result);
|
524
|
+
return result;
|
525
|
+
}
|
526
|
+
|
527
|
+
static VALUE
|
528
|
+
debug_interrupt_last(VALUE self)
|
529
|
+
{
|
530
|
+
VALUE thread, context = Qnil;
|
531
|
+
debug_context_t *debug_context;
|
532
|
+
|
533
|
+
debug_check_started();
|
534
|
+
|
535
|
+
context = find_last_context();
|
536
|
+
if(context != Qnil)
|
537
|
+
{
|
538
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
539
|
+
debug_context->stop_next = 1;
|
540
|
+
}
|
541
|
+
|
542
|
+
return Qnil;
|
543
|
+
}
|
544
|
+
|
545
|
+
static VALUE
|
546
|
+
debug_current_context(VALUE self)
|
547
|
+
{
|
548
|
+
VALUE thread, context;
|
549
|
+
|
550
|
+
debug_check_started();
|
551
|
+
|
552
|
+
thread = rb_thread_current();
|
553
|
+
context = thread_context_lookup(thread);
|
554
|
+
|
555
|
+
return context;
|
556
|
+
}
|
557
|
+
|
558
|
+
static VALUE
|
559
|
+
debug_contexts(VALUE self)
|
560
|
+
{
|
561
|
+
volatile VALUE list;
|
562
|
+
volatile VALUE new_list;
|
563
|
+
VALUE thread, context;
|
564
|
+
debug_context_t *debug_context;
|
565
|
+
int i;
|
566
|
+
|
567
|
+
debug_check_started();
|
568
|
+
|
569
|
+
new_list = rb_ary_new();
|
570
|
+
list = rb_funcall(rb_cThread, rb_intern("list"), 0);
|
571
|
+
for(i = 0; i < RARRAY(list)->len; i++)
|
572
|
+
{
|
573
|
+
thread = rb_ary_entry(list, i);
|
574
|
+
context = thread_context_lookup(thread);
|
575
|
+
rb_ary_push(new_list, context);
|
576
|
+
}
|
577
|
+
/*
|
578
|
+
* I wonder why rb_hash_clear is declared static?
|
579
|
+
*/
|
580
|
+
rb_funcall(threads_tbl, rb_intern("clear"), 0);
|
581
|
+
for(i = 0; i < RARRAY(new_list)->len; i++)
|
582
|
+
{
|
583
|
+
context = rb_ary_entry(new_list, i);
|
584
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
585
|
+
rb_hash_aset(threads_tbl, debug_context->thread, context);
|
586
|
+
}
|
587
|
+
|
588
|
+
return new_list;
|
589
|
+
}
|
590
|
+
|
591
|
+
static VALUE
|
592
|
+
debug_suspend(VALUE self)
|
593
|
+
{
|
594
|
+
VALUE current, context;
|
595
|
+
VALUE saved_crit;
|
596
|
+
VALUE context_list;
|
597
|
+
debug_context_t *debug_context;
|
598
|
+
int i;
|
599
|
+
|
600
|
+
debug_check_started();
|
601
|
+
|
602
|
+
saved_crit = rb_thread_critical;
|
603
|
+
rb_thread_critical = Qtrue;
|
604
|
+
context_list = debug_contexts(self);
|
605
|
+
current = thread_context_lookup(rb_thread_current());
|
606
|
+
|
607
|
+
for(i = 0; i < RARRAY(context_list)->len; i++)
|
608
|
+
{
|
609
|
+
context = rb_ary_entry(context_list, i);
|
610
|
+
if(current == context)
|
611
|
+
continue;
|
612
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
613
|
+
debug_context->suspend = 1;
|
614
|
+
}
|
615
|
+
rb_thread_critical = saved_crit;
|
616
|
+
|
617
|
+
if(rb_thread_critical == Qfalse)
|
618
|
+
rb_thread_schedule();
|
619
|
+
|
620
|
+
return context;
|
621
|
+
}
|
622
|
+
|
623
|
+
static VALUE
|
624
|
+
debug_resume(VALUE self)
|
625
|
+
{
|
626
|
+
VALUE current, context;
|
627
|
+
VALUE saved_crit;
|
628
|
+
VALUE thread;
|
629
|
+
VALUE context_list;
|
630
|
+
debug_context_t *debug_context;
|
631
|
+
int i;
|
632
|
+
|
633
|
+
debug_check_started();
|
634
|
+
|
635
|
+
saved_crit = rb_thread_critical;
|
636
|
+
rb_thread_critical = Qtrue;
|
637
|
+
context_list = debug_contexts(self);
|
638
|
+
|
639
|
+
current = thread_context_lookup(rb_thread_current());
|
640
|
+
for(i = 0; i < RARRAY(context_list)->len; i++)
|
641
|
+
{
|
642
|
+
context = rb_ary_entry(context_list, i);
|
643
|
+
if(current == context)
|
644
|
+
continue;
|
645
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
646
|
+
debug_context->suspend = 0;
|
647
|
+
}
|
648
|
+
for(i = 0; i < RARRAY(waiting)->len; i++)
|
649
|
+
{
|
650
|
+
thread = rb_ary_entry(waiting, i);
|
651
|
+
rb_thread_run(thread);
|
652
|
+
}
|
653
|
+
rb_ary_clear(waiting);
|
654
|
+
rb_thread_critical = saved_crit;
|
655
|
+
|
656
|
+
rb_thread_schedule();
|
657
|
+
|
658
|
+
return context;
|
659
|
+
}
|
660
|
+
|
661
|
+
static VALUE
|
662
|
+
debug_tracing(VALUE self)
|
663
|
+
{
|
664
|
+
return tracing;
|
665
|
+
}
|
666
|
+
|
667
|
+
static VALUE
|
668
|
+
debug_set_tracing(VALUE self, VALUE value)
|
669
|
+
{
|
670
|
+
tracing = RTEST(value) ? Qtrue : Qfalse;
|
671
|
+
return value;
|
672
|
+
}
|
673
|
+
|
674
|
+
static VALUE
|
675
|
+
context_stop_next(VALUE self, VALUE steps)
|
676
|
+
{
|
677
|
+
debug_context_t *debug_context;
|
678
|
+
|
679
|
+
debug_check_started();
|
680
|
+
|
681
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
682
|
+
if(FIX2INT(steps) < 0)
|
683
|
+
rb_raise(rb_eRuntimeError, "Steps argument can't be negative.");
|
684
|
+
debug_context->stop_next = FIX2INT(steps);
|
685
|
+
|
686
|
+
return steps;
|
687
|
+
}
|
688
|
+
|
689
|
+
static VALUE
|
690
|
+
context_step_over(int argc, VALUE *argv, VALUE self)
|
691
|
+
{
|
692
|
+
VALUE lines, frame, cur_frame;
|
693
|
+
debug_context_t *debug_context;
|
694
|
+
debug_frame_t *debug_frame;
|
695
|
+
|
696
|
+
debug_check_started();
|
697
|
+
|
698
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
699
|
+
if(RARRAY(debug_context->frames)->len == 0)
|
700
|
+
rb_raise(rb_eRuntimeError, "No frames collected.");
|
701
|
+
|
702
|
+
rb_scan_args(argc, argv, "11", &lines, &frame);
|
703
|
+
debug_context->stop_line = FIX2INT(lines);
|
704
|
+
if(argc == 1)
|
705
|
+
{
|
706
|
+
debug_context->dest_frame = RARRAY(debug_context->frames)->len;
|
707
|
+
}
|
708
|
+
else
|
709
|
+
{
|
710
|
+
if(FIX2INT(frame) < 0 && FIX2INT(frame) >= RARRAY(debug_context->frames)->len)
|
711
|
+
rb_raise(rb_eRuntimeError, "Destination frame is out of range.");
|
712
|
+
debug_context->dest_frame = FIX2INT(frame);
|
713
|
+
}
|
714
|
+
|
715
|
+
cur_frame = *RARRAY(debug_context->frames)->ptr;
|
716
|
+
Data_Get_Struct(cur_frame, debug_frame_t, debug_frame);
|
717
|
+
debug_context->src_line = FIX2INT(debug_frame->line);
|
718
|
+
|
719
|
+
return Qnil;
|
720
|
+
}
|
721
|
+
|
722
|
+
static VALUE
|
723
|
+
context_stop_frame(VALUE self, VALUE frame)
|
724
|
+
{
|
725
|
+
debug_context_t *debug_context;
|
726
|
+
|
727
|
+
debug_check_started();
|
728
|
+
|
729
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
730
|
+
if(FIX2INT(frame) < 0 && FIX2INT(frame) >= RARRAY(debug_context->frames)->len)
|
731
|
+
rb_raise(rb_eRuntimeError, "Stop frame is out of range.");
|
732
|
+
debug_context->stop_frame = FIX2INT(frame);
|
733
|
+
|
734
|
+
return frame;
|
735
|
+
}
|
736
|
+
|
737
|
+
static VALUE
|
738
|
+
context_frames(VALUE self)
|
739
|
+
{
|
740
|
+
debug_context_t *debug_context;
|
741
|
+
|
742
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
743
|
+
return debug_context->frames;
|
744
|
+
}
|
745
|
+
|
746
|
+
static VALUE
|
747
|
+
context_thread(VALUE self)
|
748
|
+
{
|
749
|
+
debug_context_t *debug_context;
|
750
|
+
|
751
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
752
|
+
return debug_context->thread;
|
753
|
+
}
|
754
|
+
|
755
|
+
static VALUE
|
756
|
+
context_thnum(VALUE self)
|
757
|
+
{
|
758
|
+
debug_context_t *debug_context;
|
759
|
+
|
760
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
761
|
+
return INT2FIX(debug_context->thnum);
|
762
|
+
}
|
763
|
+
|
764
|
+
static VALUE
|
765
|
+
context_set_suspend(VALUE self)
|
766
|
+
{
|
767
|
+
debug_context_t *debug_context;
|
768
|
+
|
769
|
+
debug_check_started();
|
770
|
+
|
771
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
772
|
+
debug_context->suspend = 1;
|
773
|
+
return Qnil;
|
774
|
+
}
|
775
|
+
|
776
|
+
static VALUE
|
777
|
+
context_clear_suspend(VALUE self)
|
778
|
+
{
|
779
|
+
debug_context_t *debug_context;
|
780
|
+
|
781
|
+
debug_check_started();
|
782
|
+
|
783
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
784
|
+
debug_context->suspend = 0;
|
785
|
+
return Qnil;
|
786
|
+
}
|
787
|
+
|
788
|
+
static VALUE
|
789
|
+
context_tracing(VALUE self)
|
790
|
+
{
|
791
|
+
debug_context_t *debug_context;
|
792
|
+
|
793
|
+
debug_check_started();
|
794
|
+
|
795
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
796
|
+
return debug_context->tracing ? Qtrue : Qfalse;
|
797
|
+
}
|
798
|
+
|
799
|
+
static VALUE
|
800
|
+
context_set_tracing(VALUE self, VALUE value)
|
801
|
+
{
|
802
|
+
debug_context_t *debug_context;
|
803
|
+
|
804
|
+
debug_check_started();
|
805
|
+
|
806
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
807
|
+
debug_context->tracing = RTEST(value) ? 1 : 0;
|
808
|
+
return value;
|
809
|
+
}
|
810
|
+
|
811
|
+
static VALUE
|
812
|
+
frame_file(VALUE self)
|
813
|
+
{
|
814
|
+
debug_frame_t *debug_frame;
|
815
|
+
|
816
|
+
Data_Get_Struct(self, debug_frame_t, debug_frame);
|
817
|
+
return debug_frame->file;
|
818
|
+
}
|
819
|
+
|
820
|
+
static VALUE
|
821
|
+
frame_line(VALUE self)
|
822
|
+
{
|
823
|
+
debug_frame_t *debug_frame;
|
824
|
+
|
825
|
+
Data_Get_Struct(self, debug_frame_t, debug_frame);
|
826
|
+
return debug_frame->line;
|
827
|
+
}
|
828
|
+
|
829
|
+
static VALUE
|
830
|
+
frame_binding(VALUE self)
|
831
|
+
{
|
832
|
+
debug_frame_t *debug_frame;
|
833
|
+
|
834
|
+
Data_Get_Struct(self, debug_frame_t, debug_frame);
|
835
|
+
return debug_frame->binding;
|
836
|
+
}
|
837
|
+
|
838
|
+
static VALUE
|
839
|
+
frame_id(VALUE self)
|
840
|
+
{
|
841
|
+
debug_frame_t *debug_frame;
|
842
|
+
|
843
|
+
Data_Get_Struct(self, debug_frame_t, debug_frame);
|
844
|
+
return debug_frame->id ? ID2SYM(debug_frame->id): Qnil;
|
845
|
+
}
|
846
|
+
|
847
|
+
static VALUE
|
848
|
+
breakpoint_source(VALUE self)
|
849
|
+
{
|
850
|
+
debug_breakpoint_t *breakpoint;
|
851
|
+
|
852
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
853
|
+
return breakpoint->source;
|
854
|
+
}
|
855
|
+
|
856
|
+
static VALUE
|
857
|
+
breakpoint_pos(VALUE self)
|
858
|
+
{
|
859
|
+
debug_breakpoint_t *breakpoint;
|
860
|
+
|
861
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
862
|
+
return breakpoint->pos;
|
863
|
+
}
|
864
|
+
|
865
|
+
#if defined(_WIN32)
|
866
|
+
__declspec(dllexport)
|
867
|
+
#endif
|
868
|
+
void
|
869
|
+
Init_ruby_debug()
|
870
|
+
{
|
871
|
+
mDebugger = rb_define_module("Debugger");
|
872
|
+
rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION));
|
873
|
+
rb_define_module_function(mDebugger, "start", debug_start, 0);
|
874
|
+
rb_define_module_function(mDebugger, "stop", debug_stop, 0);
|
875
|
+
rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
|
876
|
+
rb_define_module_function(mDebugger, "thnum", debug_thnum, 0);
|
877
|
+
rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
|
878
|
+
rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, 2);
|
879
|
+
rb_define_module_function(mDebugger, "catchpoint", debug_catchpoint, 0);
|
880
|
+
rb_define_module_function(mDebugger, "catchpoint=", debug_set_catchpoint, 1);
|
881
|
+
rb_define_module_function(mDebugger, "interrupt", debug_interrupt, 0);
|
882
|
+
rb_define_module_function(mDebugger, "interrupt_last", debug_interrupt_last, 0);
|
883
|
+
rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
|
884
|
+
rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
|
885
|
+
rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
|
886
|
+
rb_define_module_function(mDebugger, "resume", debug_resume, 0);
|
887
|
+
rb_define_module_function(mDebugger, "tracing", debug_tracing, 0);
|
888
|
+
rb_define_module_function(mDebugger, "tracing=", debug_set_tracing, 1);
|
889
|
+
|
890
|
+
cContext = rb_define_class_under(mDebugger, "Context", rb_cObject);
|
891
|
+
rb_define_method(cContext, "stop_next=", context_stop_next, 1);
|
892
|
+
rb_define_method(cContext, "step_over", context_step_over, -1);
|
893
|
+
rb_define_method(cContext, "stop_frame=", context_stop_frame, 1);
|
894
|
+
rb_define_method(cContext, "frames", context_frames, 0);
|
895
|
+
rb_define_method(cContext, "thread", context_thread, 0);
|
896
|
+
rb_define_method(cContext, "thnum", context_thnum, 0);
|
897
|
+
rb_define_method(cContext, "set_suspend", context_set_suspend, 0);
|
898
|
+
rb_define_method(cContext, "clear_suspend", context_clear_suspend, 0);
|
899
|
+
rb_define_method(cContext, "tracing", context_tracing, 0);
|
900
|
+
rb_define_method(cContext, "tracing=", context_set_tracing, 1);
|
901
|
+
|
902
|
+
cFrame = rb_define_class_under(cContext, "Frame", rb_cObject);
|
903
|
+
rb_define_method(cFrame, "file", frame_file, 0);
|
904
|
+
rb_define_method(cFrame, "line", frame_line, 0);
|
905
|
+
rb_define_method(cFrame, "binding", frame_binding, 0);
|
906
|
+
rb_define_method(cFrame, "id", frame_id, 0);
|
907
|
+
|
908
|
+
cBreakpoint = rb_define_class_under(mDebugger, "Breakpoint", rb_cObject);
|
909
|
+
rb_define_method(cBreakpoint, "source", breakpoint_source, 0);
|
910
|
+
rb_define_method(cBreakpoint, "pos", breakpoint_pos, 0);
|
911
|
+
|
912
|
+
idDebugCommand = rb_intern("debug_command");
|
913
|
+
idAtBreakpoint = rb_intern("at_breakpoint");
|
914
|
+
idAtCatchpoint = rb_intern("at_catchpoint");
|
915
|
+
idAtTracing = rb_intern("at_tracing");
|
916
|
+
idBinding = rb_intern("binding");
|
917
|
+
|
918
|
+
rb_global_variable(&threads_tbl);
|
919
|
+
rb_global_variable(&breakpoints);
|
920
|
+
rb_global_variable(&catchpoint);
|
921
|
+
rb_global_variable(&waiting);
|
922
|
+
}
|