debugger2 1.0.0.beta1
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 +12 -0
- data/.travis.yml +3 -0
- data/AUTHORS +10 -0
- data/CHANGELOG.md +65 -0
- data/CONTRIBUTING.md +1 -0
- data/Gemfile +3 -0
- data/LICENSE +23 -0
- data/OLDER_CHANGELOG +334 -0
- data/OLD_CHANGELOG +5655 -0
- data/OLD_README +122 -0
- data/README.md +108 -0
- data/Rakefile +78 -0
- data/bin/rdebug +397 -0
- data/debugger2.gemspec +29 -0
- data/doc/.cvsignore +42 -0
- data/doc/Makefile.am +63 -0
- data/doc/emacs-notes.txt +38 -0
- data/doc/hanoi.rb +35 -0
- data/doc/primes.rb +28 -0
- data/doc/rdebug-emacs.texi +1030 -0
- data/doc/ruby-debug.texi +3791 -0
- data/doc/test-tri2.rb +18 -0
- data/doc/tri3.rb +8 -0
- data/doc/triangle.rb +12 -0
- data/emacs/Makefile.am +130 -0
- data/emacs/rdebug-annotate.el +385 -0
- data/emacs/rdebug-breaks.el +407 -0
- data/emacs/rdebug-cmd.el +92 -0
- data/emacs/rdebug-core.el +502 -0
- data/emacs/rdebug-dbg.el +62 -0
- data/emacs/rdebug-error.el +79 -0
- data/emacs/rdebug-fns.el +111 -0
- data/emacs/rdebug-frames.el +230 -0
- data/emacs/rdebug-gud.el +242 -0
- data/emacs/rdebug-help.el +104 -0
- data/emacs/rdebug-info.el +83 -0
- data/emacs/rdebug-layouts.el +180 -0
- data/emacs/rdebug-locring.el +118 -0
- data/emacs/rdebug-output.el +106 -0
- data/emacs/rdebug-regexp.el +118 -0
- data/emacs/rdebug-secondary.el +260 -0
- data/emacs/rdebug-shortkey.el +175 -0
- data/emacs/rdebug-source.el +568 -0
- data/emacs/rdebug-track.el +392 -0
- data/emacs/rdebug-varbuf.el +150 -0
- data/emacs/rdebug-vars.el +125 -0
- data/emacs/rdebug-watch.el +132 -0
- data/emacs/rdebug.el +326 -0
- data/emacs/test/elk-test.el +242 -0
- data/emacs/test/test-annotate.el +103 -0
- data/emacs/test/test-cmd.el +116 -0
- data/emacs/test/test-core.el +104 -0
- data/emacs/test/test-fns.el +65 -0
- data/emacs/test/test-frames.el +62 -0
- data/emacs/test/test-gud.el +35 -0
- data/emacs/test/test-indent.el +58 -0
- data/emacs/test/test-regexp.el +144 -0
- data/emacs/test/test-shortkey.el +61 -0
- data/ext/ruby_debug/breakpoint.c +630 -0
- data/ext/ruby_debug/extconf.rb +11 -0
- data/ext/ruby_debug/ruby_debug.c +2203 -0
- data/ext/ruby_debug/ruby_debug.h +151 -0
- data/lib/debugger.rb +5 -0
- data/lib/debugger/version.rb +5 -0
- data/lib/debugger2.rb +6 -0
- data/lib/ruby-debug-base.rb +307 -0
- data/lib/ruby-debug.rb +176 -0
- data/lib/ruby-debug/command.rb +227 -0
- data/lib/ruby-debug/commands/breakpoints.rb +153 -0
- data/lib/ruby-debug/commands/catchpoint.rb +55 -0
- data/lib/ruby-debug/commands/condition.rb +49 -0
- data/lib/ruby-debug/commands/continue.rb +38 -0
- data/lib/ruby-debug/commands/control.rb +107 -0
- data/lib/ruby-debug/commands/display.rb +120 -0
- data/lib/ruby-debug/commands/edit.rb +48 -0
- data/lib/ruby-debug/commands/enable.rb +202 -0
- data/lib/ruby-debug/commands/eval.rb +176 -0
- data/lib/ruby-debug/commands/finish.rb +42 -0
- data/lib/ruby-debug/commands/frame.rb +301 -0
- data/lib/ruby-debug/commands/help.rb +56 -0
- data/lib/ruby-debug/commands/info.rb +467 -0
- data/lib/ruby-debug/commands/irb.rb +123 -0
- data/lib/ruby-debug/commands/jump.rb +66 -0
- data/lib/ruby-debug/commands/kill.rb +51 -0
- data/lib/ruby-debug/commands/list.rb +94 -0
- data/lib/ruby-debug/commands/method.rb +84 -0
- data/lib/ruby-debug/commands/quit.rb +39 -0
- data/lib/ruby-debug/commands/reload.rb +40 -0
- data/lib/ruby-debug/commands/save.rb +90 -0
- data/lib/ruby-debug/commands/set.rb +223 -0
- data/lib/ruby-debug/commands/show.rb +247 -0
- data/lib/ruby-debug/commands/skip.rb +35 -0
- data/lib/ruby-debug/commands/source.rb +36 -0
- data/lib/ruby-debug/commands/stepping.rb +81 -0
- data/lib/ruby-debug/commands/threads.rb +189 -0
- data/lib/ruby-debug/commands/tmate.rb +36 -0
- data/lib/ruby-debug/commands/trace.rb +57 -0
- data/lib/ruby-debug/commands/variables.rb +199 -0
- data/lib/ruby-debug/debugger.rb +5 -0
- data/lib/ruby-debug/helper.rb +69 -0
- data/lib/ruby-debug/interface.rb +232 -0
- data/lib/ruby-debug/processor.rb +474 -0
- data/man/rdebug.1 +241 -0
- data/old_scripts/Makefile.am +14 -0
- data/old_scripts/README.md +2 -0
- data/old_scripts/autogen.sh +4 -0
- data/old_scripts/configure.ac +12 -0
- data/old_scripts/rdbg.rb +33 -0
- data/old_scripts/runner.sh +7 -0
- data/old_scripts/svn2cl_usermap +3 -0
- data/test/.cvsignore +1 -0
- data/test/breakpoints_test.rb +366 -0
- data/test/conditions_test.rb +77 -0
- data/test/continue_test.rb +28 -0
- data/test/display_test.rb +143 -0
- data/test/edit_test.rb +55 -0
- data/test/eval_test.rb +94 -0
- data/test/examples/breakpoint1.rb +15 -0
- data/test/examples/breakpoint2.rb +7 -0
- data/test/examples/conditions.rb +4 -0
- data/test/examples/continue.rb +4 -0
- data/test/examples/display.rb +5 -0
- data/test/examples/edit.rb +3 -0
- data/test/examples/edit2.rb +3 -0
- data/test/examples/eval.rb +4 -0
- data/test/examples/finish.rb +20 -0
- data/test/examples/frame.rb +31 -0
- data/test/examples/help.rb +2 -0
- data/test/examples/info.rb +48 -0
- data/test/examples/info2.rb +3 -0
- data/test/examples/irb.rb +6 -0
- data/test/examples/jump.rb +14 -0
- data/test/examples/kill.rb +2 -0
- data/test/examples/list.rb +12 -0
- data/test/examples/method.rb +15 -0
- data/test/examples/post_mortem.rb +19 -0
- data/test/examples/quit.rb +2 -0
- data/test/examples/reload.rb +6 -0
- data/test/examples/restart.rb +6 -0
- data/test/examples/save.rb +3 -0
- data/test/examples/set.rb +3 -0
- data/test/examples/set_annotate.rb +12 -0
- data/test/examples/settings.rb +1 -0
- data/test/examples/show.rb +2 -0
- data/test/examples/source.rb +3 -0
- data/test/examples/stepping.rb +21 -0
- data/test/examples/thread.rb +32 -0
- data/test/examples/tmate.rb +10 -0
- data/test/examples/trace.rb +7 -0
- data/test/examples/trace_threads.rb +20 -0
- data/test/examples/variables.rb +26 -0
- data/test/finish_test.rb +49 -0
- data/test/frame_test.rb +140 -0
- data/test/help_test.rb +51 -0
- data/test/info_test.rb +326 -0
- data/test/irb_test.rb +82 -0
- data/test/jump_test.rb +70 -0
- data/test/kill_test.rb +49 -0
- data/test/list_test.rb +147 -0
- data/test/method_test.rb +72 -0
- data/test/post_mortem_test.rb +25 -0
- data/test/quit_test.rb +56 -0
- data/test/reload_test.rb +47 -0
- data/test/restart_test.rb +145 -0
- data/test/save_test.rb +94 -0
- data/test/set_test.rb +183 -0
- data/test/show_test.rb +294 -0
- data/test/source_test.rb +46 -0
- data/test/stepping_test.rb +122 -0
- data/test/support/breakpoint.rb +12 -0
- data/test/support/context.rb +14 -0
- data/test/support/matchers.rb +67 -0
- data/test/support/mocha_extensions.rb +71 -0
- data/test/support/processor.rb +7 -0
- data/test/support/test_dsl.rb +206 -0
- data/test/support/test_interface.rb +66 -0
- data/test/test_helper.rb +9 -0
- data/test/thread_test.rb +124 -0
- data/test/tmate_test.rb +45 -0
- data/test/trace_test.rb +156 -0
- data/test/variables_test.rb +116 -0
- metadata +319 -0
|
@@ -0,0 +1,2203 @@
|
|
|
1
|
+
#include <stdio.h>
|
|
2
|
+
#include <ctype.h>
|
|
3
|
+
#include "ruby_debug.h"
|
|
4
|
+
|
|
5
|
+
#define DEBUG_VERSION "0.11"
|
|
6
|
+
|
|
7
|
+
static VALUE debug_stop(VALUE);
|
|
8
|
+
static void context_suspend_0(debug_context_t *);
|
|
9
|
+
static void context_resume_0(debug_context_t *);
|
|
10
|
+
|
|
11
|
+
typedef struct {
|
|
12
|
+
st_table *tbl;
|
|
13
|
+
} threads_table_t;
|
|
14
|
+
|
|
15
|
+
static VALUE tracing = Qfalse;
|
|
16
|
+
static VALUE locker = Qnil;
|
|
17
|
+
static VALUE post_mortem = Qfalse;
|
|
18
|
+
static VALUE keep_frame_binding = Qfalse;
|
|
19
|
+
static VALUE track_frame_args = Qfalse;
|
|
20
|
+
|
|
21
|
+
static VALUE debug_debugger = Qfalse;
|
|
22
|
+
static const VALUE debug_debugger_stack_size = Qfalse;
|
|
23
|
+
|
|
24
|
+
static VALUE last_context = Qnil;
|
|
25
|
+
static VALUE last_thread = Qnil;
|
|
26
|
+
static VALUE tracepoints = Qnil;
|
|
27
|
+
|
|
28
|
+
static debug_context_t *last_debug_context = NULL;
|
|
29
|
+
|
|
30
|
+
VALUE rdebug_threads_tbl = Qnil; /* Context for each of the threads */
|
|
31
|
+
VALUE mDebugger; /* Ruby Debugger Module object */
|
|
32
|
+
|
|
33
|
+
static VALUE cThreadsTable;
|
|
34
|
+
static VALUE cContext;
|
|
35
|
+
static VALUE cDebugThread;
|
|
36
|
+
|
|
37
|
+
static int start_count = 0;
|
|
38
|
+
static int thnum_max = 0;
|
|
39
|
+
static int bkp_count = 0;
|
|
40
|
+
static int last_debugged_thnum = -1;
|
|
41
|
+
static unsigned long last_check = 0;
|
|
42
|
+
static unsigned long hook_count = 0;
|
|
43
|
+
|
|
44
|
+
typedef struct locked_thread_t {
|
|
45
|
+
VALUE thread_id;
|
|
46
|
+
struct locked_thread_t *next;
|
|
47
|
+
} locked_thread_t;
|
|
48
|
+
|
|
49
|
+
static locked_thread_t *locked_head = NULL;
|
|
50
|
+
static locked_thread_t *locked_tail = NULL;
|
|
51
|
+
|
|
52
|
+
static void
|
|
53
|
+
reset_stepping_stop_points(debug_context_t *debug_context)
|
|
54
|
+
{
|
|
55
|
+
debug_context->dest_frame = -1;
|
|
56
|
+
debug_context->stop_line = -1;
|
|
57
|
+
debug_context->stop_next = -1;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
static VALUE
|
|
61
|
+
ref2id(VALUE obj)
|
|
62
|
+
{
|
|
63
|
+
return obj;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
static VALUE
|
|
67
|
+
id2ref(VALUE id)
|
|
68
|
+
{
|
|
69
|
+
return id;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
static VALUE
|
|
73
|
+
context_thread_0(debug_context_t *debug_context)
|
|
74
|
+
{
|
|
75
|
+
return id2ref(debug_context->thread_id);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
#define ruby_threadptr_data_type *threadptr_data_type()
|
|
79
|
+
|
|
80
|
+
#define ruby_current_thread ((rb_thread_t *)RTYPEDDATA_DATA(rb_thread_current()))
|
|
81
|
+
|
|
82
|
+
static int
|
|
83
|
+
is_in_locked(VALUE thread_id)
|
|
84
|
+
{
|
|
85
|
+
locked_thread_t *node;
|
|
86
|
+
|
|
87
|
+
if(!locked_head)
|
|
88
|
+
return 0;
|
|
89
|
+
|
|
90
|
+
for(node = locked_head; node != locked_tail; node = node->next)
|
|
91
|
+
{
|
|
92
|
+
if(node->thread_id == thread_id) return 1;
|
|
93
|
+
}
|
|
94
|
+
return 0;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
static void
|
|
98
|
+
add_to_locked(VALUE thread)
|
|
99
|
+
{
|
|
100
|
+
locked_thread_t *node;
|
|
101
|
+
VALUE thread_id = ref2id(thread);
|
|
102
|
+
|
|
103
|
+
if(is_in_locked(thread_id))
|
|
104
|
+
return;
|
|
105
|
+
|
|
106
|
+
node = ALLOC(locked_thread_t);
|
|
107
|
+
node->thread_id = thread_id;
|
|
108
|
+
node->next = NULL;
|
|
109
|
+
if(locked_tail)
|
|
110
|
+
locked_tail->next = node;
|
|
111
|
+
locked_tail = node;
|
|
112
|
+
if(!locked_head)
|
|
113
|
+
locked_head = node;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static VALUE
|
|
117
|
+
remove_from_locked(void)
|
|
118
|
+
{
|
|
119
|
+
VALUE thread;
|
|
120
|
+
locked_thread_t *node;
|
|
121
|
+
|
|
122
|
+
if(locked_head == NULL)
|
|
123
|
+
return Qnil;
|
|
124
|
+
node = locked_head;
|
|
125
|
+
locked_head = locked_head->next;
|
|
126
|
+
if(locked_tail == node)
|
|
127
|
+
locked_tail = NULL;
|
|
128
|
+
thread = id2ref(node->thread_id);
|
|
129
|
+
xfree(node);
|
|
130
|
+
return thread;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
static int
|
|
134
|
+
threads_table_mark_keyvalue(st_data_t key, st_data_t value, st_data_t tbl)
|
|
135
|
+
{
|
|
136
|
+
VALUE thread = id2ref((VALUE)key);
|
|
137
|
+
if (!value)
|
|
138
|
+
return ST_CONTINUE;
|
|
139
|
+
|
|
140
|
+
rb_gc_mark((VALUE)value);
|
|
141
|
+
rb_gc_mark(thread);
|
|
142
|
+
|
|
143
|
+
return ST_CONTINUE;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static void
|
|
147
|
+
threads_table_mark(void* data)
|
|
148
|
+
{
|
|
149
|
+
threads_table_t *threads_table = (threads_table_t*)data;
|
|
150
|
+
st_table *tbl = threads_table->tbl;
|
|
151
|
+
st_foreach(tbl, threads_table_mark_keyvalue, (st_data_t)tbl);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
static void
|
|
155
|
+
threads_table_free(void* data)
|
|
156
|
+
{
|
|
157
|
+
threads_table_t *threads_table = (threads_table_t*)data;
|
|
158
|
+
st_free_table(threads_table->tbl);
|
|
159
|
+
xfree(threads_table);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
static VALUE
|
|
163
|
+
threads_table_create(void)
|
|
164
|
+
{
|
|
165
|
+
threads_table_t *threads_table;
|
|
166
|
+
|
|
167
|
+
threads_table = ALLOC(threads_table_t);
|
|
168
|
+
threads_table->tbl = st_init_numtable();
|
|
169
|
+
return Data_Wrap_Struct(cThreadsTable, threads_table_mark, threads_table_free, threads_table);
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
static void
|
|
173
|
+
threads_table_clear(VALUE table)
|
|
174
|
+
{
|
|
175
|
+
threads_table_t *threads_table;
|
|
176
|
+
|
|
177
|
+
Data_Get_Struct(table, threads_table_t, threads_table);
|
|
178
|
+
st_clear(threads_table->tbl);
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
static int
|
|
182
|
+
is_living_thread(VALUE thread)
|
|
183
|
+
{
|
|
184
|
+
return rb_funcall(thread, rb_intern("alive?"), 0) == Qtrue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
static int
|
|
188
|
+
threads_table_check_i(st_data_t key, st_data_t value, st_data_t dummy)
|
|
189
|
+
{
|
|
190
|
+
VALUE thread;
|
|
191
|
+
|
|
192
|
+
if(!value)
|
|
193
|
+
{
|
|
194
|
+
return ST_DELETE;
|
|
195
|
+
}
|
|
196
|
+
thread = id2ref((VALUE)key);
|
|
197
|
+
if(!is_living_thread(thread))
|
|
198
|
+
{
|
|
199
|
+
return ST_DELETE;
|
|
200
|
+
}
|
|
201
|
+
return ST_CONTINUE;
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
static void
|
|
205
|
+
check_thread_contexts(void)
|
|
206
|
+
{
|
|
207
|
+
threads_table_t *threads_table;
|
|
208
|
+
|
|
209
|
+
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
|
|
210
|
+
st_foreach(threads_table->tbl, threads_table_check_i, 0);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/*
|
|
214
|
+
* call-seq:
|
|
215
|
+
* Debugger.started? -> bool
|
|
216
|
+
*
|
|
217
|
+
* Returns +true+ the debugger is started.
|
|
218
|
+
*/
|
|
219
|
+
static VALUE
|
|
220
|
+
debug_is_started(VALUE self)
|
|
221
|
+
{
|
|
222
|
+
return IS_STARTED ? Qtrue : Qfalse;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
static void
|
|
226
|
+
debug_context_mark(void *data)
|
|
227
|
+
{
|
|
228
|
+
debug_context_t *debug_context = (debug_context_t *)data;
|
|
229
|
+
rb_gc_mark(debug_context->breakpoint);
|
|
230
|
+
rb_gc_mark(debug_context->inspected_frame);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
static void
|
|
234
|
+
debug_context_free(void *data)
|
|
235
|
+
{
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
static int
|
|
239
|
+
exact_stack_size(VALUE thread)
|
|
240
|
+
{
|
|
241
|
+
VALUE locs = rb_funcall(thread, rb_intern("backtrace_locations"), 1, INT2FIX(1));
|
|
242
|
+
int stack_size = (int)RARRAY_LEN(locs);
|
|
243
|
+
if (debug_debugger_stack_size && debug_debugger) {
|
|
244
|
+
fprintf(stderr, "[debug:stacksize] %d\n", stack_size);
|
|
245
|
+
rb_p(locs);
|
|
246
|
+
}
|
|
247
|
+
return stack_size;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
static VALUE
|
|
251
|
+
debug_context_create(VALUE thread)
|
|
252
|
+
{
|
|
253
|
+
debug_context_t *debug_context;
|
|
254
|
+
|
|
255
|
+
debug_context = ALLOC(debug_context_t);
|
|
256
|
+
|
|
257
|
+
debug_context->thnum = ++thnum_max;
|
|
258
|
+
debug_context->last_file = Qnil;
|
|
259
|
+
debug_context->last_line = Qnil;
|
|
260
|
+
debug_context->flags = 0;
|
|
261
|
+
debug_context->calced_stack_size = exact_stack_size(thread);
|
|
262
|
+
|
|
263
|
+
debug_context->stop_next = -1;
|
|
264
|
+
debug_context->dest_frame = -1;
|
|
265
|
+
debug_context->stop_line = -1;
|
|
266
|
+
debug_context->stop_frame = -1;
|
|
267
|
+
|
|
268
|
+
debug_context->stop_reason = CTX_STOP_NONE;
|
|
269
|
+
debug_context->thread_id = ref2id(thread);
|
|
270
|
+
debug_context->breakpoint = Qnil;
|
|
271
|
+
debug_context->inspected_frame = Qnil;
|
|
272
|
+
|
|
273
|
+
if (rb_obj_class(thread) == cDebugThread) {
|
|
274
|
+
CTX_FL_SET(debug_context, CTX_FL_IGNORE);
|
|
275
|
+
}
|
|
276
|
+
return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, debug_context);
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
static VALUE
|
|
280
|
+
debug_context_dup(debug_context_t *debug_context, VALUE self)
|
|
281
|
+
{
|
|
282
|
+
debug_context_t *new_debug_context;
|
|
283
|
+
|
|
284
|
+
new_debug_context = ALLOC(debug_context_t);
|
|
285
|
+
memcpy(new_debug_context, debug_context, sizeof(debug_context_t));
|
|
286
|
+
new_debug_context->stop_next = -1;
|
|
287
|
+
new_debug_context->dest_frame = -1;
|
|
288
|
+
new_debug_context->stop_line = -1;
|
|
289
|
+
new_debug_context->stop_frame = -1;
|
|
290
|
+
new_debug_context->breakpoint = Qnil;
|
|
291
|
+
new_debug_context->inspected_frame = debug_context->inspected_frame;
|
|
292
|
+
CTX_FL_SET(new_debug_context, CTX_FL_DEAD);
|
|
293
|
+
|
|
294
|
+
return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, new_debug_context);
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
static void
|
|
298
|
+
thread_context_lookup(VALUE thread, VALUE *context, debug_context_t **debug_context, int create)
|
|
299
|
+
{
|
|
300
|
+
threads_table_t *threads_table;
|
|
301
|
+
VALUE thread_id;
|
|
302
|
+
debug_context_t *l_debug_context;
|
|
303
|
+
|
|
304
|
+
debug_check_started();
|
|
305
|
+
|
|
306
|
+
if (last_thread == thread && last_context != Qnil) {
|
|
307
|
+
*context = last_context;
|
|
308
|
+
if (debug_context) {
|
|
309
|
+
*debug_context = last_debug_context;
|
|
310
|
+
}
|
|
311
|
+
return;
|
|
312
|
+
}
|
|
313
|
+
thread_id = ref2id(thread);
|
|
314
|
+
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
|
|
315
|
+
if (!st_lookup(threads_table->tbl, thread_id, context) || !*context) {
|
|
316
|
+
if (create) {
|
|
317
|
+
*context = debug_context_create(thread);
|
|
318
|
+
st_insert(threads_table->tbl, thread_id, *context);
|
|
319
|
+
}
|
|
320
|
+
else {
|
|
321
|
+
*context = 0;
|
|
322
|
+
if (debug_context) {
|
|
323
|
+
*debug_context = NULL;
|
|
324
|
+
}
|
|
325
|
+
return;
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
Data_Get_Struct(*context, debug_context_t, l_debug_context);
|
|
330
|
+
if (debug_context) {
|
|
331
|
+
*debug_context = l_debug_context;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
last_thread = thread;
|
|
335
|
+
last_context = *context;
|
|
336
|
+
last_debug_context = l_debug_context;
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
static VALUE
|
|
340
|
+
dc_inspected_frame(const debug_context_t *debug_context)
|
|
341
|
+
{
|
|
342
|
+
if (NIL_P(debug_context->inspected_frame)) {
|
|
343
|
+
rb_raise(rb_eRuntimeError, "Inspected frame information is not available");
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
return debug_context->inspected_frame;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
static VALUE
|
|
350
|
+
dc_inspected_frame_get(const debug_context_t *debug_context, int frame_index, enum inspected_frame_type type)
|
|
351
|
+
{
|
|
352
|
+
VALUE frame = rb_ary_entry(dc_inspected_frame(debug_context), frame_index);
|
|
353
|
+
return rb_ary_entry(frame, type);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
static VALUE
|
|
357
|
+
dc_inspected_frame_location(const debug_context_t *debug_context, int frame_index)
|
|
358
|
+
{
|
|
359
|
+
return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_LOCATION);
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
static VALUE
|
|
363
|
+
dc_inspected_frame_self(const debug_context_t *debug_context, int frame_index)
|
|
364
|
+
{
|
|
365
|
+
return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_SELF);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
static VALUE
|
|
369
|
+
dc_inspected_frame_class(const debug_context_t *debug_context, int frame_index)
|
|
370
|
+
{
|
|
371
|
+
return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_CLASS);
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
static VALUE
|
|
375
|
+
dc_inspected_frame_binding(const debug_context_t *debug_context, int frame_index)
|
|
376
|
+
{
|
|
377
|
+
return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_BIDING);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
#if 0 /* unused */
|
|
381
|
+
static VALUE
|
|
382
|
+
dc_inspected_frame_iseq(const debug_context_t *debug_context, int frame_index)
|
|
383
|
+
{
|
|
384
|
+
return dc_inspected_frame_get(debug_context, frame_index, INSPECTED_FRAME_ISEQ);
|
|
385
|
+
}
|
|
386
|
+
#endif
|
|
387
|
+
|
|
388
|
+
static int
|
|
389
|
+
dc_stack_size(const debug_context_t *debug_context)
|
|
390
|
+
{
|
|
391
|
+
if (!NIL_P(debug_context->inspected_frame)) {
|
|
392
|
+
int stack_size = (int)RARRAY_LEN(debug_context->inspected_frame);
|
|
393
|
+
|
|
394
|
+
/* for debug */
|
|
395
|
+
if (0 && debug_debugger && stack_size != debug_context->calced_stack_size) {
|
|
396
|
+
rb_p(debug_context->inspected_frame);
|
|
397
|
+
rb_bug("dc_stack_size: stack size calculation miss: calced %d but %d",
|
|
398
|
+
debug_context->calced_stack_size, stack_size);
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
return stack_size;
|
|
402
|
+
}
|
|
403
|
+
else {
|
|
404
|
+
/* TOOD: optimize */
|
|
405
|
+
if (0 && debug_debugger) {
|
|
406
|
+
int stack_size = exact_stack_size(rb_thread_current());
|
|
407
|
+
if (stack_size != debug_context->calced_stack_size) {
|
|
408
|
+
rb_bug("dc_stack_size: stack size calculation miss: calced %d but %d",
|
|
409
|
+
debug_context->calced_stack_size, stack_size);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
return debug_context->calced_stack_size;
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
static void
|
|
417
|
+
halt_while_other_thread_is_active(VALUE current_thread, debug_context_t *debug_context)
|
|
418
|
+
{
|
|
419
|
+
while(1) {
|
|
420
|
+
/* halt execution of the current thread if the debugger
|
|
421
|
+
is activated in another
|
|
422
|
+
*/
|
|
423
|
+
while(locker != Qnil && locker != current_thread) {
|
|
424
|
+
add_to_locked(current_thread);
|
|
425
|
+
rb_thread_stop();
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
/* stop the current thread if it's marked as suspended */
|
|
429
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) && locker != current_thread) {
|
|
430
|
+
CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
|
|
431
|
+
rb_thread_stop();
|
|
432
|
+
}
|
|
433
|
+
else break;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
static void
|
|
438
|
+
trace_cleanup(debug_context_t *debug_context)
|
|
439
|
+
{
|
|
440
|
+
VALUE next_thread;
|
|
441
|
+
debug_context->stop_reason = CTX_STOP_NONE;
|
|
442
|
+
|
|
443
|
+
/* check that all contexts point to alive threads */
|
|
444
|
+
if (hook_count - last_check > 3000) {
|
|
445
|
+
check_thread_contexts();
|
|
446
|
+
last_check = hook_count;
|
|
447
|
+
}
|
|
448
|
+
|
|
449
|
+
/* release a lock */
|
|
450
|
+
locker = Qnil;
|
|
451
|
+
|
|
452
|
+
/* let the next thread to run */
|
|
453
|
+
next_thread = remove_from_locked();
|
|
454
|
+
if(next_thread != Qnil) {
|
|
455
|
+
rb_thread_run(next_thread);
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
|
|
459
|
+
static void
|
|
460
|
+
trace_debug_print(rb_trace_arg_t *trace_arg, debug_context_t *debug_context)
|
|
461
|
+
{
|
|
462
|
+
if (debug_debugger == Qtrue) {
|
|
463
|
+
VALUE path = rb_tracearg_path(trace_arg);
|
|
464
|
+
VALUE line = rb_tracearg_lineno(trace_arg);
|
|
465
|
+
VALUE event = rb_tracearg_event(trace_arg);
|
|
466
|
+
VALUE mid = rb_tracearg_method_id(trace_arg);
|
|
467
|
+
fprintf(stderr, "%*s[debug:event#%d] %s@%s:%d %s\n",
|
|
468
|
+
debug_context->calced_stack_size, "",
|
|
469
|
+
debug_context->thnum,
|
|
470
|
+
rb_id2name(SYM2ID(event)),
|
|
471
|
+
RSTRING_PTR(path),
|
|
472
|
+
NUM2INT(line),
|
|
473
|
+
NIL_P(mid) ? "" : rb_id2name(SYM2ID(mid))
|
|
474
|
+
);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
#define TRACE_SETUP \
|
|
479
|
+
rb_trace_arg_t *trace_arg = rb_tracearg_from_tracepoint(tpval); \
|
|
480
|
+
VALUE current_thread = rb_thread_current(); \
|
|
481
|
+
debug_context_t *debug_context; \
|
|
482
|
+
VALUE context; \
|
|
483
|
+
thread_context_lookup(current_thread, &context, &debug_context, 1); \
|
|
484
|
+
trace_debug_print(trace_arg, debug_context);
|
|
485
|
+
|
|
486
|
+
#define TRACE_COMMON() \
|
|
487
|
+
if (trace_common(trace_arg, debug_context, current_thread) == 0) { return; }
|
|
488
|
+
|
|
489
|
+
static int
|
|
490
|
+
trace_common(rb_trace_arg_t *trace_arg, debug_context_t *debug_context, VALUE current_thread)
|
|
491
|
+
{
|
|
492
|
+
hook_count++;
|
|
493
|
+
|
|
494
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_IGNORE)) return 0;
|
|
495
|
+
|
|
496
|
+
halt_while_other_thread_is_active(current_thread, debug_context);
|
|
497
|
+
|
|
498
|
+
if (locker != Qnil) return 0;
|
|
499
|
+
|
|
500
|
+
locker = current_thread;
|
|
501
|
+
|
|
502
|
+
/* ignore a skipped section of code */
|
|
503
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_SKIPPED)) {
|
|
504
|
+
trace_cleanup(debug_context);
|
|
505
|
+
return 0;
|
|
506
|
+
}
|
|
507
|
+
|
|
508
|
+
/* Sometimes duplicate RUBY_EVENT_LINE messages get generated by the compiler.
|
|
509
|
+
* Ignore them. */
|
|
510
|
+
if (0 /* TODO: check was emitted */) {
|
|
511
|
+
trace_cleanup(debug_context);
|
|
512
|
+
return 0;
|
|
513
|
+
}
|
|
514
|
+
|
|
515
|
+
/* There can be many event calls per line, but we only want *one* breakpoint per line. */
|
|
516
|
+
if (debug_context->last_line != rb_tracearg_lineno(trace_arg) ||
|
|
517
|
+
debug_context->last_file != rb_tracearg_path(trace_arg)) {
|
|
518
|
+
CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
|
|
519
|
+
}
|
|
520
|
+
|
|
521
|
+
return 1;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
struct call_with_inspection_data {
|
|
525
|
+
debug_context_t *debug_context;
|
|
526
|
+
VALUE context;
|
|
527
|
+
ID id;
|
|
528
|
+
int argc;
|
|
529
|
+
VALUE *argv;
|
|
530
|
+
};
|
|
531
|
+
|
|
532
|
+
static VALUE
|
|
533
|
+
open_debug_inspector_i(const rb_debug_inspector_t *inspector, void *data)
|
|
534
|
+
{
|
|
535
|
+
struct call_with_inspection_data *cwi = (struct call_with_inspection_data *)data;
|
|
536
|
+
VALUE inspected_frame = rb_ary_new();
|
|
537
|
+
VALUE locs = rb_debug_inspector_backtrace_locations(inspector);
|
|
538
|
+
int i;
|
|
539
|
+
|
|
540
|
+
for (i=0; i<RARRAY_LEN(locs); i++) {
|
|
541
|
+
VALUE frame = rb_ary_new();
|
|
542
|
+
rb_ary_push(frame, rb_ary_entry(locs, i));
|
|
543
|
+
rb_ary_push(frame, rb_debug_inspector_frame_self_get(inspector, i));
|
|
544
|
+
rb_ary_push(frame, rb_debug_inspector_frame_class_get(inspector, i));
|
|
545
|
+
rb_ary_push(frame, rb_debug_inspector_frame_binding_get(inspector, i));
|
|
546
|
+
rb_ary_push(frame, rb_debug_inspector_frame_iseq_get(inspector, i));
|
|
547
|
+
|
|
548
|
+
rb_ary_push(inspected_frame, frame);
|
|
549
|
+
}
|
|
550
|
+
|
|
551
|
+
cwi->debug_context->inspected_frame = inspected_frame;
|
|
552
|
+
return rb_funcall2(cwi->context, cwi->id, cwi->argc, cwi->argv);
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
static VALUE
|
|
556
|
+
open_debug_inspector(struct call_with_inspection_data *cwi)
|
|
557
|
+
{
|
|
558
|
+
return rb_debug_inspector_open(open_debug_inspector_i, cwi);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
static VALUE
|
|
562
|
+
close_debug_inspector(struct call_with_inspection_data *cwi)
|
|
563
|
+
{
|
|
564
|
+
cwi->debug_context->inspected_frame = Qnil;
|
|
565
|
+
return Qnil;
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
static VALUE
|
|
569
|
+
call_with_debug_inspector(struct call_with_inspection_data *data)
|
|
570
|
+
{
|
|
571
|
+
return rb_ensure(open_debug_inspector, (VALUE)data, close_debug_inspector, (VALUE)data);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
static void
|
|
575
|
+
save_current_position(debug_context_t *debug_context, VALUE file, VALUE line)
|
|
576
|
+
{
|
|
577
|
+
debug_context->last_file = file;
|
|
578
|
+
debug_context->last_line = line;
|
|
579
|
+
CTX_FL_UNSET(debug_context, CTX_FL_ENABLE_BKPT);
|
|
580
|
+
CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
|
|
581
|
+
CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
static VALUE
|
|
585
|
+
call_at(VALUE context, debug_context_t *debug_context, ID mid, int argc, VALUE a0, VALUE a1)
|
|
586
|
+
{
|
|
587
|
+
struct call_with_inspection_data cwi;
|
|
588
|
+
VALUE argv[2];
|
|
589
|
+
|
|
590
|
+
argv[0] = a0;
|
|
591
|
+
argv[1] = a1;
|
|
592
|
+
|
|
593
|
+
if (0) fprintf(stderr, "call_at: %s\n", rb_id2name(mid));
|
|
594
|
+
|
|
595
|
+
cwi.debug_context = debug_context;
|
|
596
|
+
cwi.context = context;
|
|
597
|
+
cwi.id = mid;
|
|
598
|
+
cwi.argc = argc;
|
|
599
|
+
cwi.argv = &argv[0];
|
|
600
|
+
return call_with_debug_inspector(&cwi);
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
static VALUE
|
|
604
|
+
call_at_line(VALUE context, debug_context_t *debug_context, VALUE file, VALUE line)
|
|
605
|
+
{
|
|
606
|
+
save_current_position(debug_context, file, line);
|
|
607
|
+
return call_at(context, debug_context, rb_intern("at_line"), 2, file, line);
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
static VALUE
|
|
611
|
+
call_at_tracing(VALUE context, debug_context_t *debug_context, VALUE file, VALUE line)
|
|
612
|
+
{
|
|
613
|
+
return call_at(context, debug_context, rb_intern("at_tracing"), 2, file, line);
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
static VALUE
|
|
617
|
+
call_at_breakpoint(VALUE context, debug_context_t *debug_context, VALUE breakpoint)
|
|
618
|
+
{
|
|
619
|
+
debug_context->stop_reason = CTX_STOP_BREAKPOINT;
|
|
620
|
+
return call_at(context, debug_context, rb_intern("at_breakpoint"), 1, breakpoint, 0);
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
static VALUE
|
|
624
|
+
call_at_catchpoint(VALUE context, debug_context_t *debug_context, VALUE exp)
|
|
625
|
+
{
|
|
626
|
+
return call_at(context, debug_context, rb_intern("at_catchpoint"), 1, exp, 0);
|
|
627
|
+
}
|
|
628
|
+
|
|
629
|
+
static void
|
|
630
|
+
call_at_line_check(VALUE binding, debug_context_t *debug_context, VALUE breakpoint, VALUE context, VALUE file, VALUE line)
|
|
631
|
+
{
|
|
632
|
+
debug_context->stop_reason = CTX_STOP_STEP;
|
|
633
|
+
|
|
634
|
+
/* check breakpoint expression */
|
|
635
|
+
if (breakpoint != Qnil) {
|
|
636
|
+
if (!check_breakpoint_expression(breakpoint, binding)) return;// TODO
|
|
637
|
+
if (!check_breakpoint_hit_condition(breakpoint)) return;// TODO
|
|
638
|
+
|
|
639
|
+
if (breakpoint != debug_context->breakpoint) {
|
|
640
|
+
call_at_breakpoint(context, debug_context, breakpoint);
|
|
641
|
+
}
|
|
642
|
+
else {
|
|
643
|
+
debug_context->breakpoint = Qnil;
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
reset_stepping_stop_points(debug_context);
|
|
648
|
+
call_at_line(context, debug_context, file, line);
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
static void
|
|
652
|
+
line_tracepoint(VALUE tpval, void *data)
|
|
653
|
+
{
|
|
654
|
+
int moved = 0; /* TODO */
|
|
655
|
+
VALUE breakpoint = Qnil;
|
|
656
|
+
VALUE file, line;
|
|
657
|
+
TRACE_SETUP;
|
|
658
|
+
|
|
659
|
+
TRACE_COMMON();
|
|
660
|
+
|
|
661
|
+
CTX_FL_SET(debug_context, CTX_FL_STEPPED);
|
|
662
|
+
|
|
663
|
+
file = rb_tracearg_path(trace_arg);
|
|
664
|
+
line = rb_tracearg_lineno(trace_arg);
|
|
665
|
+
|
|
666
|
+
if(RTEST(tracing) || CTX_FL_TEST(debug_context, CTX_FL_TRACING)) {
|
|
667
|
+
call_at_tracing(context, debug_context, file, line);
|
|
668
|
+
}
|
|
669
|
+
|
|
670
|
+
if (debug_context->dest_frame == -1 ||
|
|
671
|
+
dc_stack_size(debug_context) == debug_context->dest_frame) {
|
|
672
|
+
if (moved || !CTX_FL_TEST(debug_context, CTX_FL_FORCE_MOVE)) debug_context->stop_next--;
|
|
673
|
+
if (debug_context->stop_next < 0) debug_context->stop_next = -1;
|
|
674
|
+
|
|
675
|
+
if (moved || (CTX_FL_TEST(debug_context, CTX_FL_STEPPED) &&
|
|
676
|
+
!CTX_FL_TEST(debug_context, CTX_FL_FORCE_MOVE))) {
|
|
677
|
+
debug_context->stop_line--;
|
|
678
|
+
CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
|
|
679
|
+
}
|
|
680
|
+
}
|
|
681
|
+
else if (dc_stack_size(debug_context) < debug_context->dest_frame) {
|
|
682
|
+
debug_context->stop_next = 0;
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
if (0) fprintf(stderr, "stop_next: %d, stop_line: %d\n",
|
|
686
|
+
debug_context->stop_next,
|
|
687
|
+
debug_context->stop_line
|
|
688
|
+
);
|
|
689
|
+
|
|
690
|
+
if (debug_context->stop_next == 0 ||
|
|
691
|
+
debug_context->stop_line == 0 ||
|
|
692
|
+
(breakpoint = check_breakpoints_by_pos(debug_context, file, line)) != Qnil) {
|
|
693
|
+
call_at_line_check(rb_tracearg_binding(trace_arg), debug_context, breakpoint, context, file, line);
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
trace_cleanup(debug_context);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
static void
|
|
700
|
+
call_tracepoint(VALUE tpval, void *data)
|
|
701
|
+
{
|
|
702
|
+
VALUE breakpoint;
|
|
703
|
+
VALUE klass, mid, self;
|
|
704
|
+
TRACE_SETUP;
|
|
705
|
+
|
|
706
|
+
debug_context->calced_stack_size ++;
|
|
707
|
+
if (debug_debugger_stack_size && debug_debugger) {
|
|
708
|
+
fprintf(stderr, "[debug:stacksize] %d (add)\n", debug_context->calced_stack_size);
|
|
709
|
+
}
|
|
710
|
+
|
|
711
|
+
TRACE_COMMON();
|
|
712
|
+
|
|
713
|
+
klass = rb_tracearg_defined_class(trace_arg);
|
|
714
|
+
mid = rb_tracearg_method_id(trace_arg);
|
|
715
|
+
self = rb_tracearg_self(trace_arg);
|
|
716
|
+
|
|
717
|
+
breakpoint = check_breakpoints_by_method(debug_context, klass, mid, self);
|
|
718
|
+
|
|
719
|
+
if (breakpoint != Qnil) {
|
|
720
|
+
VALUE binding = rb_tracearg_binding(trace_arg);
|
|
721
|
+
|
|
722
|
+
if (!check_breakpoint_expression(breakpoint, binding)) return;
|
|
723
|
+
if (!check_breakpoint_hit_condition(breakpoint)) return;
|
|
724
|
+
if (breakpoint != debug_context->breakpoint) {
|
|
725
|
+
call_at_breakpoint(context, debug_context, breakpoint);
|
|
726
|
+
}
|
|
727
|
+
else {
|
|
728
|
+
debug_context->breakpoint = Qnil;
|
|
729
|
+
}
|
|
730
|
+
call_at_line(context, debug_context, rb_tracearg_path(trace_arg), rb_tracearg_lineno(trace_arg));
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
trace_cleanup(debug_context);
|
|
734
|
+
}
|
|
735
|
+
|
|
736
|
+
static void
|
|
737
|
+
return_tracepoint(VALUE tpval, void *data)
|
|
738
|
+
{
|
|
739
|
+
TRACE_SETUP;
|
|
740
|
+
|
|
741
|
+
debug_context->calced_stack_size --;
|
|
742
|
+
|
|
743
|
+
TRACE_COMMON();
|
|
744
|
+
|
|
745
|
+
if (debug_context->calced_stack_size + 1 == debug_context->stop_frame) {
|
|
746
|
+
debug_context->stop_next = 1;
|
|
747
|
+
debug_context->stop_frame = 0;
|
|
748
|
+
/* NOTE: can't use call_at_line function here to trigger a debugger event.
|
|
749
|
+
this can lead to segfault. We should only unroll the stack on this event.
|
|
750
|
+
*/
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
|
|
754
|
+
|
|
755
|
+
trace_cleanup(debug_context);
|
|
756
|
+
}
|
|
757
|
+
|
|
758
|
+
|
|
759
|
+
static void
|
|
760
|
+
misc_call_tracepoint(VALUE tpval, void *data)
|
|
761
|
+
{
|
|
762
|
+
TRACE_SETUP;
|
|
763
|
+
debug_context->calced_stack_size ++;
|
|
764
|
+
if (debug_debugger_stack_size && debug_debugger) {
|
|
765
|
+
fprintf(stderr, "[debug:stacksize] %d (add)\n", debug_context->calced_stack_size);
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
|
|
769
|
+
static void
|
|
770
|
+
misc_return_tracepoint(VALUE tpval, void *data)
|
|
771
|
+
{
|
|
772
|
+
TRACE_SETUP;
|
|
773
|
+
|
|
774
|
+
debug_context->calced_stack_size --;
|
|
775
|
+
|
|
776
|
+
if (debug_debugger_stack_size && debug_debugger) {
|
|
777
|
+
fprintf(stderr, "[debug:stacksize] %d (dec)\n", debug_context->calced_stack_size);
|
|
778
|
+
}
|
|
779
|
+
CTX_FL_SET(debug_context, CTX_FL_ENABLE_BKPT);
|
|
780
|
+
}
|
|
781
|
+
|
|
782
|
+
static void
|
|
783
|
+
raise_tracepoint(VALUE tpval, void *data)
|
|
784
|
+
{
|
|
785
|
+
VALUE ancestors;
|
|
786
|
+
VALUE expn_class, aclass;
|
|
787
|
+
VALUE binding;
|
|
788
|
+
VALUE err = rb_errinfo();
|
|
789
|
+
int i;
|
|
790
|
+
TRACE_SETUP;
|
|
791
|
+
|
|
792
|
+
TRACE_COMMON();
|
|
793
|
+
|
|
794
|
+
if (post_mortem == Qtrue) {
|
|
795
|
+
binding = rb_tracearg_binding(trace_arg);
|
|
796
|
+
rb_ivar_set(rb_errinfo(), rb_intern("@__debug_file"), rb_tracearg_path(trace_arg));
|
|
797
|
+
rb_ivar_set(rb_errinfo(), rb_intern("@__debug_line"), rb_tracearg_lineno(trace_arg));
|
|
798
|
+
rb_ivar_set(rb_errinfo(), rb_intern("@__debug_binding"), rb_tracearg_binding(trace_arg));
|
|
799
|
+
rb_ivar_set(rb_errinfo(), rb_intern("@__debug_context"), debug_context_dup(debug_context, rb_tracearg_self(trace_arg)));
|
|
800
|
+
}
|
|
801
|
+
|
|
802
|
+
expn_class = rb_obj_class(err);
|
|
803
|
+
|
|
804
|
+
if (rdebug_catchpoints == Qnil ||
|
|
805
|
+
(dc_stack_size(debug_context) == 0) ||
|
|
806
|
+
CTX_FL_TEST(debug_context, CTX_FL_CATCHING) ||
|
|
807
|
+
(RHASH_TBL(rdebug_catchpoints)->num_entries) == 0) {
|
|
808
|
+
goto cleanup;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
ancestors = rb_mod_ancestors(expn_class);
|
|
812
|
+
|
|
813
|
+
for (i = 0; i < RARRAY_LEN(ancestors); i++) {
|
|
814
|
+
VALUE mod_name;
|
|
815
|
+
VALUE hit_count;
|
|
816
|
+
|
|
817
|
+
aclass = rb_ary_entry(ancestors, i);
|
|
818
|
+
mod_name = rb_mod_name(aclass);
|
|
819
|
+
hit_count = rb_hash_aref(rdebug_catchpoints, mod_name);
|
|
820
|
+
|
|
821
|
+
if (hit_count != Qnil) {
|
|
822
|
+
/* increment exception */
|
|
823
|
+
rb_hash_aset(rdebug_catchpoints, mod_name, INT2FIX(FIX2INT(hit_count) + 1));
|
|
824
|
+
call_at_catchpoint(context, debug_context, err);
|
|
825
|
+
break;
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
cleanup:
|
|
830
|
+
trace_cleanup(debug_context);
|
|
831
|
+
}
|
|
832
|
+
|
|
833
|
+
|
|
834
|
+
static void
|
|
835
|
+
register_debug_trace_points(void)
|
|
836
|
+
{
|
|
837
|
+
int i;
|
|
838
|
+
VALUE traces = tracepoints;
|
|
839
|
+
|
|
840
|
+
if (NIL_P(traces)) {
|
|
841
|
+
traces = rb_ary_new();
|
|
842
|
+
|
|
843
|
+
rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_LINE, line_tracepoint, 0));
|
|
844
|
+
rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_CALL, call_tracepoint, 0));
|
|
845
|
+
rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_RAISE, raise_tracepoint, 0));
|
|
846
|
+
rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_RETURN | RUBY_EVENT_END, return_tracepoint, 0));
|
|
847
|
+
rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_C_CALL | RUBY_EVENT_B_CALL, misc_call_tracepoint, 0));
|
|
848
|
+
rb_ary_push(traces, rb_tracepoint_new(Qnil, RUBY_EVENT_C_RETURN | RUBY_EVENT_B_RETURN, misc_return_tracepoint, 0));
|
|
849
|
+
tracepoints = traces;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
for (i=0; i<RARRAY_LEN(traces); i++) {
|
|
853
|
+
rb_tracepoint_enable(rb_ary_entry(traces, i));
|
|
854
|
+
}
|
|
855
|
+
}
|
|
856
|
+
|
|
857
|
+
static void
|
|
858
|
+
clear_debug_trace_points(void)
|
|
859
|
+
{
|
|
860
|
+
int i;
|
|
861
|
+
|
|
862
|
+
for (i=0; i<RARRAY_LEN(tracepoints); i++) {
|
|
863
|
+
rb_tracepoint_disable(rb_ary_entry(tracepoints, i));
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
static VALUE
|
|
868
|
+
debug_stop_i(VALUE self)
|
|
869
|
+
{
|
|
870
|
+
if (IS_STARTED) {
|
|
871
|
+
debug_stop(self);
|
|
872
|
+
}
|
|
873
|
+
return Qnil;
|
|
874
|
+
}
|
|
875
|
+
|
|
876
|
+
/*
|
|
877
|
+
* call-seq:
|
|
878
|
+
* Debugger.start_ -> bool
|
|
879
|
+
* Debugger.start_ { ... } -> bool
|
|
880
|
+
*
|
|
881
|
+
* This method is internal and activates the debugger. Use
|
|
882
|
+
* Debugger.start (from <tt>lib/ruby-debug-base.rb</tt>) instead.
|
|
883
|
+
*
|
|
884
|
+
* The return value is the value of !Debugger.started? <i>before</i>
|
|
885
|
+
* issuing the +start+; That is, +true+ is returned, unless debugger
|
|
886
|
+
* was previously started.
|
|
887
|
+
|
|
888
|
+
* If a block is given, it starts debugger and yields to block. When
|
|
889
|
+
* the block is finished executing it stops the debugger with
|
|
890
|
+
* Debugger.stop method. Inside the block you will probably want to
|
|
891
|
+
* have a call to Debugger.debugger. For example:
|
|
892
|
+
* Debugger.start{debugger; foo} # Stop inside of foo
|
|
893
|
+
*
|
|
894
|
+
* Also, ruby-debug only allows
|
|
895
|
+
* one invocation of debugger at a time; nested Debugger.start's
|
|
896
|
+
* have no effect and you can't use this inside the debugger itself.
|
|
897
|
+
*
|
|
898
|
+
* <i>Note that if you want to completely remove the debugger hook,
|
|
899
|
+
* you must call Debugger.stop as many times as you called
|
|
900
|
+
* Debugger.start method.</i>
|
|
901
|
+
*/
|
|
902
|
+
static VALUE
|
|
903
|
+
debug_start(VALUE self)
|
|
904
|
+
{
|
|
905
|
+
VALUE result;
|
|
906
|
+
start_count++;
|
|
907
|
+
|
|
908
|
+
if(IS_STARTED) {
|
|
909
|
+
result = Qfalse;
|
|
910
|
+
}
|
|
911
|
+
else {
|
|
912
|
+
locker = Qnil;
|
|
913
|
+
rdebug_breakpoints = rb_ary_new();
|
|
914
|
+
rdebug_catchpoints = rb_hash_new();
|
|
915
|
+
rdebug_threads_tbl = threads_table_create();
|
|
916
|
+
|
|
917
|
+
register_debug_trace_points();
|
|
918
|
+
debug_context_create(rb_thread_current());
|
|
919
|
+
result = Qtrue;
|
|
920
|
+
}
|
|
921
|
+
|
|
922
|
+
if(rb_block_given_p())
|
|
923
|
+
rb_ensure(rb_yield, self, debug_stop_i, self);
|
|
924
|
+
|
|
925
|
+
return result;
|
|
926
|
+
}
|
|
927
|
+
|
|
928
|
+
/*
|
|
929
|
+
* call-seq:
|
|
930
|
+
* Debugger.stop -> bool
|
|
931
|
+
*
|
|
932
|
+
* This method disables the debugger. It returns +true+ if the debugger is disabled,
|
|
933
|
+
* otherwise it returns +false+.
|
|
934
|
+
*
|
|
935
|
+
* <i>Note that if you want to complete remove the debugger hook,
|
|
936
|
+
* you must call Debugger.stop as many times as you called
|
|
937
|
+
* Debugger.start method.</i>
|
|
938
|
+
*/
|
|
939
|
+
static VALUE
|
|
940
|
+
debug_stop(VALUE self)
|
|
941
|
+
{
|
|
942
|
+
debug_check_started();
|
|
943
|
+
|
|
944
|
+
start_count--;
|
|
945
|
+
if(start_count)
|
|
946
|
+
return Qfalse;
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
clear_debug_trace_points();
|
|
950
|
+
|
|
951
|
+
locker = Qnil;
|
|
952
|
+
rdebug_breakpoints = Qnil;
|
|
953
|
+
rdebug_threads_tbl = Qnil;
|
|
954
|
+
|
|
955
|
+
return Qtrue;
|
|
956
|
+
}
|
|
957
|
+
|
|
958
|
+
static int
|
|
959
|
+
find_last_context_func(st_data_t key, st_data_t value, st_data_t *result_arg)
|
|
960
|
+
{
|
|
961
|
+
debug_context_t *debug_context;
|
|
962
|
+
VALUE *result = (VALUE*)result_arg;
|
|
963
|
+
if(!value)
|
|
964
|
+
return ST_CONTINUE;
|
|
965
|
+
|
|
966
|
+
Data_Get_Struct((VALUE)value, debug_context_t, debug_context);
|
|
967
|
+
if(debug_context->thnum == last_debugged_thnum)
|
|
968
|
+
{
|
|
969
|
+
*result = value;
|
|
970
|
+
return ST_STOP;
|
|
971
|
+
}
|
|
972
|
+
return ST_CONTINUE;
|
|
973
|
+
}
|
|
974
|
+
|
|
975
|
+
/*
|
|
976
|
+
* call-seq:
|
|
977
|
+
* Debugger.last_interrupted -> context
|
|
978
|
+
*
|
|
979
|
+
* Returns last debugged context.
|
|
980
|
+
*/
|
|
981
|
+
static VALUE
|
|
982
|
+
debug_last_interrupted(VALUE self)
|
|
983
|
+
{
|
|
984
|
+
VALUE result = Qnil;
|
|
985
|
+
threads_table_t *threads_table;
|
|
986
|
+
|
|
987
|
+
debug_check_started();
|
|
988
|
+
|
|
989
|
+
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
|
|
990
|
+
|
|
991
|
+
st_foreach(threads_table->tbl, find_last_context_func, (st_data_t)&result);
|
|
992
|
+
return result;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
/*
|
|
996
|
+
* call-seq:
|
|
997
|
+
* Debugger.current_context -> context
|
|
998
|
+
*
|
|
999
|
+
* Returns current context.
|
|
1000
|
+
* <i>Note:</i> Debugger.current_context.thread == Thread.current
|
|
1001
|
+
*/
|
|
1002
|
+
static VALUE
|
|
1003
|
+
debug_current_context(VALUE self)
|
|
1004
|
+
{
|
|
1005
|
+
VALUE thread, context;
|
|
1006
|
+
|
|
1007
|
+
debug_check_started();
|
|
1008
|
+
|
|
1009
|
+
thread = rb_thread_current();
|
|
1010
|
+
thread_context_lookup(thread, &context, NULL, 1);
|
|
1011
|
+
|
|
1012
|
+
return context;
|
|
1013
|
+
}
|
|
1014
|
+
|
|
1015
|
+
/*
|
|
1016
|
+
* call-seq:
|
|
1017
|
+
* Debugger.thread_context(thread) -> context
|
|
1018
|
+
*
|
|
1019
|
+
* Returns context of the thread passed as an argument.
|
|
1020
|
+
*/
|
|
1021
|
+
static VALUE
|
|
1022
|
+
debug_thread_context(VALUE self, VALUE thread)
|
|
1023
|
+
{
|
|
1024
|
+
VALUE context;
|
|
1025
|
+
|
|
1026
|
+
debug_check_started();
|
|
1027
|
+
thread_context_lookup(thread, &context, NULL, 1);
|
|
1028
|
+
return context;
|
|
1029
|
+
}
|
|
1030
|
+
|
|
1031
|
+
/*
|
|
1032
|
+
* call-seq:
|
|
1033
|
+
* Debugger.contexts -> array
|
|
1034
|
+
*
|
|
1035
|
+
* Returns an array of all contexts.
|
|
1036
|
+
*/
|
|
1037
|
+
static VALUE
|
|
1038
|
+
debug_contexts(VALUE self)
|
|
1039
|
+
{
|
|
1040
|
+
volatile VALUE list;
|
|
1041
|
+
volatile VALUE new_list;
|
|
1042
|
+
VALUE thread, context;
|
|
1043
|
+
threads_table_t *threads_table;
|
|
1044
|
+
debug_context_t *debug_context;
|
|
1045
|
+
int i;
|
|
1046
|
+
|
|
1047
|
+
debug_check_started();
|
|
1048
|
+
|
|
1049
|
+
new_list = rb_ary_new();
|
|
1050
|
+
list = rb_funcall(rb_cThread, rb_intern("list"), 0);
|
|
1051
|
+
|
|
1052
|
+
for (i = 0; i < RARRAY_LEN(list); i++) {
|
|
1053
|
+
thread = rb_ary_entry(list, i);
|
|
1054
|
+
thread_context_lookup(thread, &context, NULL, 1);
|
|
1055
|
+
rb_ary_push(new_list, context);
|
|
1056
|
+
}
|
|
1057
|
+
|
|
1058
|
+
threads_table_clear(rdebug_threads_tbl);
|
|
1059
|
+
Data_Get_Struct(rdebug_threads_tbl, threads_table_t, threads_table);
|
|
1060
|
+
|
|
1061
|
+
for (i = 0; i < RARRAY_LEN(new_list); i++) {
|
|
1062
|
+
context = rb_ary_entry(new_list, i);
|
|
1063
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
|
1064
|
+
st_insert(threads_table->tbl, debug_context->thread_id, context);
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
return new_list;
|
|
1068
|
+
}
|
|
1069
|
+
|
|
1070
|
+
/*
|
|
1071
|
+
* call-seq:
|
|
1072
|
+
* Debugger.suspend -> Debugger
|
|
1073
|
+
*
|
|
1074
|
+
* Suspends all contexts.
|
|
1075
|
+
*/
|
|
1076
|
+
static VALUE
|
|
1077
|
+
debug_suspend(VALUE self)
|
|
1078
|
+
{
|
|
1079
|
+
VALUE current, context;
|
|
1080
|
+
VALUE context_list;
|
|
1081
|
+
debug_context_t *debug_context;
|
|
1082
|
+
int i;
|
|
1083
|
+
|
|
1084
|
+
debug_check_started();
|
|
1085
|
+
|
|
1086
|
+
context_list = debug_contexts(self);
|
|
1087
|
+
thread_context_lookup(rb_thread_current(), ¤t, NULL, 1);
|
|
1088
|
+
|
|
1089
|
+
for(i = 0; i < RARRAY_LEN(context_list); i++)
|
|
1090
|
+
{
|
|
1091
|
+
context = rb_ary_entry(context_list, i);
|
|
1092
|
+
if(current == context)
|
|
1093
|
+
continue;
|
|
1094
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
|
1095
|
+
context_suspend_0(debug_context);
|
|
1096
|
+
}
|
|
1097
|
+
|
|
1098
|
+
return self;
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
/*
|
|
1102
|
+
* call-seq:
|
|
1103
|
+
* Debugger.resume -> Debugger
|
|
1104
|
+
*
|
|
1105
|
+
* Resumes all contexts.
|
|
1106
|
+
*/
|
|
1107
|
+
static VALUE
|
|
1108
|
+
debug_resume(VALUE self)
|
|
1109
|
+
{
|
|
1110
|
+
VALUE current, context;
|
|
1111
|
+
VALUE context_list;
|
|
1112
|
+
debug_context_t *debug_context;
|
|
1113
|
+
int i;
|
|
1114
|
+
|
|
1115
|
+
debug_check_started();
|
|
1116
|
+
|
|
1117
|
+
context_list = debug_contexts(self);
|
|
1118
|
+
|
|
1119
|
+
thread_context_lookup(rb_thread_current(), ¤t, NULL, 1);
|
|
1120
|
+
for(i = 0; i < RARRAY_LEN(context_list); i++)
|
|
1121
|
+
{
|
|
1122
|
+
context = rb_ary_entry(context_list, i);
|
|
1123
|
+
if(current == context)
|
|
1124
|
+
continue;
|
|
1125
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
|
1126
|
+
context_resume_0(debug_context);
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
rb_thread_schedule();
|
|
1130
|
+
|
|
1131
|
+
return self;
|
|
1132
|
+
}
|
|
1133
|
+
|
|
1134
|
+
/*
|
|
1135
|
+
* call-seq:
|
|
1136
|
+
* Debugger.tracing -> bool
|
|
1137
|
+
*
|
|
1138
|
+
* Returns +true+ if the global tracing is activated.
|
|
1139
|
+
*/
|
|
1140
|
+
static VALUE
|
|
1141
|
+
debug_tracing(VALUE self)
|
|
1142
|
+
{
|
|
1143
|
+
return tracing;
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
/*
|
|
1147
|
+
* call-seq:
|
|
1148
|
+
* Debugger.tracing = bool
|
|
1149
|
+
*
|
|
1150
|
+
* Sets the global tracing flag.
|
|
1151
|
+
*/
|
|
1152
|
+
static VALUE
|
|
1153
|
+
debug_set_tracing(VALUE self, VALUE value)
|
|
1154
|
+
{
|
|
1155
|
+
tracing = RTEST(value) ? Qtrue : Qfalse;
|
|
1156
|
+
return value;
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
/*
|
|
1160
|
+
* call-seq:
|
|
1161
|
+
* Debugger.post_mortem? -> bool
|
|
1162
|
+
*
|
|
1163
|
+
* Returns +true+ if post-moterm debugging is enabled.
|
|
1164
|
+
*/
|
|
1165
|
+
static VALUE
|
|
1166
|
+
debug_post_mortem(VALUE self)
|
|
1167
|
+
{
|
|
1168
|
+
return post_mortem;
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
/*
|
|
1172
|
+
* call-seq:
|
|
1173
|
+
* Debugger.post_mortem = bool
|
|
1174
|
+
*
|
|
1175
|
+
* Sets post-moterm flag.
|
|
1176
|
+
* FOR INTERNAL USE ONLY.
|
|
1177
|
+
*/
|
|
1178
|
+
static VALUE
|
|
1179
|
+
debug_set_post_mortem(VALUE self, VALUE value)
|
|
1180
|
+
{
|
|
1181
|
+
debug_check_started();
|
|
1182
|
+
|
|
1183
|
+
post_mortem = RTEST(value) ? Qtrue : Qfalse;
|
|
1184
|
+
return value;
|
|
1185
|
+
}
|
|
1186
|
+
|
|
1187
|
+
/*
|
|
1188
|
+
* call-seq:
|
|
1189
|
+
* Debugger.track_fame_args? -> bool
|
|
1190
|
+
*
|
|
1191
|
+
* Returns +true+ if the debugger track frame argument values on calls.
|
|
1192
|
+
*/
|
|
1193
|
+
static VALUE
|
|
1194
|
+
debug_track_frame_args(VALUE self)
|
|
1195
|
+
{
|
|
1196
|
+
return track_frame_args;
|
|
1197
|
+
}
|
|
1198
|
+
|
|
1199
|
+
/*
|
|
1200
|
+
* call-seq:
|
|
1201
|
+
* Debugger.track_frame_args = bool
|
|
1202
|
+
*
|
|
1203
|
+
* Setting to +true+ will make the debugger save argument info on calls.
|
|
1204
|
+
*/
|
|
1205
|
+
static VALUE
|
|
1206
|
+
debug_set_track_frame_args(VALUE self, VALUE value)
|
|
1207
|
+
{
|
|
1208
|
+
track_frame_args = RTEST(value) ? Qtrue : Qfalse;
|
|
1209
|
+
return value;
|
|
1210
|
+
}
|
|
1211
|
+
|
|
1212
|
+
/*
|
|
1213
|
+
* call-seq:
|
|
1214
|
+
* Debugger.keep_frame_binding? -> bool
|
|
1215
|
+
*
|
|
1216
|
+
* Returns +true+ if the debugger will collect frame bindings.
|
|
1217
|
+
*/
|
|
1218
|
+
static VALUE
|
|
1219
|
+
debug_keep_frame_binding(VALUE self)
|
|
1220
|
+
{
|
|
1221
|
+
return keep_frame_binding;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
/*
|
|
1225
|
+
* call-seq:
|
|
1226
|
+
* Debugger.keep_frame_binding = bool
|
|
1227
|
+
*
|
|
1228
|
+
* Setting to +true+ will make the debugger create frame bindings.
|
|
1229
|
+
*/
|
|
1230
|
+
static VALUE
|
|
1231
|
+
debug_set_keep_frame_binding(VALUE self, VALUE value)
|
|
1232
|
+
{
|
|
1233
|
+
keep_frame_binding = RTEST(value) ? Qtrue : Qfalse;
|
|
1234
|
+
return value;
|
|
1235
|
+
}
|
|
1236
|
+
|
|
1237
|
+
/* :nodoc: */
|
|
1238
|
+
static VALUE
|
|
1239
|
+
debug_debug(VALUE self)
|
|
1240
|
+
{
|
|
1241
|
+
return debug_debugger;
|
|
1242
|
+
}
|
|
1243
|
+
|
|
1244
|
+
/* :nodoc: */
|
|
1245
|
+
static VALUE
|
|
1246
|
+
debug_set_debug(VALUE self, VALUE value)
|
|
1247
|
+
{
|
|
1248
|
+
debug_debugger = RTEST(value) ? Qtrue : Qfalse;
|
|
1249
|
+
return value;
|
|
1250
|
+
}
|
|
1251
|
+
|
|
1252
|
+
/* :nodoc: */
|
|
1253
|
+
static VALUE
|
|
1254
|
+
debug_thread_inherited(VALUE klass)
|
|
1255
|
+
{
|
|
1256
|
+
rb_raise(rb_eRuntimeError, "Can't inherit Debugger::DebugThread class");
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
/*
|
|
1260
|
+
* call-seq:
|
|
1261
|
+
* Debugger.debug_load(file, stop = false, increment_start = false) -> nil
|
|
1262
|
+
*
|
|
1263
|
+
* Same as Kernel#load but resets current context's frames.
|
|
1264
|
+
* +stop+ parameter forces the debugger to stop at the first line of code in the +file+
|
|
1265
|
+
* +increment_start+ determines if start_count should be incremented. When
|
|
1266
|
+
* control threads are used, they have to be set up before loading the
|
|
1267
|
+
* debugger; so here +increment_start+ will be false.
|
|
1268
|
+
* FOR INTERNAL USE ONLY.
|
|
1269
|
+
*/
|
|
1270
|
+
static VALUE
|
|
1271
|
+
debug_debug_load(int argc, VALUE *argv, VALUE self)
|
|
1272
|
+
{
|
|
1273
|
+
VALUE file, stop, context, increment_start;
|
|
1274
|
+
debug_context_t *debug_context;
|
|
1275
|
+
int state = 0;
|
|
1276
|
+
|
|
1277
|
+
if (rb_scan_args(argc, argv, "12", &file, &stop, &increment_start) == 1) {
|
|
1278
|
+
stop = Qfalse;
|
|
1279
|
+
increment_start = Qtrue;
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
debug_start(self);
|
|
1283
|
+
if (Qfalse == increment_start) start_count--;
|
|
1284
|
+
|
|
1285
|
+
context = debug_current_context(self);
|
|
1286
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
|
1287
|
+
|
|
1288
|
+
/* debug_context->stack_size = 0; */
|
|
1289
|
+
|
|
1290
|
+
if (RTEST(stop)) {
|
|
1291
|
+
debug_context->stop_next = 1;
|
|
1292
|
+
}
|
|
1293
|
+
/* Initializing $0 to the script's path */
|
|
1294
|
+
ruby_script(RSTRING_PTR(file));
|
|
1295
|
+
rb_load_protect(file, 0, &state);
|
|
1296
|
+
if (0 != state)
|
|
1297
|
+
{
|
|
1298
|
+
VALUE errinfo = rb_errinfo();
|
|
1299
|
+
debug_suspend(self);
|
|
1300
|
+
reset_stepping_stop_points(debug_context);
|
|
1301
|
+
rb_set_errinfo(Qnil);
|
|
1302
|
+
return errinfo;
|
|
1303
|
+
}
|
|
1304
|
+
|
|
1305
|
+
/* We should run all at_exit handler's in order to provide,
|
|
1306
|
+
* for instance, a chance to run all defined test cases */
|
|
1307
|
+
rb_exec_end_proc();
|
|
1308
|
+
|
|
1309
|
+
/* We could have issued a Debugger.stop inside the debug
|
|
1310
|
+
session. */
|
|
1311
|
+
if (start_count > 0)
|
|
1312
|
+
debug_stop(self);
|
|
1313
|
+
|
|
1314
|
+
return Qnil;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
static VALUE
|
|
1318
|
+
set_current_skipped_status(VALUE status)
|
|
1319
|
+
{
|
|
1320
|
+
VALUE context;
|
|
1321
|
+
debug_context_t *debug_context;
|
|
1322
|
+
|
|
1323
|
+
context = debug_current_context(Qnil);
|
|
1324
|
+
Data_Get_Struct(context, debug_context_t, debug_context);
|
|
1325
|
+
if (status) {
|
|
1326
|
+
CTX_FL_SET(debug_context, CTX_FL_SKIPPED);
|
|
1327
|
+
}
|
|
1328
|
+
else {
|
|
1329
|
+
CTX_FL_UNSET(debug_context, CTX_FL_SKIPPED);
|
|
1330
|
+
}
|
|
1331
|
+
return Qnil;
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
/*
|
|
1335
|
+
* call-seq:
|
|
1336
|
+
* Debugger.skip { block } -> obj or nil
|
|
1337
|
+
*
|
|
1338
|
+
* The code inside of the block is escaped from the debugger.
|
|
1339
|
+
*/
|
|
1340
|
+
static VALUE
|
|
1341
|
+
debug_skip(VALUE self)
|
|
1342
|
+
{
|
|
1343
|
+
if (!rb_block_given_p()) {
|
|
1344
|
+
rb_raise(rb_eArgError, "called without a block");
|
|
1345
|
+
}
|
|
1346
|
+
|
|
1347
|
+
if (!IS_STARTED) {
|
|
1348
|
+
return rb_yield(Qnil);
|
|
1349
|
+
}
|
|
1350
|
+
set_current_skipped_status(Qtrue);
|
|
1351
|
+
return rb_ensure(rb_yield, Qnil, set_current_skipped_status, Qfalse);
|
|
1352
|
+
}
|
|
1353
|
+
|
|
1354
|
+
static VALUE
|
|
1355
|
+
debug_at_exit_c(VALUE proc)
|
|
1356
|
+
{
|
|
1357
|
+
return rb_funcall(proc, rb_intern("call"), 0);
|
|
1358
|
+
}
|
|
1359
|
+
|
|
1360
|
+
static void
|
|
1361
|
+
debug_at_exit_i(VALUE proc)
|
|
1362
|
+
{
|
|
1363
|
+
if(!IS_STARTED)
|
|
1364
|
+
{
|
|
1365
|
+
debug_at_exit_c(proc);
|
|
1366
|
+
}
|
|
1367
|
+
else
|
|
1368
|
+
{
|
|
1369
|
+
set_current_skipped_status(Qtrue);
|
|
1370
|
+
rb_ensure(debug_at_exit_c, proc, set_current_skipped_status, Qfalse);
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
|
|
1374
|
+
/*
|
|
1375
|
+
* call-seq:
|
|
1376
|
+
* Debugger.debug_at_exit { block } -> proc
|
|
1377
|
+
*
|
|
1378
|
+
* Register <tt>at_exit</tt> hook which is escaped from the debugger.
|
|
1379
|
+
* FOR INTERNAL USE ONLY.
|
|
1380
|
+
*/
|
|
1381
|
+
static VALUE
|
|
1382
|
+
debug_at_exit(VALUE self)
|
|
1383
|
+
{
|
|
1384
|
+
VALUE proc;
|
|
1385
|
+
if (!rb_block_given_p())
|
|
1386
|
+
rb_raise(rb_eArgError, "called without a block");
|
|
1387
|
+
proc = rb_block_proc();
|
|
1388
|
+
rb_set_end_proc(debug_at_exit_i, proc);
|
|
1389
|
+
return proc;
|
|
1390
|
+
}
|
|
1391
|
+
|
|
1392
|
+
/*
|
|
1393
|
+
* call-seq:
|
|
1394
|
+
* context.step(steps, force = false)
|
|
1395
|
+
*
|
|
1396
|
+
* Stops the current context after a number of +steps+ are made.
|
|
1397
|
+
* +force+ parameter (if true) ensures that the cursor moves from the current line.
|
|
1398
|
+
*/
|
|
1399
|
+
static VALUE
|
|
1400
|
+
context_stop_next(int argc, VALUE *argv, VALUE self)
|
|
1401
|
+
{
|
|
1402
|
+
VALUE steps, force;
|
|
1403
|
+
debug_context_t *debug_context;
|
|
1404
|
+
|
|
1405
|
+
debug_check_started();
|
|
1406
|
+
|
|
1407
|
+
rb_scan_args(argc, argv, "11", &steps, &force);
|
|
1408
|
+
if (FIX2INT(steps) < 0) {
|
|
1409
|
+
rb_raise(rb_eRuntimeError, "Steps argument can't be negative.");
|
|
1410
|
+
}
|
|
1411
|
+
|
|
1412
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1413
|
+
|
|
1414
|
+
debug_context->stop_next = FIX2INT(steps);
|
|
1415
|
+
|
|
1416
|
+
if (RTEST(force)) {
|
|
1417
|
+
CTX_FL_SET(debug_context, CTX_FL_FORCE_MOVE);
|
|
1418
|
+
}
|
|
1419
|
+
else {
|
|
1420
|
+
CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
|
|
1421
|
+
}
|
|
1422
|
+
|
|
1423
|
+
return steps;
|
|
1424
|
+
}
|
|
1425
|
+
|
|
1426
|
+
/*
|
|
1427
|
+
* call-seq:
|
|
1428
|
+
* context.step_over(steps, frame = nil, force = false)
|
|
1429
|
+
*
|
|
1430
|
+
* Steps over a +steps+ number of times.
|
|
1431
|
+
* Make step over operation on +frame+, by default the current frame.
|
|
1432
|
+
* +force+ parameter (if true) ensures that the cursor moves from the current line.
|
|
1433
|
+
*/
|
|
1434
|
+
static VALUE
|
|
1435
|
+
context_step_over(int argc, VALUE *argv, VALUE self)
|
|
1436
|
+
{
|
|
1437
|
+
VALUE lines, frame, force;
|
|
1438
|
+
debug_context_t *debug_context;
|
|
1439
|
+
|
|
1440
|
+
debug_check_started();
|
|
1441
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1442
|
+
|
|
1443
|
+
if (dc_stack_size(debug_context) == 0) {
|
|
1444
|
+
rb_raise(rb_eRuntimeError, "No frames collected.");
|
|
1445
|
+
}
|
|
1446
|
+
|
|
1447
|
+
rb_scan_args(argc, argv, "12", &lines, &frame, &force);
|
|
1448
|
+
debug_context->stop_line = FIX2INT(lines);
|
|
1449
|
+
CTX_FL_UNSET(debug_context, CTX_FL_STEPPED);
|
|
1450
|
+
|
|
1451
|
+
if (frame == Qnil) {
|
|
1452
|
+
debug_context->dest_frame = dc_stack_size(debug_context);
|
|
1453
|
+
}
|
|
1454
|
+
else {
|
|
1455
|
+
if (FIX2INT(frame) < 0 && FIX2INT(frame) >= dc_stack_size(debug_context)) {
|
|
1456
|
+
rb_raise(rb_eRuntimeError, "Destination frame is out of range.");
|
|
1457
|
+
debug_context->dest_frame = dc_stack_size(debug_context) - FIX2INT(frame);
|
|
1458
|
+
}
|
|
1459
|
+
}
|
|
1460
|
+
|
|
1461
|
+
if (RTEST(force)) {
|
|
1462
|
+
CTX_FL_SET(debug_context, CTX_FL_FORCE_MOVE);
|
|
1463
|
+
}
|
|
1464
|
+
else {
|
|
1465
|
+
CTX_FL_UNSET(debug_context, CTX_FL_FORCE_MOVE);
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
return Qnil;
|
|
1469
|
+
}
|
|
1470
|
+
|
|
1471
|
+
/*
|
|
1472
|
+
* call-seq:
|
|
1473
|
+
* context.stop_frame(frame)
|
|
1474
|
+
*
|
|
1475
|
+
* Stops when a frame with number +frame+ is activated. Implements +finish+ and +next+ commands.
|
|
1476
|
+
*/
|
|
1477
|
+
static VALUE
|
|
1478
|
+
context_stop_frame(VALUE self, VALUE frame)
|
|
1479
|
+
{
|
|
1480
|
+
debug_context_t *debug_context;
|
|
1481
|
+
|
|
1482
|
+
debug_check_started();
|
|
1483
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1484
|
+
|
|
1485
|
+
if (FIX2INT(frame) < 0 || FIX2INT(frame) >= dc_stack_size(debug_context)) {
|
|
1486
|
+
rb_raise(rb_eRuntimeError, "Stop frame is out of range.");
|
|
1487
|
+
}
|
|
1488
|
+
debug_context->stop_frame = dc_stack_size(debug_context) - FIX2INT(frame);
|
|
1489
|
+
|
|
1490
|
+
return frame;
|
|
1491
|
+
}
|
|
1492
|
+
|
|
1493
|
+
static int
|
|
1494
|
+
check_frame_number(debug_context_t *debug_context, int frame)
|
|
1495
|
+
{
|
|
1496
|
+
if (frame < 0 || frame >= dc_stack_size(debug_context)) {
|
|
1497
|
+
rb_raise(rb_eArgError, "Invalid frame number %d, stack (0...%d)",
|
|
1498
|
+
frame, dc_stack_size(debug_context) - 1);
|
|
1499
|
+
}
|
|
1500
|
+
|
|
1501
|
+
return frame;
|
|
1502
|
+
}
|
|
1503
|
+
|
|
1504
|
+
static int
|
|
1505
|
+
optional_frame_position(int argc, VALUE *argv)
|
|
1506
|
+
{
|
|
1507
|
+
VALUE level = INT2FIX(0);
|
|
1508
|
+
|
|
1509
|
+
if ((argc > 1) || (argc < 0)) {
|
|
1510
|
+
rb_raise(rb_eArgError, "wrong number of arguments (%d for 0 or 1)", argc);
|
|
1511
|
+
}
|
|
1512
|
+
rb_scan_args(argc, argv, "01", &level);
|
|
1513
|
+
return FIX2INT(level);
|
|
1514
|
+
}
|
|
1515
|
+
|
|
1516
|
+
/*
|
|
1517
|
+
* call-seq:
|
|
1518
|
+
* context.frame_args_info(frame_position=0) -> list
|
|
1519
|
+
if track_frame_args or nil otherwise
|
|
1520
|
+
*
|
|
1521
|
+
* Returns info saved about call arguments (if any saved).
|
|
1522
|
+
*/
|
|
1523
|
+
static VALUE
|
|
1524
|
+
context_frame_args_info(int argc, VALUE *argv, VALUE self)
|
|
1525
|
+
{
|
|
1526
|
+
int frame;
|
|
1527
|
+
debug_context_t *debug_context;
|
|
1528
|
+
VALUE binding;
|
|
1529
|
+
const char src[] = "method(__method__).parameters";
|
|
1530
|
+
|
|
1531
|
+
debug_check_started();
|
|
1532
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1533
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1534
|
+
|
|
1535
|
+
binding = dc_inspected_frame_binding(debug_context, frame);
|
|
1536
|
+
return NIL_P(binding) ? rb_ary_new() : rb_funcall(binding, rb_intern("eval"), 1, rb_str_new2(src));
|
|
1537
|
+
}
|
|
1538
|
+
|
|
1539
|
+
/*
|
|
1540
|
+
* call-seq:
|
|
1541
|
+
* context.frame_binding(frame_position=0) -> binding
|
|
1542
|
+
*
|
|
1543
|
+
* Returns frame's binding.
|
|
1544
|
+
*/
|
|
1545
|
+
static VALUE
|
|
1546
|
+
context_frame_binding(int argc, VALUE *argv, VALUE self)
|
|
1547
|
+
{
|
|
1548
|
+
int frame;
|
|
1549
|
+
debug_context_t *debug_context;
|
|
1550
|
+
|
|
1551
|
+
debug_check_started();
|
|
1552
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1553
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1554
|
+
|
|
1555
|
+
return dc_inspected_frame_binding(debug_context, frame);
|
|
1556
|
+
}
|
|
1557
|
+
|
|
1558
|
+
/*
|
|
1559
|
+
* call-seq:
|
|
1560
|
+
* context.frame_method(frame_position=0) -> sym
|
|
1561
|
+
*
|
|
1562
|
+
* Returns the sym of the called method.
|
|
1563
|
+
*/
|
|
1564
|
+
static VALUE
|
|
1565
|
+
context_frame_id(int argc, VALUE *argv, VALUE self)
|
|
1566
|
+
{
|
|
1567
|
+
int frame;
|
|
1568
|
+
debug_context_t *debug_context;
|
|
1569
|
+
VALUE loc;
|
|
1570
|
+
|
|
1571
|
+
debug_check_started();
|
|
1572
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1573
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1574
|
+
loc = dc_inspected_frame_location(debug_context, frame);
|
|
1575
|
+
|
|
1576
|
+
return rb_str_intern(rb_funcall(loc, rb_intern("label"), 0));
|
|
1577
|
+
}
|
|
1578
|
+
|
|
1579
|
+
/*
|
|
1580
|
+
* call-seq:
|
|
1581
|
+
* context.frame_line(frame_position) -> int
|
|
1582
|
+
*
|
|
1583
|
+
* Returns the line number in the file.
|
|
1584
|
+
*/
|
|
1585
|
+
static VALUE
|
|
1586
|
+
context_frame_line(int argc, VALUE *argv, VALUE self)
|
|
1587
|
+
{
|
|
1588
|
+
int frame;
|
|
1589
|
+
debug_context_t *debug_context;
|
|
1590
|
+
VALUE loc;
|
|
1591
|
+
|
|
1592
|
+
debug_check_started();
|
|
1593
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1594
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1595
|
+
loc = dc_inspected_frame_location(debug_context, frame);
|
|
1596
|
+
|
|
1597
|
+
return rb_funcall(loc, rb_intern("lineno"), 0);
|
|
1598
|
+
}
|
|
1599
|
+
|
|
1600
|
+
/*
|
|
1601
|
+
* call-seq:
|
|
1602
|
+
* context.frame_file(frame_position) -> string
|
|
1603
|
+
*
|
|
1604
|
+
* Returns the name of the file.
|
|
1605
|
+
*/
|
|
1606
|
+
static VALUE
|
|
1607
|
+
context_frame_file(int argc, VALUE *argv, VALUE self)
|
|
1608
|
+
{
|
|
1609
|
+
int frame;
|
|
1610
|
+
debug_context_t *debug_context;
|
|
1611
|
+
VALUE loc;
|
|
1612
|
+
|
|
1613
|
+
debug_check_started();
|
|
1614
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1615
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1616
|
+
loc = dc_inspected_frame_location(debug_context, frame);
|
|
1617
|
+
|
|
1618
|
+
return rb_funcall(loc, rb_intern("absolute_path"), 0);
|
|
1619
|
+
}
|
|
1620
|
+
|
|
1621
|
+
/*
|
|
1622
|
+
* call-seq:
|
|
1623
|
+
* context.frame_locals(frame) -> hash
|
|
1624
|
+
*
|
|
1625
|
+
* Returns frame's local variables.
|
|
1626
|
+
*/
|
|
1627
|
+
static VALUE
|
|
1628
|
+
context_frame_locals(int argc, VALUE *argv, VALUE self)
|
|
1629
|
+
{
|
|
1630
|
+
int frame;
|
|
1631
|
+
debug_context_t *debug_context;
|
|
1632
|
+
VALUE binding;
|
|
1633
|
+
const char src[] = "local_variables.inject({}){|h, v| h[v] = eval(\"#{v}\"); h}";
|
|
1634
|
+
|
|
1635
|
+
debug_check_started();
|
|
1636
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1637
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1638
|
+
|
|
1639
|
+
|
|
1640
|
+
binding = dc_inspected_frame_binding(debug_context, frame);
|
|
1641
|
+
return NIL_P(binding) ? rb_hash_new() : rb_funcall(binding, rb_intern("eval"), 1, rb_str_new2(src));
|
|
1642
|
+
}
|
|
1643
|
+
|
|
1644
|
+
/*
|
|
1645
|
+
* call-seq:
|
|
1646
|
+
* context.frame_args(frame_position=0) -> list
|
|
1647
|
+
*
|
|
1648
|
+
* Returns frame's argument parameters
|
|
1649
|
+
*/
|
|
1650
|
+
static VALUE
|
|
1651
|
+
context_frame_args(int argc, VALUE *argv, VALUE self)
|
|
1652
|
+
{
|
|
1653
|
+
int frame;
|
|
1654
|
+
debug_context_t *debug_context;
|
|
1655
|
+
VALUE binding;
|
|
1656
|
+
const char src[] = "__method__ ? self.method(__method__).parameters.map{|(attr, mid)| mid} : []";
|
|
1657
|
+
|
|
1658
|
+
debug_check_started();
|
|
1659
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1660
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1661
|
+
|
|
1662
|
+
binding = dc_inspected_frame_binding(debug_context, frame);
|
|
1663
|
+
return NIL_P(binding) ? rb_ary_new() : rb_funcall(binding, rb_intern("eval"), 1, rb_str_new2(src));
|
|
1664
|
+
}
|
|
1665
|
+
|
|
1666
|
+
/*
|
|
1667
|
+
* call-seq:
|
|
1668
|
+
* context.frame_self(frame_postion=0) -> obj
|
|
1669
|
+
*
|
|
1670
|
+
* Returns self object of the frame.
|
|
1671
|
+
*/
|
|
1672
|
+
static VALUE
|
|
1673
|
+
context_frame_self(int argc, VALUE *argv, VALUE self)
|
|
1674
|
+
{
|
|
1675
|
+
int frame;
|
|
1676
|
+
debug_context_t *debug_context;
|
|
1677
|
+
|
|
1678
|
+
debug_check_started();
|
|
1679
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1680
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1681
|
+
|
|
1682
|
+
return dc_inspected_frame_self(debug_context, frame);
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
/*
|
|
1686
|
+
* call-seq:
|
|
1687
|
+
* context.frame_class(frame_position) -> obj
|
|
1688
|
+
*
|
|
1689
|
+
* Returns the real class of the frame.
|
|
1690
|
+
* It could be different than context.frame_self(frame).class
|
|
1691
|
+
*/
|
|
1692
|
+
static VALUE
|
|
1693
|
+
context_frame_class(int argc, VALUE *argv, VALUE self)
|
|
1694
|
+
{
|
|
1695
|
+
int frame;
|
|
1696
|
+
debug_context_t *debug_context;
|
|
1697
|
+
|
|
1698
|
+
debug_check_started();
|
|
1699
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1700
|
+
frame = check_frame_number(debug_context, optional_frame_position(argc, argv));
|
|
1701
|
+
|
|
1702
|
+
return dc_inspected_frame_class(debug_context, frame);
|
|
1703
|
+
}
|
|
1704
|
+
|
|
1705
|
+
/*
|
|
1706
|
+
* call-seq:
|
|
1707
|
+
* context.stack_size-> int
|
|
1708
|
+
*
|
|
1709
|
+
* Returns the size of the context stack.
|
|
1710
|
+
*/
|
|
1711
|
+
static VALUE
|
|
1712
|
+
context_stack_size(VALUE self)
|
|
1713
|
+
{
|
|
1714
|
+
debug_context_t *debug_context;
|
|
1715
|
+
|
|
1716
|
+
debug_check_started();
|
|
1717
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1718
|
+
|
|
1719
|
+
return INT2FIX(dc_stack_size(debug_context));
|
|
1720
|
+
}
|
|
1721
|
+
|
|
1722
|
+
/*
|
|
1723
|
+
* call-seq:
|
|
1724
|
+
* context.thread -> thread
|
|
1725
|
+
*
|
|
1726
|
+
* Returns a thread this context is associated with.
|
|
1727
|
+
*/
|
|
1728
|
+
static VALUE
|
|
1729
|
+
context_thread(VALUE self)
|
|
1730
|
+
{
|
|
1731
|
+
debug_context_t *debug_context;
|
|
1732
|
+
|
|
1733
|
+
debug_check_started();
|
|
1734
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1735
|
+
|
|
1736
|
+
return(id2ref(debug_context->thread_id));
|
|
1737
|
+
}
|
|
1738
|
+
|
|
1739
|
+
/*
|
|
1740
|
+
* call-seq:
|
|
1741
|
+
* context.thnum -> int
|
|
1742
|
+
*
|
|
1743
|
+
* Returns the context's number.
|
|
1744
|
+
*/
|
|
1745
|
+
static VALUE
|
|
1746
|
+
context_thnum(VALUE self)
|
|
1747
|
+
{
|
|
1748
|
+
debug_context_t *debug_context;
|
|
1749
|
+
|
|
1750
|
+
debug_check_started();
|
|
1751
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1752
|
+
|
|
1753
|
+
return INT2FIX(debug_context->thnum);
|
|
1754
|
+
}
|
|
1755
|
+
|
|
1756
|
+
static void
|
|
1757
|
+
context_suspend_0(debug_context_t *debug_context)
|
|
1758
|
+
{
|
|
1759
|
+
VALUE status;
|
|
1760
|
+
|
|
1761
|
+
status = rb_funcall(context_thread_0(debug_context), rb_intern("status"), 0);
|
|
1762
|
+
if(rb_str_cmp(status, rb_str_new2("run")) == 0) {
|
|
1763
|
+
CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
|
|
1764
|
+
}
|
|
1765
|
+
else if(rb_str_cmp(status, rb_str_new2("sleep")) == 0) {
|
|
1766
|
+
CTX_FL_UNSET(debug_context, CTX_FL_WAS_RUNNING);
|
|
1767
|
+
}
|
|
1768
|
+
else {
|
|
1769
|
+
return;
|
|
1770
|
+
}
|
|
1771
|
+
|
|
1772
|
+
CTX_FL_SET(debug_context, CTX_FL_SUSPEND);
|
|
1773
|
+
}
|
|
1774
|
+
|
|
1775
|
+
static void
|
|
1776
|
+
context_resume_0(debug_context_t *debug_context)
|
|
1777
|
+
{
|
|
1778
|
+
if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND)) {
|
|
1779
|
+
return;
|
|
1780
|
+
}
|
|
1781
|
+
|
|
1782
|
+
CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
|
|
1783
|
+
|
|
1784
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_WAS_RUNNING)) {
|
|
1785
|
+
rb_thread_wakeup(context_thread_0(debug_context));
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
|
|
1789
|
+
/*
|
|
1790
|
+
* call-seq:
|
|
1791
|
+
* context.suspend -> nil
|
|
1792
|
+
*
|
|
1793
|
+
* Suspends the thread when it is running.
|
|
1794
|
+
*/
|
|
1795
|
+
static VALUE
|
|
1796
|
+
context_suspend(VALUE self)
|
|
1797
|
+
{
|
|
1798
|
+
debug_context_t *debug_context;
|
|
1799
|
+
|
|
1800
|
+
debug_check_started();
|
|
1801
|
+
|
|
1802
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1803
|
+
|
|
1804
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND)) {
|
|
1805
|
+
rb_raise(rb_eRuntimeError, "Already suspended.");
|
|
1806
|
+
}
|
|
1807
|
+
context_suspend_0(debug_context);
|
|
1808
|
+
return Qnil;
|
|
1809
|
+
}
|
|
1810
|
+
|
|
1811
|
+
/*
|
|
1812
|
+
* call-seq:
|
|
1813
|
+
* context.suspended? -> bool
|
|
1814
|
+
*
|
|
1815
|
+
* Returns +true+ if the thread is suspended by debugger.
|
|
1816
|
+
*/
|
|
1817
|
+
static VALUE
|
|
1818
|
+
context_is_suspended(VALUE self)
|
|
1819
|
+
{
|
|
1820
|
+
debug_context_t *debug_context;
|
|
1821
|
+
|
|
1822
|
+
debug_check_started();
|
|
1823
|
+
|
|
1824
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1825
|
+
return CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) ? Qtrue : Qfalse;
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
/*
|
|
1829
|
+
* call-seq:
|
|
1830
|
+
* context.resume -> nil
|
|
1831
|
+
*
|
|
1832
|
+
* Resumes the thread from the suspended mode.
|
|
1833
|
+
*/
|
|
1834
|
+
static VALUE
|
|
1835
|
+
context_resume(VALUE self)
|
|
1836
|
+
{
|
|
1837
|
+
debug_context_t *debug_context;
|
|
1838
|
+
|
|
1839
|
+
debug_check_started();
|
|
1840
|
+
|
|
1841
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1842
|
+
if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND)) {
|
|
1843
|
+
rb_raise(rb_eRuntimeError, "Thread is not suspended.");
|
|
1844
|
+
}
|
|
1845
|
+
context_resume_0(debug_context);
|
|
1846
|
+
return Qnil;
|
|
1847
|
+
}
|
|
1848
|
+
|
|
1849
|
+
/*
|
|
1850
|
+
* call-seq:
|
|
1851
|
+
* context.tracing -> bool
|
|
1852
|
+
*
|
|
1853
|
+
* Returns the tracing flag for the current context.
|
|
1854
|
+
*/
|
|
1855
|
+
static VALUE
|
|
1856
|
+
context_tracing(VALUE self)
|
|
1857
|
+
{
|
|
1858
|
+
debug_context_t *debug_context;
|
|
1859
|
+
|
|
1860
|
+
debug_check_started();
|
|
1861
|
+
|
|
1862
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1863
|
+
return CTX_FL_TEST(debug_context, CTX_FL_TRACING) ? Qtrue : Qfalse;
|
|
1864
|
+
}
|
|
1865
|
+
|
|
1866
|
+
/*
|
|
1867
|
+
* call-seq:
|
|
1868
|
+
* context.tracing = bool
|
|
1869
|
+
*
|
|
1870
|
+
* Controls the tracing for this context.
|
|
1871
|
+
*/
|
|
1872
|
+
static VALUE
|
|
1873
|
+
context_set_tracing(VALUE self, VALUE value)
|
|
1874
|
+
{
|
|
1875
|
+
debug_context_t *debug_context;
|
|
1876
|
+
|
|
1877
|
+
debug_check_started();
|
|
1878
|
+
|
|
1879
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1880
|
+
if (RTEST(value)) {
|
|
1881
|
+
CTX_FL_SET(debug_context, CTX_FL_TRACING);
|
|
1882
|
+
}
|
|
1883
|
+
else {
|
|
1884
|
+
CTX_FL_UNSET(debug_context, CTX_FL_TRACING);
|
|
1885
|
+
}
|
|
1886
|
+
return value;
|
|
1887
|
+
}
|
|
1888
|
+
|
|
1889
|
+
/*
|
|
1890
|
+
* call-seq:
|
|
1891
|
+
* context.ignored? -> bool
|
|
1892
|
+
*
|
|
1893
|
+
* Returns the ignore flag for the current context.
|
|
1894
|
+
*/
|
|
1895
|
+
static VALUE
|
|
1896
|
+
context_ignored(VALUE self)
|
|
1897
|
+
{
|
|
1898
|
+
debug_context_t *debug_context;
|
|
1899
|
+
|
|
1900
|
+
debug_check_started();
|
|
1901
|
+
|
|
1902
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1903
|
+
return CTX_FL_TEST(debug_context, CTX_FL_IGNORE) ? Qtrue : Qfalse;
|
|
1904
|
+
}
|
|
1905
|
+
|
|
1906
|
+
/*
|
|
1907
|
+
* call-seq:
|
|
1908
|
+
* context.dead? -> bool
|
|
1909
|
+
*
|
|
1910
|
+
* Returns +true+ if context doesn't represent a live context and is created
|
|
1911
|
+
* during post-mortem exception handling.
|
|
1912
|
+
*/
|
|
1913
|
+
static VALUE
|
|
1914
|
+
context_dead(VALUE self)
|
|
1915
|
+
{
|
|
1916
|
+
debug_context_t *debug_context;
|
|
1917
|
+
|
|
1918
|
+
debug_check_started();
|
|
1919
|
+
|
|
1920
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1921
|
+
return CTX_FL_TEST(debug_context, CTX_FL_DEAD) ? Qtrue : Qfalse;
|
|
1922
|
+
}
|
|
1923
|
+
|
|
1924
|
+
/*
|
|
1925
|
+
* call-seq:
|
|
1926
|
+
* context.stop_reason -> sym
|
|
1927
|
+
*
|
|
1928
|
+
* Returns the reason for the stop. It maybe of the following values:
|
|
1929
|
+
* :initial, :step, :breakpoint, :catchpoint, :post-mortem
|
|
1930
|
+
*/
|
|
1931
|
+
static VALUE
|
|
1932
|
+
context_stop_reason(VALUE self)
|
|
1933
|
+
{
|
|
1934
|
+
debug_context_t *debug_context;
|
|
1935
|
+
const char * sym_name;
|
|
1936
|
+
|
|
1937
|
+
debug_check_started();
|
|
1938
|
+
|
|
1939
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1940
|
+
|
|
1941
|
+
switch(debug_context->stop_reason) {
|
|
1942
|
+
case CTX_STOP_STEP:
|
|
1943
|
+
sym_name = "step";
|
|
1944
|
+
break;
|
|
1945
|
+
case CTX_STOP_BREAKPOINT:
|
|
1946
|
+
sym_name = "breakpoint";
|
|
1947
|
+
break;
|
|
1948
|
+
case CTX_STOP_CATCHPOINT:
|
|
1949
|
+
sym_name = "catchpoint";
|
|
1950
|
+
break;
|
|
1951
|
+
case CTX_STOP_NONE:
|
|
1952
|
+
default:
|
|
1953
|
+
sym_name = "none";
|
|
1954
|
+
}
|
|
1955
|
+
|
|
1956
|
+
if(CTX_FL_TEST(debug_context, CTX_FL_DEAD)) {
|
|
1957
|
+
sym_name = "post-mortem";
|
|
1958
|
+
}
|
|
1959
|
+
|
|
1960
|
+
return ID2SYM(rb_intern(sym_name));
|
|
1961
|
+
}
|
|
1962
|
+
|
|
1963
|
+
/*
|
|
1964
|
+
* call-seq:
|
|
1965
|
+
* context.jump(line, file) -> bool
|
|
1966
|
+
*
|
|
1967
|
+
* Returns +true+ if jump to +line+ in filename +file+ was successful.
|
|
1968
|
+
*/
|
|
1969
|
+
static VALUE
|
|
1970
|
+
context_jump(VALUE self, VALUE line, VALUE file)
|
|
1971
|
+
{
|
|
1972
|
+
return INT2FIX(1);
|
|
1973
|
+
|
|
1974
|
+
#if 0
|
|
1975
|
+
debug_context_t *debug_context;
|
|
1976
|
+
debug_frame_t *debug_frame;
|
|
1977
|
+
int i;
|
|
1978
|
+
rb_thread_t *th;
|
|
1979
|
+
rb_control_frame_t *cfp;
|
|
1980
|
+
rb_control_frame_t *cfp_end;
|
|
1981
|
+
rb_control_frame_t *cfp_start = NULL;
|
|
1982
|
+
|
|
1983
|
+
debug_check_started();
|
|
1984
|
+
|
|
1985
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
1986
|
+
GetThreadPtr(context_thread_0(debug_context), th);
|
|
1987
|
+
debug_frame = get_top_frame(debug_context);
|
|
1988
|
+
if (debug_frame == NULL)
|
|
1989
|
+
rb_raise(rb_eRuntimeError, "No frames collected.");
|
|
1990
|
+
|
|
1991
|
+
line = FIX2INT(line);
|
|
1992
|
+
|
|
1993
|
+
/* find topmost frame of the debugged code */
|
|
1994
|
+
cfp = th->cfp;
|
|
1995
|
+
cfp_end = RUBY_VM_END_CONTROL_FRAME(th);
|
|
1996
|
+
while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, cfp_end))
|
|
1997
|
+
{
|
|
1998
|
+
if (cfp->pc == debug_frame->info.runtime.last_pc)
|
|
1999
|
+
{
|
|
2000
|
+
cfp_start = cfp;
|
|
2001
|
+
if ((cfp->pc - cfp->iseq->iseq_encoded) >= (cfp->iseq->iseq_size - 1))
|
|
2002
|
+
return(INT2FIX(1)); /* no space for opt_call_c_function hijack */
|
|
2003
|
+
break;
|
|
2004
|
+
}
|
|
2005
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
|
2006
|
+
}
|
|
2007
|
+
if (cfp_start == NULL)
|
|
2008
|
+
return(INT2FIX(2)); /* couldn't find frame; should never happen */
|
|
2009
|
+
|
|
2010
|
+
/* find target frame to jump to */
|
|
2011
|
+
while (RUBY_VM_VALID_CONTROL_FRAME_P(cfp, cfp_end))
|
|
2012
|
+
{
|
|
2013
|
+
if ((cfp->iseq != NULL) && (rb_str_cmp(file, cfp->iseq->filename) == 0))
|
|
2014
|
+
{
|
|
2015
|
+
for (i = 0; i < cfp->iseq->insn_info_size; i++)
|
|
2016
|
+
{
|
|
2017
|
+
if (cfp->iseq->insn_info_table[i].line_no != line)
|
|
2018
|
+
continue;
|
|
2019
|
+
|
|
2020
|
+
/* hijack the currently running code so that we can change the frame PC */
|
|
2021
|
+
debug_context->saved_jump_ins[0] = cfp_start->pc[0];
|
|
2022
|
+
debug_context->saved_jump_ins[1] = cfp_start->pc[1];
|
|
2023
|
+
cfp_start->pc[0] = opt_call_c_function;
|
|
2024
|
+
cfp_start->pc[1] = (VALUE)do_jump;
|
|
2025
|
+
|
|
2026
|
+
debug_context->jump_cfp = cfp;
|
|
2027
|
+
debug_context->jump_pc =
|
|
2028
|
+
cfp->iseq->iseq_encoded + cfp->iseq->insn_info_table[i].position;
|
|
2029
|
+
|
|
2030
|
+
return(INT2FIX(0)); /* success */
|
|
2031
|
+
}
|
|
2032
|
+
}
|
|
2033
|
+
|
|
2034
|
+
cfp = RUBY_VM_PREVIOUS_CONTROL_FRAME(cfp);
|
|
2035
|
+
}
|
|
2036
|
+
|
|
2037
|
+
return(INT2FIX(3)); /* couldn't find a line and file frame match */
|
|
2038
|
+
#endif
|
|
2039
|
+
}
|
|
2040
|
+
|
|
2041
|
+
/*
|
|
2042
|
+
* call-seq:
|
|
2043
|
+
* context.break -> bool
|
|
2044
|
+
*
|
|
2045
|
+
* Returns +true+ if context is currently running and set flag to break it at next line
|
|
2046
|
+
*/
|
|
2047
|
+
static VALUE
|
|
2048
|
+
context_pause(VALUE self)
|
|
2049
|
+
{
|
|
2050
|
+
debug_context_t *debug_context;
|
|
2051
|
+
|
|
2052
|
+
debug_check_started();
|
|
2053
|
+
|
|
2054
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
|
2055
|
+
|
|
2056
|
+
if (CTX_FL_TEST(debug_context, CTX_FL_DEAD)) {
|
|
2057
|
+
return(Qfalse);
|
|
2058
|
+
}
|
|
2059
|
+
|
|
2060
|
+
if (debug_context->thread_id == rb_thread_current()) {
|
|
2061
|
+
return(Qfalse);
|
|
2062
|
+
}
|
|
2063
|
+
|
|
2064
|
+
debug_context->thread_pause = 1;
|
|
2065
|
+
return(Qtrue);
|
|
2066
|
+
}
|
|
2067
|
+
|
|
2068
|
+
/*
|
|
2069
|
+
* Document-class: Context
|
|
2070
|
+
*
|
|
2071
|
+
* == Summary
|
|
2072
|
+
*
|
|
2073
|
+
* Debugger keeps a single instance of this class for each Ruby thread.
|
|
2074
|
+
*/
|
|
2075
|
+
static void
|
|
2076
|
+
Init_context(void)
|
|
2077
|
+
{
|
|
2078
|
+
cContext = rb_define_class_under(mDebugger, "Context", rb_cObject);
|
|
2079
|
+
rb_define_method(cContext, "stop_next=", context_stop_next, -1);
|
|
2080
|
+
rb_define_method(cContext, "step", context_stop_next, -1);
|
|
2081
|
+
rb_define_method(cContext, "step_over", context_step_over, -1);
|
|
2082
|
+
rb_define_method(cContext, "stop_frame=", context_stop_frame, 1);
|
|
2083
|
+
rb_define_method(cContext, "thread", context_thread, 0);
|
|
2084
|
+
rb_define_method(cContext, "thnum", context_thnum, 0);
|
|
2085
|
+
rb_define_method(cContext, "stop_reason", context_stop_reason, 0);
|
|
2086
|
+
rb_define_method(cContext, "suspend", context_suspend, 0);
|
|
2087
|
+
rb_define_method(cContext, "suspended?", context_is_suspended, 0);
|
|
2088
|
+
rb_define_method(cContext, "resume", context_resume, 0);
|
|
2089
|
+
rb_define_method(cContext, "tracing", context_tracing, 0);
|
|
2090
|
+
rb_define_method(cContext, "tracing=", context_set_tracing, 1);
|
|
2091
|
+
rb_define_method(cContext, "ignored?", context_ignored, 0);
|
|
2092
|
+
rb_define_method(cContext, "frame_args", context_frame_args, -1);
|
|
2093
|
+
rb_define_method(cContext, "frame_args_info", context_frame_args_info, -1);
|
|
2094
|
+
rb_define_method(cContext, "frame_binding", context_frame_binding, -1);
|
|
2095
|
+
rb_define_method(cContext, "frame_class", context_frame_class, -1);
|
|
2096
|
+
rb_define_method(cContext, "frame_file", context_frame_file, -1);
|
|
2097
|
+
rb_define_method(cContext, "frame_id", context_frame_id, -1);
|
|
2098
|
+
rb_define_method(cContext, "frame_line", context_frame_line, -1);
|
|
2099
|
+
rb_define_method(cContext, "frame_locals", context_frame_locals, -1);
|
|
2100
|
+
rb_define_method(cContext, "frame_method", context_frame_id, -1);
|
|
2101
|
+
rb_define_method(cContext, "frame_self", context_frame_self, -1);
|
|
2102
|
+
rb_define_method(cContext, "stack_size", context_stack_size, 0);
|
|
2103
|
+
rb_define_method(cContext, "dead?", context_dead, 0);
|
|
2104
|
+
rb_define_method(cContext, "breakpoint", context_breakpoint, 0); /* in breakpoint.c */
|
|
2105
|
+
rb_define_method(cContext, "set_breakpoint", context_set_breakpoint, -1); /* in breakpoint.c */
|
|
2106
|
+
rb_define_method(cContext, "jump", context_jump, 2);
|
|
2107
|
+
rb_define_method(cContext, "pause", context_pause, 0);
|
|
2108
|
+
}
|
|
2109
|
+
|
|
2110
|
+
/*
|
|
2111
|
+
* call-seq:
|
|
2112
|
+
* Debugger.breakpoints -> array
|
|
2113
|
+
*
|
|
2114
|
+
* Returns an array of breakpoints.
|
|
2115
|
+
*/
|
|
2116
|
+
static VALUE
|
|
2117
|
+
debug_breakpoints(VALUE self)
|
|
2118
|
+
{
|
|
2119
|
+
debug_check_started();
|
|
2120
|
+
|
|
2121
|
+
return rdebug_breakpoints;
|
|
2122
|
+
}
|
|
2123
|
+
|
|
2124
|
+
/*
|
|
2125
|
+
* call-seq:
|
|
2126
|
+
* Debugger.add_breakpoint(source, pos, condition = nil) -> breakpoint
|
|
2127
|
+
*
|
|
2128
|
+
* Adds a new breakpoint.
|
|
2129
|
+
* <i>source</i> is a name of a file or a class.
|
|
2130
|
+
* <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
|
|
2131
|
+
* <i>condition</i> is a string which is evaluated to +true+ when this breakpoint
|
|
2132
|
+
* is activated.
|
|
2133
|
+
*/
|
|
2134
|
+
static VALUE
|
|
2135
|
+
debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
|
|
2136
|
+
{
|
|
2137
|
+
VALUE result;
|
|
2138
|
+
|
|
2139
|
+
debug_check_started();
|
|
2140
|
+
|
|
2141
|
+
result = create_breakpoint_from_args(argc, argv, ++bkp_count);
|
|
2142
|
+
rb_ary_push(rdebug_breakpoints, result);
|
|
2143
|
+
return result;
|
|
2144
|
+
}
|
|
2145
|
+
|
|
2146
|
+
/*
|
|
2147
|
+
* Document-class: Debugger
|
|
2148
|
+
*
|
|
2149
|
+
* == Summary
|
|
2150
|
+
*
|
|
2151
|
+
* This is a singleton class allows controlling the debugger. Use it to start/stop debugger,
|
|
2152
|
+
* set/remove breakpoints, etc.
|
|
2153
|
+
*/
|
|
2154
|
+
void
|
|
2155
|
+
Init_ruby_debug(void)
|
|
2156
|
+
{
|
|
2157
|
+
mDebugger = rb_define_module("Debugger");
|
|
2158
|
+
rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION));
|
|
2159
|
+
rb_define_module_function(mDebugger, "start_", debug_start, 0);
|
|
2160
|
+
rb_define_module_function(mDebugger, "stop", debug_stop, 0);
|
|
2161
|
+
rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
|
|
2162
|
+
rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
|
|
2163
|
+
rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1);
|
|
2164
|
+
rb_define_module_function(mDebugger, "remove_breakpoint", rdebug_remove_breakpoint, 1); /* in breakpoint.c */
|
|
2165
|
+
rb_define_module_function(mDebugger, "add_catchpoint", rdebug_add_catchpoint, 1); /* in breakpoint.c */
|
|
2166
|
+
rb_define_module_function(mDebugger, "catchpoints", debug_catchpoints, 0); /* in breakpoint.c */
|
|
2167
|
+
rb_define_module_function(mDebugger, "last_context", debug_last_interrupted, 0);
|
|
2168
|
+
rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
|
|
2169
|
+
rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
|
|
2170
|
+
rb_define_module_function(mDebugger, "thread_context", debug_thread_context, 1);
|
|
2171
|
+
rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
|
|
2172
|
+
rb_define_module_function(mDebugger, "resume", debug_resume, 0);
|
|
2173
|
+
rb_define_module_function(mDebugger, "tracing", debug_tracing, 0);
|
|
2174
|
+
rb_define_module_function(mDebugger, "tracing=", debug_set_tracing, 1);
|
|
2175
|
+
rb_define_module_function(mDebugger, "debug_load", debug_debug_load, -1);
|
|
2176
|
+
rb_define_module_function(mDebugger, "skip", debug_skip, 0);
|
|
2177
|
+
rb_define_module_function(mDebugger, "debug_at_exit", debug_at_exit, 0);
|
|
2178
|
+
rb_define_module_function(mDebugger, "post_mortem?", debug_post_mortem, 0);
|
|
2179
|
+
rb_define_module_function(mDebugger, "post_mortem=", debug_set_post_mortem, 1);
|
|
2180
|
+
rb_define_module_function(mDebugger, "keep_frame_binding?", debug_keep_frame_binding, 0);
|
|
2181
|
+
rb_define_module_function(mDebugger, "keep_frame_binding=", debug_set_keep_frame_binding, 1);
|
|
2182
|
+
rb_define_module_function(mDebugger, "track_frame_args?", debug_track_frame_args, 0);
|
|
2183
|
+
rb_define_module_function(mDebugger, "track_frame_args=", debug_set_track_frame_args, 1);
|
|
2184
|
+
rb_define_module_function(mDebugger, "debug", debug_debug, 0);
|
|
2185
|
+
rb_define_module_function(mDebugger, "debug=", debug_set_debug, 1);
|
|
2186
|
+
|
|
2187
|
+
cThreadsTable = rb_define_class_under(mDebugger, "ThreadsTable", rb_cObject);
|
|
2188
|
+
|
|
2189
|
+
cDebugThread = rb_define_class_under(mDebugger, "DebugThread", rb_cThread);
|
|
2190
|
+
rb_define_singleton_method(cDebugThread, "inherited",
|
|
2191
|
+
debug_thread_inherited, 1);
|
|
2192
|
+
|
|
2193
|
+
Init_context();
|
|
2194
|
+
Init_breakpoint();
|
|
2195
|
+
|
|
2196
|
+
rb_global_variable(&last_context);
|
|
2197
|
+
rb_global_variable(&last_thread);
|
|
2198
|
+
rb_global_variable(&locker);
|
|
2199
|
+
rb_global_variable(&rdebug_breakpoints);
|
|
2200
|
+
rb_global_variable(&rdebug_catchpoints);
|
|
2201
|
+
rb_global_variable(&rdebug_threads_tbl);
|
|
2202
|
+
rb_global_variable(&tracepoints);
|
|
2203
|
+
}
|