debase 0.0.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.
- checksums.yaml +7 -0
- data/.gitignore +8 -0
- data/.travis.yml +4 -0
- data/Gemfile +4 -0
- data/LICENSE +23 -0
- data/MIT_LICENSE +22 -0
- data/README +22 -0
- data/Rakefile +34 -0
- data/debase.gemspec +30 -0
- data/ext/breakpoint.c +233 -0
- data/ext/context.c +377 -0
- data/ext/debase_internals.c +427 -0
- data/ext/debase_internals.h +94 -0
- data/ext/extconf.rb +14 -0
- data/ext/locker.c +52 -0
- data/lib/debase.rb +57 -0
- data/lib/debase/context.rb +44 -0
- data/lib/debase/version.rb +3 -0
- data/test/example/a/example.rb +1 -0
- data/test/example/at-exit.rb +3 -0
- data/test/example/b/example.rb +1 -0
- data/test/example/bp_loop_issue.rb +3 -0
- data/test/example/breakpoints-basename.rb +2 -0
- data/test/example/brkpt-class-bug.rb +8 -0
- data/test/example/classes.rb +11 -0
- data/test/example/dollar-0.rb +6 -0
- data/test/example/except-bug1.rb +4 -0
- data/test/example/file with space.rb +1 -0
- data/test/example/gcd.rb +18 -0
- data/test/example/info-var-bug.rb +47 -0
- data/test/example/info-var-bug2.rb +2 -0
- data/test/example/null.rb +1 -0
- data/test/example/output.rb +2 -0
- data/test/example/pm-bug.rb +3 -0
- data/test/example/pm.rb +11 -0
- data/test/example/raise.rb +3 -0
- data/test/helper.rb +2 -0
- data/test/test_base.rb +77 -0
- data/test/test_breakpoints.rb +14 -0
- data/test/test_catchpoint.rb +19 -0
- data/test/test_load.rb +44 -0
- data/test/test_reload_bug.rb +8 -0
- metadata +142 -0
@@ -0,0 +1,427 @@
|
|
1
|
+
#include <debase_internals.h>
|
2
|
+
|
3
|
+
static VALUE mDebase; /* Ruby Debase Module object */
|
4
|
+
static VALUE cContext;
|
5
|
+
static VALUE cDebugThread;
|
6
|
+
|
7
|
+
static VALUE debug = Qfalse;
|
8
|
+
static VALUE locker = Qnil;
|
9
|
+
static VALUE contexts;
|
10
|
+
static VALUE catchpoints;
|
11
|
+
static VALUE breakpoints;
|
12
|
+
|
13
|
+
static VALUE tpLine;
|
14
|
+
static VALUE tpCall;
|
15
|
+
static VALUE tpReturn;
|
16
|
+
static VALUE tpRaise;
|
17
|
+
|
18
|
+
static VALUE idAlive;
|
19
|
+
static VALUE idAtLine;
|
20
|
+
static VALUE idAtBreakpoint;
|
21
|
+
static VALUE idAtCatchpoint;
|
22
|
+
|
23
|
+
static VALUE
|
24
|
+
Debase_thread_context(VALUE self, VALUE thread)
|
25
|
+
{
|
26
|
+
VALUE context;
|
27
|
+
|
28
|
+
context = rb_hash_aref(contexts, thread);
|
29
|
+
if (context == Qnil) {
|
30
|
+
context = context_create(thread, cDebugThread);
|
31
|
+
rb_hash_aset(contexts, thread, context);
|
32
|
+
}
|
33
|
+
return context;
|
34
|
+
}
|
35
|
+
|
36
|
+
static VALUE
|
37
|
+
Debase_current_context(VALUE self)
|
38
|
+
{
|
39
|
+
return Debase_thread_context(self, rb_thread_current());
|
40
|
+
}
|
41
|
+
|
42
|
+
static int
|
43
|
+
remove_dead_threads(VALUE thread, VALUE context, VALUE ignored)
|
44
|
+
{
|
45
|
+
return (IS_THREAD_ALIVE(thread)) ? ST_CONTINUE : ST_DELETE;
|
46
|
+
}
|
47
|
+
|
48
|
+
static void
|
49
|
+
cleanup(debug_context_t *context)
|
50
|
+
{
|
51
|
+
VALUE thread;
|
52
|
+
|
53
|
+
context->stop_reason = CTX_STOP_NONE;
|
54
|
+
|
55
|
+
/* release a lock */
|
56
|
+
locker = Qnil;
|
57
|
+
|
58
|
+
/* let the next thread to run */
|
59
|
+
thread = remove_from_locked();
|
60
|
+
if(thread != Qnil)
|
61
|
+
rb_thread_run(thread);
|
62
|
+
}
|
63
|
+
|
64
|
+
static int
|
65
|
+
check_start_processing(debug_context_t *context, VALUE thread)
|
66
|
+
{
|
67
|
+
/* return if thread is marked as 'ignored'.
|
68
|
+
debugger's threads are marked this way
|
69
|
+
*/
|
70
|
+
if(CTX_FL_TEST(context, CTX_FL_IGNORE)) return 0;
|
71
|
+
|
72
|
+
while(1)
|
73
|
+
{
|
74
|
+
/* halt execution of the current thread if the debugger
|
75
|
+
is activated in another
|
76
|
+
*/
|
77
|
+
while(locker != Qnil && locker != thread)
|
78
|
+
{
|
79
|
+
add_to_locked(thread);
|
80
|
+
rb_thread_stop();
|
81
|
+
}
|
82
|
+
|
83
|
+
/* stop the current thread if it's marked as suspended */
|
84
|
+
if(CTX_FL_TEST(context, CTX_FL_SUSPEND) && locker != thread)
|
85
|
+
{
|
86
|
+
CTX_FL_SET(context, CTX_FL_WAS_RUNNING);
|
87
|
+
rb_thread_stop();
|
88
|
+
}
|
89
|
+
else break;
|
90
|
+
}
|
91
|
+
|
92
|
+
/* return if the current thread is the locker */
|
93
|
+
if(locker != Qnil) return 0;
|
94
|
+
|
95
|
+
/* only the current thread can proceed */
|
96
|
+
locker = thread;
|
97
|
+
|
98
|
+
/* ignore a skipped section of code */
|
99
|
+
if(CTX_FL_TEST(context, CTX_FL_SKIPPED)) {
|
100
|
+
cleanup(context);
|
101
|
+
return 0;
|
102
|
+
}
|
103
|
+
return 1;
|
104
|
+
}
|
105
|
+
|
106
|
+
static inline void
|
107
|
+
load_frame_info(VALUE trace_point, VALUE *path, VALUE *lineno, VALUE *binding, VALUE *self)
|
108
|
+
{
|
109
|
+
rb_trace_point_t *tp;
|
110
|
+
|
111
|
+
tp = rb_tracearg_from_tracepoint(trace_point);
|
112
|
+
|
113
|
+
*path = rb_tracearg_path(tp);
|
114
|
+
*lineno = rb_tracearg_lineno(tp);
|
115
|
+
*binding = rb_tracearg_binding(tp);
|
116
|
+
*self = rb_tracearg_self(tp);
|
117
|
+
}
|
118
|
+
|
119
|
+
static void
|
120
|
+
call_at_line(debug_context_t *context, char *file, int line, VALUE context_object, VALUE path, VALUE lineno)
|
121
|
+
{
|
122
|
+
CTX_FL_UNSET(context, CTX_FL_STEPPED);
|
123
|
+
CTX_FL_UNSET(context, CTX_FL_FORCE_MOVE);
|
124
|
+
context->last_file = file;
|
125
|
+
context->last_line = line;
|
126
|
+
rb_funcall(context_object, idAtLine, 2, path, lineno);
|
127
|
+
}
|
128
|
+
|
129
|
+
static void
|
130
|
+
process_line_event(VALUE trace_point, void *data)
|
131
|
+
{
|
132
|
+
VALUE path;
|
133
|
+
VALUE lineno;
|
134
|
+
VALUE binding;
|
135
|
+
VALUE self;
|
136
|
+
VALUE context_object;
|
137
|
+
VALUE breakpoint;
|
138
|
+
debug_context_t *context;
|
139
|
+
char *file;
|
140
|
+
int line;
|
141
|
+
int moved;
|
142
|
+
|
143
|
+
context_object = Debase_current_context(mDebase);
|
144
|
+
Data_Get_Struct(context_object, debug_context_t, context);
|
145
|
+
if (!check_start_processing(context, rb_thread_current())) return;
|
146
|
+
|
147
|
+
load_frame_info(trace_point, &path, &lineno, &binding, &self);
|
148
|
+
file = RSTRING_PTR(path);
|
149
|
+
line = FIX2INT(lineno);
|
150
|
+
update_frame(context_object, file, line, binding, self);
|
151
|
+
|
152
|
+
if (debug == Qtrue)
|
153
|
+
fprintf(stderr, "line: file=%s, line=%d, stack_size=%d\n", file, line, context->stack_size);
|
154
|
+
|
155
|
+
moved = context->last_line != line || context->last_file == NULL ||
|
156
|
+
strcmp(context->last_file, file) != 0;
|
157
|
+
|
158
|
+
if(context->dest_frame == -1 || context->stack_size == context->dest_frame)
|
159
|
+
{
|
160
|
+
if(moved || !CTX_FL_TEST(context, CTX_FL_FORCE_MOVE))
|
161
|
+
context->stop_next--;
|
162
|
+
if(context->stop_next < 0)
|
163
|
+
context->stop_next = -1;
|
164
|
+
if(moved || (CTX_FL_TEST(context, CTX_FL_STEPPED) && !CTX_FL_TEST(context, CTX_FL_FORCE_MOVE)))
|
165
|
+
{
|
166
|
+
context->stop_line--;
|
167
|
+
CTX_FL_UNSET(context, CTX_FL_STEPPED);
|
168
|
+
}
|
169
|
+
}
|
170
|
+
else if(context->stack_size < context->dest_frame)
|
171
|
+
{
|
172
|
+
context->stop_next = 0;
|
173
|
+
}
|
174
|
+
|
175
|
+
breakpoint = breakpoint_find(breakpoints, path, lineno, binding);
|
176
|
+
if(context->stop_next == 0 || context->stop_line == 0 || breakpoint != Qnil) {
|
177
|
+
context->stop_reason = CTX_STOP_STEP;
|
178
|
+
if (breakpoint != Qnil) {
|
179
|
+
rb_funcall(context_object, idAtBreakpoint, 1, breakpoint);
|
180
|
+
}
|
181
|
+
reset_stepping_stop_points(context);
|
182
|
+
call_at_line(context, file, line, context_object, path, lineno);
|
183
|
+
}
|
184
|
+
cleanup(context);
|
185
|
+
}
|
186
|
+
|
187
|
+
static void
|
188
|
+
process_return_event(VALUE trace_point, void *data)
|
189
|
+
{
|
190
|
+
VALUE path;
|
191
|
+
VALUE lineno;
|
192
|
+
VALUE binding;
|
193
|
+
VALUE self;
|
194
|
+
VALUE context_object;
|
195
|
+
debug_context_t *context;
|
196
|
+
|
197
|
+
context_object = Debase_current_context(mDebase);
|
198
|
+
Data_Get_Struct(context_object, debug_context_t, context);
|
199
|
+
if (!check_start_processing(context, rb_thread_current())) return;
|
200
|
+
|
201
|
+
if(context->stack_size == context->stop_frame)
|
202
|
+
{
|
203
|
+
context->stop_next = 1;
|
204
|
+
context->stop_frame = 0;
|
205
|
+
}
|
206
|
+
|
207
|
+
load_frame_info(trace_point, &path, &lineno, &binding, &self);
|
208
|
+
if (debug == Qtrue)
|
209
|
+
fprintf(stderr, "return: file=%s, line=%d, stack_size=%d\n", RSTRING_PTR(path), FIX2INT(lineno), context->stack_size);
|
210
|
+
// rb_funcall(context_object, idAtReturn, 2, path, lineno);
|
211
|
+
pop_frame(context_object);
|
212
|
+
cleanup(context);
|
213
|
+
}
|
214
|
+
|
215
|
+
static void
|
216
|
+
process_call_event(VALUE trace_point, void *data)
|
217
|
+
{
|
218
|
+
VALUE path;
|
219
|
+
VALUE lineno;
|
220
|
+
VALUE binding;
|
221
|
+
VALUE self;
|
222
|
+
VALUE context_object;
|
223
|
+
debug_context_t *context;
|
224
|
+
|
225
|
+
context_object = Debase_current_context(mDebase);
|
226
|
+
Data_Get_Struct(context_object, debug_context_t, context);
|
227
|
+
if (!check_start_processing(context, rb_thread_current())) return;
|
228
|
+
|
229
|
+
load_frame_info(trace_point, &path, &lineno, &binding, &self);
|
230
|
+
if (debug == Qtrue)
|
231
|
+
fprintf(stderr, "call: file=%s, line=%d, stack_size=%d\n", RSTRING_PTR(path), FIX2INT(lineno), context->stack_size);
|
232
|
+
push_frame(context_object, RSTRING_PTR(path), FIX2INT(lineno), binding, self);
|
233
|
+
cleanup(context);
|
234
|
+
}
|
235
|
+
|
236
|
+
static void
|
237
|
+
process_raise_event(VALUE trace_point, void *data)
|
238
|
+
{
|
239
|
+
VALUE path;
|
240
|
+
VALUE lineno;
|
241
|
+
VALUE binding;
|
242
|
+
VALUE self;
|
243
|
+
VALUE context_object;
|
244
|
+
VALUE hit_count;
|
245
|
+
VALUE exception_name;
|
246
|
+
debug_context_t *context;
|
247
|
+
char *file;
|
248
|
+
int line;
|
249
|
+
int c_hit_count;
|
250
|
+
|
251
|
+
context_object = Debase_current_context(mDebase);
|
252
|
+
Data_Get_Struct(context_object, debug_context_t, context);
|
253
|
+
if (!check_start_processing(context, rb_thread_current())) return;
|
254
|
+
|
255
|
+
load_frame_info(trace_point, &path, &lineno, &binding, &self);
|
256
|
+
file = RSTRING_PTR(path);
|
257
|
+
line = FIX2INT(lineno);
|
258
|
+
update_frame(context_object, file, line, binding, self);
|
259
|
+
|
260
|
+
if (catchpoint_hit_count(catchpoints, rb_errinfo(), &exception_name) != Qnil) {
|
261
|
+
/* On 64-bit systems with gcc and -O2 there seems to be
|
262
|
+
an optimization bug in running INT2FIX(FIX2INT...)..)
|
263
|
+
So we do this in two steps.
|
264
|
+
*/
|
265
|
+
c_hit_count = FIX2INT(rb_hash_aref(catchpoints, exception_name)) + 1;
|
266
|
+
hit_count = INT2FIX(c_hit_count);
|
267
|
+
rb_hash_aset(catchpoints, exception_name, hit_count);
|
268
|
+
context->stop_reason = CTX_STOP_CATCHPOINT;
|
269
|
+
rb_funcall(context_object, idAtCatchpoint, 1, rb_errinfo());
|
270
|
+
call_at_line(context, file, line, context_object, path, lineno);
|
271
|
+
}
|
272
|
+
|
273
|
+
cleanup(context);
|
274
|
+
}
|
275
|
+
|
276
|
+
static VALUE
|
277
|
+
Debase_setup_tracepoints(VALUE self)
|
278
|
+
{
|
279
|
+
if (catchpoints != Qnil) return Qnil;
|
280
|
+
contexts = rb_hash_new();
|
281
|
+
breakpoints = rb_ary_new();
|
282
|
+
catchpoints = rb_hash_new();
|
283
|
+
|
284
|
+
tpLine = rb_tracepoint_new(Qnil, RUBY_EVENT_LINE, process_line_event, NULL);
|
285
|
+
rb_tracepoint_enable(tpLine);
|
286
|
+
tpReturn = rb_tracepoint_new(Qnil, RUBY_EVENT_RETURN | RUBY_EVENT_C_RETURN | RUBY_EVENT_B_RETURN | RUBY_EVENT_CLASS | RUBY_EVENT_END,
|
287
|
+
process_return_event, NULL);
|
288
|
+
rb_tracepoint_enable(tpReturn);
|
289
|
+
tpCall = rb_tracepoint_new(Qnil, RUBY_EVENT_CALL | RUBY_EVENT_C_CALL | RUBY_EVENT_B_CALL,
|
290
|
+
process_call_event, NULL);
|
291
|
+
rb_tracepoint_enable(tpCall);
|
292
|
+
tpRaise = rb_tracepoint_new(Qnil, RUBY_EVENT_RAISE, process_raise_event, NULL);
|
293
|
+
rb_tracepoint_enable(tpRaise);
|
294
|
+
return Qnil;
|
295
|
+
}
|
296
|
+
|
297
|
+
static VALUE
|
298
|
+
Debase_remove_tracepoints(VALUE self)
|
299
|
+
{
|
300
|
+
contexts = Qnil;
|
301
|
+
breakpoints = Qnil;
|
302
|
+
catchpoints = Qnil;
|
303
|
+
|
304
|
+
if (tpLine != Qnil) rb_tracepoint_disable(tpLine);
|
305
|
+
tpLine = Qnil;
|
306
|
+
if (tpReturn != Qnil) rb_tracepoint_disable(tpReturn);
|
307
|
+
tpReturn = Qnil;
|
308
|
+
if (tpCall != Qnil) rb_tracepoint_disable(tpCall);
|
309
|
+
tpCall = Qnil;
|
310
|
+
if (tpRaise != Qnil) rb_tracepoint_disable(tpRaise);
|
311
|
+
tpRaise = Qnil;
|
312
|
+
return Qnil;
|
313
|
+
}
|
314
|
+
|
315
|
+
static VALUE
|
316
|
+
debase_prepare_context(VALUE self, VALUE file, VALUE stop)
|
317
|
+
{
|
318
|
+
VALUE context_object;
|
319
|
+
debug_context_t *context;
|
320
|
+
|
321
|
+
context_object = Debase_current_context(self);
|
322
|
+
Data_Get_Struct(context_object, debug_context_t, context);
|
323
|
+
|
324
|
+
if(RTEST(stop)) context->stop_next = 1;
|
325
|
+
ruby_script(RSTRING_PTR(file));
|
326
|
+
return self;
|
327
|
+
}
|
328
|
+
|
329
|
+
static VALUE
|
330
|
+
Debase_debug_load(int argc, VALUE *argv, VALUE self)
|
331
|
+
{
|
332
|
+
VALUE file, stop, increment_start;
|
333
|
+
int state;
|
334
|
+
|
335
|
+
if(rb_scan_args(argc, argv, "12", &file, &stop, &increment_start) == 1)
|
336
|
+
{
|
337
|
+
stop = Qfalse;
|
338
|
+
increment_start = Qtrue;
|
339
|
+
}
|
340
|
+
Debase_setup_tracepoints(self);
|
341
|
+
debase_prepare_context(self, file, stop);
|
342
|
+
rb_load_protect(file, 0, &state);
|
343
|
+
if (0 != state)
|
344
|
+
{
|
345
|
+
return rb_errinfo();
|
346
|
+
}
|
347
|
+
return Qnil;
|
348
|
+
}
|
349
|
+
|
350
|
+
static int
|
351
|
+
values_i(VALUE key, VALUE value, VALUE ary)
|
352
|
+
{
|
353
|
+
rb_ary_push(ary, value);
|
354
|
+
return ST_CONTINUE;
|
355
|
+
}
|
356
|
+
|
357
|
+
static VALUE
|
358
|
+
Debase_contexts(VALUE self)
|
359
|
+
{
|
360
|
+
VALUE ary;
|
361
|
+
|
362
|
+
ary = rb_ary_new();
|
363
|
+
/* check that all contexts point to alive threads */
|
364
|
+
rb_hash_foreach(contexts, remove_dead_threads, 0);
|
365
|
+
|
366
|
+
rb_hash_foreach(contexts, values_i, ary);
|
367
|
+
|
368
|
+
return ary;
|
369
|
+
}
|
370
|
+
|
371
|
+
static VALUE
|
372
|
+
Debase_breakpoints(VALUE self)
|
373
|
+
{
|
374
|
+
return breakpoints;
|
375
|
+
}
|
376
|
+
|
377
|
+
static VALUE
|
378
|
+
Debase_catchpoints(VALUE self)
|
379
|
+
{
|
380
|
+
if (catchpoints == Qnil)
|
381
|
+
rb_raise(rb_eRuntimeError, "Debugger.start is not called yet.");
|
382
|
+
return catchpoints;
|
383
|
+
}
|
384
|
+
|
385
|
+
static VALUE
|
386
|
+
Debase_started(VALUE self)
|
387
|
+
{
|
388
|
+
return catchpoints != Qnil ? Qtrue : Qfalse;
|
389
|
+
}
|
390
|
+
/*
|
391
|
+
* Document-class: Debase
|
392
|
+
*
|
393
|
+
* == Summary
|
394
|
+
*
|
395
|
+
* This is a singleton class allows controlling the debugger. Use it to start/stop debugger,
|
396
|
+
* set/remove breakpoints, etc.
|
397
|
+
*/
|
398
|
+
void
|
399
|
+
Init_debase_internals()
|
400
|
+
{
|
401
|
+
mDebase = rb_define_module("Debase");
|
402
|
+
rb_define_module_function(mDebase, "setup_tracepoints", Debase_setup_tracepoints, 0);
|
403
|
+
rb_define_module_function(mDebase, "remove_tracepoints", Debase_remove_tracepoints, 0);
|
404
|
+
rb_define_module_function(mDebase, "current_context", Debase_current_context, 0);
|
405
|
+
rb_define_module_function(mDebase, "debug_load", Debase_debug_load, -1);
|
406
|
+
rb_define_module_function(mDebase, "contexts", Debase_contexts, 0);
|
407
|
+
rb_define_module_function(mDebase, "breakpoints", Debase_breakpoints, 0);
|
408
|
+
rb_define_module_function(mDebase, "catchpoints", Debase_catchpoints, 0);
|
409
|
+
rb_define_module_function(mDebase, "started?", Debase_started, 0);
|
410
|
+
|
411
|
+
idAlive = rb_intern("alive?");
|
412
|
+
idAtLine = rb_intern("at_line");
|
413
|
+
idAtBreakpoint = rb_intern("at_breakpoint");
|
414
|
+
idAtCatchpoint = rb_intern("at_catchpoint");
|
415
|
+
|
416
|
+
cContext = Init_context(mDebase);
|
417
|
+
Init_breakpoint(mDebase);
|
418
|
+
cDebugThread = rb_define_class_under(mDebase, "DebugThread", rb_cThread);
|
419
|
+
contexts = Qnil;
|
420
|
+
catchpoints = Qnil;
|
421
|
+
breakpoints = Qnil;
|
422
|
+
|
423
|
+
rb_global_variable(&locker);
|
424
|
+
rb_global_variable(&breakpoints);
|
425
|
+
rb_global_variable(&catchpoints);
|
426
|
+
rb_global_variable(&contexts);
|
427
|
+
}
|
@@ -0,0 +1,94 @@
|
|
1
|
+
#ifndef DEBASE_INTERNALS
|
2
|
+
#define DEBASE_INTERNALS
|
3
|
+
|
4
|
+
#include <ruby.h>
|
5
|
+
#include <ruby/debug.h>
|
6
|
+
|
7
|
+
typedef struct rb_trace_arg_struct rb_trace_point_t;
|
8
|
+
|
9
|
+
/* Debase::Context */
|
10
|
+
/* flags */
|
11
|
+
#define CTX_FL_SUSPEND (1<<1)
|
12
|
+
#define CTX_FL_TRACING (1<<2)
|
13
|
+
#define CTX_FL_SKIPPED (1<<3)
|
14
|
+
#define CTX_FL_IGNORE (1<<4)
|
15
|
+
#define CTX_FL_DEAD (1<<5)
|
16
|
+
#define CTX_FL_WAS_RUNNING (1<<6)
|
17
|
+
#define CTX_FL_ENABLE_BKPT (1<<7)
|
18
|
+
#define CTX_FL_STEPPED (1<<8)
|
19
|
+
#define CTX_FL_FORCE_MOVE (1<<9)
|
20
|
+
#define CTX_FL_CATCHING (1<<10)
|
21
|
+
|
22
|
+
/* macro functions */
|
23
|
+
#define CTX_FL_TEST(c,f) ((c)->flags & (f))
|
24
|
+
#define CTX_FL_SET(c,f) do { (c)->flags |= (f); } while (0)
|
25
|
+
#define CTX_FL_UNSET(c,f) do { (c)->flags &= ~(f); } while (0)
|
26
|
+
|
27
|
+
#define IS_THREAD_ALIVE(t) (rb_funcall((t), idAlive, 0) == Qtrue)
|
28
|
+
|
29
|
+
/* types */
|
30
|
+
typedef enum {CTX_STOP_NONE, CTX_STOP_STEP, CTX_STOP_BREAKPOINT, CTX_STOP_CATCHPOINT} ctx_stop_reason;
|
31
|
+
|
32
|
+
typedef struct debug_frame_t
|
33
|
+
{
|
34
|
+
struct debug_frame_t *prev;
|
35
|
+
char *file;
|
36
|
+
int line;
|
37
|
+
VALUE binding;
|
38
|
+
VALUE self;
|
39
|
+
} debug_frame_t;
|
40
|
+
|
41
|
+
typedef struct {
|
42
|
+
debug_frame_t *stack;
|
43
|
+
int stack_size;
|
44
|
+
|
45
|
+
VALUE thread;
|
46
|
+
int thnum;
|
47
|
+
int flags;
|
48
|
+
|
49
|
+
ctx_stop_reason stop_reason;
|
50
|
+
int stop_next;
|
51
|
+
int dest_frame;
|
52
|
+
int stop_line;
|
53
|
+
int stop_frame;
|
54
|
+
|
55
|
+
char *last_file;
|
56
|
+
int last_line;
|
57
|
+
} debug_context_t;
|
58
|
+
|
59
|
+
/* functions */
|
60
|
+
extern VALUE Init_context(VALUE mDebase);
|
61
|
+
extern VALUE context_create(VALUE thread, VALUE cDebugThread);
|
62
|
+
extern void reset_stepping_stop_points(debug_context_t *context);
|
63
|
+
extern VALUE Context_ignored(VALUE self);
|
64
|
+
extern void push_frame(VALUE context_object, char* file, int line, VALUE binding, VALUE self);
|
65
|
+
extern void pop_frame(VALUE context_object);
|
66
|
+
extern void update_frame(VALUE context_object, char* file, int line, VALUE binding, VALUE self);
|
67
|
+
|
68
|
+
/* locked threads container */
|
69
|
+
/* types */
|
70
|
+
typedef struct locked_thread_t {
|
71
|
+
VALUE thread;
|
72
|
+
struct locked_thread_t *next;
|
73
|
+
} locked_thread_t;
|
74
|
+
|
75
|
+
/* functions */
|
76
|
+
extern int is_in_locked(VALUE thread_id);
|
77
|
+
extern void add_to_locked(VALUE thread);
|
78
|
+
extern VALUE remove_from_locked();
|
79
|
+
|
80
|
+
/* breakpoints and catchpoints */
|
81
|
+
/* types */
|
82
|
+
typedef struct
|
83
|
+
{
|
84
|
+
VALUE enabled;
|
85
|
+
VALUE source;
|
86
|
+
VALUE expr;
|
87
|
+
int line;
|
88
|
+
int id;
|
89
|
+
} breakpoint_t;
|
90
|
+
|
91
|
+
extern VALUE catchpoint_hit_count(VALUE catchpoints, VALUE exception, VALUE *exception_name);
|
92
|
+
extern VALUE breakpoint_find(VALUE breakpoints, VALUE source, VALUE pos, VALUE binding);
|
93
|
+
extern void Init_breakpoint(VALUE mDebase);
|
94
|
+
#endif
|