ruby-debug-base 0.8

Sign up to get free protection for your applications and to get access to all the features.
Files changed (9) hide show
  1. data/AUTHORS +6 -0
  2. data/CHANGES +143 -0
  3. data/LICENSE +23 -0
  4. data/README +63 -0
  5. data/ext/extconf.rb +18 -0
  6. data/ext/ruby_debug.c +2175 -0
  7. data/ext/tags +118 -0
  8. data/lib/ruby-debug-base.rb +230 -0
  9. metadata +55 -0
data/AUTHORS ADDED
@@ -0,0 +1,6 @@
1
+ Author and maintainer:
2
+ Kent Sibilev
3
+
4
+ Contributers:
5
+ Markus Barchfeld
6
+ R. Bernstein
data/CHANGES ADDED
@@ -0,0 +1,143 @@
1
+ 0.8
2
+ - Extract the base debugger API into a separate gem (ruby-debug-base), so it will be easier to add a new interface.
3
+ - Bugfixes.
4
+
5
+ 0.7.5
6
+ - Fixed 'reload on' command
7
+ - 'reload on' command is removed in favor of 'set autoreload'
8
+ - rdebug will evaluate ~/.rdebugrc script on startup
9
+
10
+ 0.7.4
11
+ - Added a workaround of the Ruby interpreter problem where a method created with Module#define_method
12
+ and which raises an exception doesn't trigger a :return event, this way screwing the stack trace.
13
+ - Fixed a situation of an array 'out of bounds' access.
14
+ - Fixed the help for 'where' command.
15
+
16
+ 0.7.3
17
+ - Fixed a case when a frame is not popped up properly.
18
+ - Removed Context.ignore= method, since it can result with the segmentation fault error.
19
+ - Fixed the case when Context#suspend may effect the state of the thread on Context#resume
20
+ - Fixed several cases of seg faults when accessing dyna_vars structure.
21
+
22
+ 0.7.2
23
+ - Fixed Context#resume (a thread should be waked up only when it was running when it was suspended).
24
+ - When handling post-mortem exception, all threads must be suspended.
25
+
26
+ 0.7.1
27
+ - Fixed 'delete' command
28
+
29
+ 0.7
30
+ - Eliminated explicit Frame object. Use Context.frame_[binding,file,line] instead.
31
+ - Fixed help command.
32
+ - Renamed Debugger.keep_frame_info to Debugger.keep_frame_binding
33
+ - 'eval' command is available, even when keep_frame_binding is not used.
34
+ - New 'set' command is available.
35
+
36
+ 0.6.2
37
+ - Added thread lookup cache.
38
+ - Control thread is always started by rdebug script.
39
+ - Ability to specify negative frame number to frame commands. Patch from R. Bernstein.
40
+
41
+ 0.6.1
42
+ - Another performance optimization.
43
+
44
+ 0.6
45
+ - Added option to exclude collecting of frame bindings.
46
+ - Several performance optimizations.
47
+
48
+ 0.5.4
49
+ - Added -x/--trace option to rdebug script. Patch from R. Bernstein.
50
+ - Removed a live thread reference from the context's structure avoiding memory leakage.
51
+
52
+ 0.5.3
53
+ - Added Module#post_mortem_method method, which wraps any method with Debugger.post_mortem block.
54
+ - Added breakpoint id, which is not dependent on the breakpoint position in Debugger.breakpoints array.
55
+
56
+ 0.5.2
57
+ - Fixes interoperability problems with rspec.
58
+ - Made 'exit' as an alias to 'quit'
59
+ - Added 'restart' command. Patch from R. Bernstein.
60
+
61
+ 0.5.1
62
+ - Bugfixes.
63
+
64
+ 0.5
65
+ - Added post-mortem debugging
66
+ - Added 'irb' command.
67
+
68
+ 0.4.5
69
+ - Fixed debug_method when applied to setter.
70
+ - Added 'reload' command which can be used to reload source code in case it's been changed.
71
+ - Added Debugger.reload_source_on_change option (true, by default) which controls whether ruby-debug should keep
72
+ track of the source files modification times and reload them if they've been changed.
73
+
74
+ 0.4.4
75
+ - Renamed Context#set_suspend and Context#clear_suspend methods to Context#suspend and Context#resume respectively.
76
+ - Context#resume method not only clears suspend flag, but also resumes the thread execution.
77
+ - Bugfixes.
78
+
79
+ 0.4.3
80
+ - Added Debugger.skip method which allows escaping a block from the debugger reach.
81
+ - Bugfixes.
82
+
83
+ 0.4.2
84
+ - Module#deubg_method added.
85
+ - Added rdoc.
86
+ - Bugfixes.
87
+
88
+ 0.4.1
89
+ - New binding_n method for Kernel module.
90
+ - Bugfixes.
91
+
92
+ 0.4
93
+ - Debugger.start method takes a block. If a block is specified, this method starts debugger, yields to the block
94
+ and stops debugger at the end.
95
+ - 'tm[ate]' command accepts a frame number now.
96
+ - 'list' command accepts on/off parameter which controls whether listing will be displayed on every stop.
97
+ - 'eval on/off' controls the evaluation of unknown command.
98
+ - Debugger reads readline history file .rdebug_hist at startup and saves it at exit.
99
+ - 'sa[ve] <file>' command can be used to save current breackpoints and catchpoint if any
100
+ - 'sc[ript] <file>' command can be used to run script file. Script files can contain only control commands.
101
+ - rdebug script accepts '--script FILE' parameter.
102
+ - thread commands are available for the control port.
103
+
104
+ 0.3 (2006-08-07)
105
+ - Renamed Debugger.start_server to Debugger.start_remote.
106
+ - Debugger.start_remote activates debugger by calling Debugger.start.
107
+ - Debugger.start_remote starts a control thread which listen on port 8990 and accepts control
108
+ commands, such as adding/deleting breakpoints, assigning catchpoint, etc. (Useful for GUI integration)
109
+ - New Debugger.wait_connection option. When it's true, Debugger.start_remote waits until
110
+ a remote connection is made.
111
+ - New Debugger.stop_on_connect option. When a remote connection is established, debugger
112
+ stops the main thread (Thread.main).
113
+ - 'interrupt' command is available for the control thread.
114
+
115
+ 0.2.1 (2006-07-29)
116
+ - 'f[rame] nn' command selects a numbered frame. Frame numbers can be obtained by running frame
117
+ command without parameters.
118
+ - 'l[ist] =' show code in the context of the current line.
119
+ - 'tm[ate]' opens the current file in TextMate. Available only on Mac OSX.
120
+
121
+ 0.2 (2006-07-17)
122
+ - Added the remote debugging. It should be activated by calling Debugger#start_server method.
123
+ - CHANGED: In order to activate the debugger, it's not enough to require 'ruby-debug'.
124
+ Debugger#start method must be called explicitly.
125
+ - Debugger used to evaluate anything you enter as long as it's not a command. Starting from
126
+ this version the 'eval' command must be used to evaluate an expression.
127
+
128
+ 0.1.5 (2006-07-13)
129
+ - Now the check for a breakpoint uses base filename of the source file.
130
+ - Removed compilation warnings when compiling with -Wall
131
+
132
+ 0.1.4 (2006-07-12)
133
+ - Remembers the previous command. Invoke it by typing a carriage return
134
+ at the command prompt.
135
+
136
+ 0.1.3 (2006-07-11)
137
+ - Conditional breakpoints
138
+ - Bugfixes
139
+
140
+ 0.1.2 (2006-07-16)
141
+ ========================
142
+
143
+ - Initial release.
data/LICENSE ADDED
@@ -0,0 +1,23 @@
1
+ Copyright (C) 2005 Kent Sibilev <ksibilev@yahoo.com>
2
+ All rights reserved.
3
+ *
4
+ Redistribution and use in source and binary forms, with or without
5
+ modification, are permitted provided that the following conditions
6
+ are met:
7
+ 1. Redistributions of source code must retain the above copyright
8
+ notice, this list of conditions and the following disclaimer.
9
+ 2. Redistributions in binary form must reproduce the above copyright
10
+ notice, this list of conditions and the following disclaimer in the
11
+ documentation and/or other materials provided with the distribution.
12
+ *
13
+ THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
14
+ ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
+ IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
+ ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
17
+ FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
+ DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
+ OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
+ HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
+ LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
+ OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
+ SUCH DAMAGE.
data/README ADDED
@@ -0,0 +1,63 @@
1
+ = ruby-debug
2
+
3
+ == Overview
4
+
5
+ ruby-debug is a fast implementation of the standard debugger debug.rb.
6
+ The faster execution speed is achieved by utilizing a new hook Ruby C API.
7
+
8
+ == Requirements
9
+
10
+ ruby-debug requires Ruby 1.8.4 or higher.
11
+
12
+ If you are running Linux or Unix you'll need a C compiler so the extension
13
+ can be compiled when it is installed.
14
+
15
+
16
+ == Install
17
+
18
+ ruby-debug is provided as a RubyGem. To install:
19
+
20
+ <tt>gem install ruby-debug</tt>
21
+
22
+ == Usage
23
+
24
+ There are two ways of running ruby-debug.
25
+
26
+ === rdebug executable:
27
+
28
+ $ rdebug <your-script>
29
+
30
+ When you start your script this way, the debugger will stop at
31
+ the first line of code in the script file. So you will be able
32
+ to set up your breakpoints.
33
+
34
+ === ruby-debug API
35
+
36
+ The second way is to use the ruby-debug API to interrupt your
37
+ code execution at runtime.
38
+
39
+ require 'ruby-debug'
40
+ ...
41
+ def your_method
42
+ ...
43
+ debugger
44
+ ...
45
+ end
46
+
47
+ When Kernel#debugger method is executed, the debugger is activated
48
+ and you will be able to inspect and step through your code.
49
+
50
+ == Performance
51
+
52
+ The debug.rb script that comes with the standard library uses
53
+ Kernel#set_trace_func API. This way it is possible to implement
54
+ the debugger in pure Ruby, but has a negative effect on the speed
55
+ of your program execution. For each trace call Ruby interpreter
56
+ creates a Binding object, even though it is not being used most
57
+ of the time. ruby-debug library moves most of the functionality
58
+ of debug.rb to a native extension, this way significantly improving
59
+ the execution of your program.
60
+
61
+ == License
62
+
63
+ See LICENSE for license information.
@@ -0,0 +1,18 @@
1
+ require "mkmf"
2
+
3
+ if RUBY_VERSION >= "1.9"
4
+ if RUBY_RELEASE_DATE < "2005-03-17"
5
+ STDERR.print("Ruby version is too old\n")
6
+ exit(1)
7
+ end
8
+ elsif RUBY_VERSION >= "1.8"
9
+ if RUBY_RELEASE_DATE < "2005-03-22"
10
+ STDERR.print("Ruby version is too old\n")
11
+ exit(1)
12
+ end
13
+ else
14
+ STDERR.print("Ruby version is too old\n")
15
+ exit(1)
16
+ end
17
+
18
+ create_makefile("ruby_debug")
@@ -0,0 +1,2175 @@
1
+ #include <stdio.h>
2
+ #include <ruby.h>
3
+ #include <node.h>
4
+ #include <rubysig.h>
5
+ #include <st.h>
6
+
7
+ #define DEBUG_VERSION "0.8"
8
+
9
+ #ifdef _WIN32
10
+ struct FRAME {
11
+ VALUE self;
12
+ int argc;
13
+ ID last_func;
14
+ ID orig_func;
15
+ VALUE last_class;
16
+ struct FRAME *prev;
17
+ struct FRAME *tmp;
18
+ struct RNode *node;
19
+ int iter;
20
+ int flags;
21
+ unsigned long uniq;
22
+ };
23
+
24
+ struct SCOPE {
25
+ struct RBasic super;
26
+ ID *local_tbl;
27
+ VALUE *local_vars;
28
+ int flags;
29
+ };
30
+
31
+ struct RVarmap {
32
+ struct RBasic super;
33
+ ID id;
34
+ VALUE val;
35
+ struct RVarmap *next;
36
+ };
37
+
38
+ RUBY_EXTERN struct SCOPE *ruby_scope;
39
+ RUBY_EXTERN struct FRAME *ruby_frame;
40
+ RUBY_EXTERN struct RVarmap *ruby_dyna_vars;
41
+ #else
42
+ #include <env.h>
43
+ #endif
44
+
45
+ #define CTX_FL_MOVED (1<<1)
46
+ #define CTX_FL_SUSPEND (1<<2)
47
+ #define CTX_FL_TRACING (1<<3)
48
+ #define CTX_FL_SKIPPED (1<<4)
49
+ #define CTX_FL_IGNORE (1<<5)
50
+ #define CTX_FL_DEAD (1<<6)
51
+ #define CTX_FL_WAS_RUNNING (1<<7)
52
+
53
+ #define CTX_FL_TEST(c,f) ((c)->flags & (f))
54
+ #define CTX_FL_SET(c,f) do { (c)->flags |= (f); } while (0)
55
+ #define CTX_FL_UNSET(c,f) do { (c)->flags &= ~(f); } while (0)
56
+
57
+ #define DID_MOVED (debug_context->last_line != line || \
58
+ debug_context->last_file == NULL || \
59
+ strcmp(debug_context->last_file, file) != 0)
60
+
61
+ #define IS_STARTED (threads_tbl != Qnil)
62
+ #define FRAME_N(n) (&debug_context->frames[debug_context->stack_size-(n)-1])
63
+ #define GET_FRAME (FRAME_N(check_frame_number(debug_context, frame)))
64
+
65
+ #ifndef min
66
+ #define min(x,y) ((x) < (y) ? (x) : (y))
67
+ #endif
68
+
69
+ #define STACK_SIZE_INCREMENT 128
70
+
71
+ typedef struct {
72
+ VALUE binding;
73
+ ID id;
74
+ ID orig_id;
75
+ int line;
76
+ const char * file;
77
+ short dead;
78
+ VALUE self;
79
+ union {
80
+ struct {
81
+ struct FRAME *frame;
82
+ struct SCOPE *scope;
83
+ struct RVarmap *dyna_vars;
84
+ } runtime;
85
+ struct {
86
+ VALUE locals;
87
+ } copy;
88
+ } info;
89
+ } debug_frame_t;
90
+
91
+ typedef struct {
92
+ VALUE thread_id;
93
+ int thnum;
94
+ int flags;
95
+ int stop_next;
96
+ int dest_frame;
97
+ int stop_line;
98
+ int stop_frame;
99
+ int stack_size;
100
+ int stack_len;
101
+ debug_frame_t *frames;
102
+ const char * last_file;
103
+ int last_line;
104
+ } debug_context_t;
105
+
106
+ enum bp_type {BP_POS_TYPE, BP_METHOD_TYPE};
107
+
108
+ typedef struct {
109
+ int id;
110
+ int type;
111
+ VALUE source;
112
+ union
113
+ {
114
+ int line;
115
+ ID mid;
116
+ } pos;
117
+ VALUE expr;
118
+ } debug_breakpoint_t;
119
+
120
+ typedef struct {
121
+ st_table *tbl;
122
+ } threads_table_t;
123
+
124
+ static VALUE threads_tbl = Qnil;
125
+ static VALUE breakpoints = Qnil;
126
+ static VALUE catchpoint = Qnil;
127
+ static VALUE tracing = Qfalse;
128
+ static VALUE locker = Qnil;
129
+ static VALUE post_mortem = Qfalse;
130
+ static VALUE keep_frame_binding = Qfalse;
131
+ static VALUE debug = Qfalse;
132
+
133
+ static VALUE last_context = Qnil;
134
+ static VALUE last_thread = Qnil;
135
+ static debug_context_t *last_debug_context = NULL;
136
+
137
+ static VALUE mDebugger;
138
+ static VALUE cThreadsTable;
139
+ static VALUE cContext;
140
+ static VALUE cBreakpoint;
141
+ static VALUE cDebugThread;
142
+
143
+ static VALUE rb_mObjectSpace;
144
+
145
+ static ID idAtLine;
146
+ static ID idAtBreakpoint;
147
+ static ID idAtCatchpoint;
148
+ static ID idAtTracing;
149
+ static ID idEval;
150
+ static ID idList;
151
+
152
+ static int start_count = 0;
153
+ static int thnum_max = 0;
154
+ static int bkp_count = 0;
155
+ static int last_debugged_thnum = -1;
156
+ static unsigned long last_check = 0;
157
+ static unsigned long hook_count = 0;
158
+
159
+ static VALUE create_binding(VALUE);
160
+ static VALUE debug_stop(VALUE);
161
+ static void save_current_position(debug_context_t *);
162
+ static VALUE context_copy_locals(debug_frame_t *);
163
+ static void context_suspend_0(debug_context_t *);
164
+ static void context_resume_0(debug_context_t *);
165
+
166
+ typedef struct locked_thread_t {
167
+ VALUE thread_id;
168
+ struct locked_thread_t *next;
169
+ } locked_thread_t;
170
+
171
+ static locked_thread_t *locked_head = NULL;
172
+ static locked_thread_t *locked_tail = NULL;
173
+
174
+ inline static void *
175
+ ruby_method_ptr(VALUE class, ID meth_id)
176
+ {
177
+ NODE *body, *method;
178
+ st_lookup(RCLASS(class)->m_tbl, meth_id, (st_data_t *)&body);
179
+ method = (NODE *)body->u2.value;
180
+ return (void *)method->u1.value;
181
+ }
182
+
183
+ inline static VALUE
184
+ ref2id(VALUE obj)
185
+ {
186
+ return rb_obj_id(obj);
187
+ }
188
+
189
+ static VALUE
190
+ id2ref_unprotected(VALUE id)
191
+ {
192
+ typedef VALUE (*id2ref_func_t)(VALUE, VALUE);
193
+ static id2ref_func_t f_id2ref = NULL;
194
+ if(f_id2ref == NULL)
195
+ {
196
+ f_id2ref = (id2ref_func_t)ruby_method_ptr(rb_mObjectSpace, rb_intern("_id2ref"));
197
+ }
198
+ return f_id2ref(rb_mObjectSpace, id);
199
+ }
200
+
201
+ static VALUE
202
+ id2ref_error()
203
+ {
204
+ rb_p(ruby_errinfo);
205
+ return Qnil;
206
+ }
207
+
208
+ static VALUE
209
+ id2ref(VALUE id)
210
+ {
211
+ return rb_rescue(id2ref_unprotected, id, id2ref_error, 0);
212
+ }
213
+
214
+ inline static VALUE
215
+ context_thread_0(debug_context_t *debug_context)
216
+ {
217
+ return id2ref(debug_context->thread_id);
218
+ }
219
+
220
+ static int
221
+ is_in_locked(VALUE thread_id)
222
+ {
223
+ locked_thread_t *node;
224
+
225
+ if(!locked_head)
226
+ return 0;
227
+
228
+ for(node = locked_head; node != locked_tail; node = node->next)
229
+ {
230
+ if(node->thread_id == thread_id) return 1;
231
+ }
232
+ return 0;
233
+ }
234
+
235
+ static void
236
+ add_to_locked(VALUE thread)
237
+ {
238
+ locked_thread_t *node;
239
+ VALUE thread_id = ref2id(thread);
240
+
241
+ if(is_in_locked(thread_id))
242
+ return;
243
+
244
+ node = ALLOC(locked_thread_t);
245
+ node->thread_id = thread_id;
246
+ node->next = NULL;
247
+ if(locked_tail)
248
+ locked_tail->next = node;
249
+ locked_tail = node;
250
+ if(!locked_head)
251
+ locked_head = node;
252
+ }
253
+
254
+ static VALUE
255
+ remove_from_locked()
256
+ {
257
+ VALUE thread;
258
+ locked_thread_t *node;
259
+
260
+ if(locked_head == NULL)
261
+ return Qnil;
262
+ node = locked_head;
263
+ locked_head = locked_head->next;
264
+ if(locked_tail == node)
265
+ locked_tail = NULL;
266
+ thread = id2ref(node->thread_id);
267
+ xfree(node);
268
+ return thread;
269
+ }
270
+
271
+ static int
272
+ threads_table_mark_keyvalue(VALUE key, VALUE value, int dummy)
273
+ {
274
+ rb_gc_mark(value);
275
+ return ST_CONTINUE;
276
+ }
277
+
278
+ static void
279
+ threads_table_mark(void* data)
280
+ {
281
+ threads_table_t *threads_table = (threads_table_t*)data;
282
+ st_foreach(threads_table->tbl, threads_table_mark_keyvalue, 0);
283
+ }
284
+
285
+ static void
286
+ threads_table_free(void* data)
287
+ {
288
+ threads_table_t *threads_table = (threads_table_t*)data;
289
+ st_free_table(threads_table->tbl);
290
+ xfree(threads_table);
291
+ }
292
+
293
+ static VALUE
294
+ threads_table_create()
295
+ {
296
+ threads_table_t *threads_table;
297
+
298
+ threads_table = ALLOC(threads_table_t);
299
+ threads_table->tbl = st_init_numtable();
300
+ return Data_Wrap_Struct(cThreadsTable, threads_table_mark, threads_table_free, threads_table);
301
+ }
302
+
303
+ static int
304
+ threads_table_clear_i(VALUE key, VALUE value, VALUE dummy)
305
+ {
306
+ return ST_DELETE;
307
+ }
308
+
309
+ static void
310
+ threads_table_clear(VALUE table)
311
+ {
312
+ threads_table_t *threads_table;
313
+
314
+ Data_Get_Struct(table, threads_table_t, threads_table);
315
+ st_foreach(threads_table->tbl, threads_table_clear_i, 0);
316
+ }
317
+
318
+ static VALUE
319
+ is_thread_alive(VALUE thread)
320
+ {
321
+ typedef VALUE (*thread_alive_func_t)(VALUE);
322
+ static thread_alive_func_t f_thread_alive = NULL;
323
+ if(!f_thread_alive)
324
+ {
325
+ f_thread_alive = (thread_alive_func_t)ruby_method_ptr(rb_cThread, rb_intern("alive?"));
326
+ }
327
+ return f_thread_alive(thread);
328
+ }
329
+
330
+ static int
331
+ threads_table_check_i(VALUE key, VALUE value, VALUE dummy)
332
+ {
333
+ VALUE thread;
334
+
335
+ thread = id2ref(key);
336
+ if(!rb_obj_is_kind_of(thread, rb_cThread))
337
+ {
338
+ return ST_DELETE;
339
+ }
340
+ if(rb_protect(is_thread_alive, thread, 0) != Qtrue)
341
+ {
342
+ return ST_DELETE;
343
+ }
344
+ return ST_CONTINUE;
345
+ }
346
+
347
+ static void
348
+ check_thread_contexts()
349
+ {
350
+ threads_table_t *threads_table;
351
+
352
+ Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
353
+ st_foreach(threads_table->tbl, threads_table_check_i, 0);
354
+ }
355
+
356
+ /*
357
+ * call-seq:
358
+ * Debugger.started? -> bool
359
+ *
360
+ * Returns +true+ the debugger is started.
361
+ */
362
+ static VALUE
363
+ debug_is_started(VALUE self)
364
+ {
365
+ return IS_STARTED ? Qtrue : Qfalse;
366
+ }
367
+
368
+ static void
369
+ debug_check_started()
370
+ {
371
+ if(!IS_STARTED)
372
+ {
373
+ rb_raise(rb_eRuntimeError, "Debugger.start is not called yet.");
374
+ }
375
+ }
376
+
377
+ static void
378
+ debug_context_mark(void *data)
379
+ {
380
+ debug_frame_t *frame;
381
+ int i;
382
+
383
+ debug_context_t *debug_context = (debug_context_t *)data;
384
+ for(i = 0; i < debug_context->stack_size; i++)
385
+ {
386
+ frame = &(debug_context->frames[i]);
387
+ rb_gc_mark(frame->binding);
388
+ rb_gc_mark(frame->self);
389
+ if(frame->dead)
390
+ {
391
+ rb_gc_mark(frame->info.copy.locals);
392
+ }
393
+ }
394
+ }
395
+
396
+ static void
397
+ debug_context_free(void *data)
398
+ {
399
+ debug_context_t *debug_context = (debug_context_t *)data;
400
+ xfree(debug_context->frames);
401
+ }
402
+
403
+ static VALUE
404
+ debug_context_create(VALUE thread)
405
+ {
406
+ debug_context_t *debug_context;
407
+
408
+ debug_context = ALLOC(debug_context_t);
409
+ debug_context-> thnum = ++thnum_max;
410
+
411
+ debug_context->last_file = NULL;
412
+ debug_context->last_line = 0;
413
+ debug_context->flags = 0;
414
+
415
+ debug_context->stop_next = -1;
416
+ debug_context->dest_frame = -1;
417
+ debug_context->stop_line = -1;
418
+ debug_context->stop_frame = -1;
419
+ debug_context->stack_len = STACK_SIZE_INCREMENT;
420
+ debug_context->frames = ALLOC_N(debug_frame_t, STACK_SIZE_INCREMENT);
421
+ debug_context->stack_size = 0;
422
+ debug_context->thread_id = ref2id(thread);
423
+ if(rb_obj_class(thread) == cDebugThread)
424
+ CTX_FL_SET(debug_context, CTX_FL_IGNORE);
425
+ return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, debug_context);
426
+ }
427
+
428
+ static VALUE
429
+ debug_context_dup(debug_context_t *debug_context)
430
+ {
431
+ debug_context_t *new_debug_context;
432
+ debug_frame_t *new_frame, *old_frame;
433
+ int i;
434
+
435
+ new_debug_context = ALLOC(debug_context_t);
436
+ memcpy(new_debug_context, debug_context, sizeof(debug_context_t));
437
+ new_debug_context->stop_next = -1;
438
+ new_debug_context->dest_frame = -1;
439
+ new_debug_context->stop_line = -1;
440
+ new_debug_context->stop_frame = -1;
441
+ CTX_FL_SET(new_debug_context, CTX_FL_DEAD);
442
+ new_debug_context->frames = ALLOC_N(debug_frame_t, debug_context->stack_size);
443
+ new_debug_context->stack_len = debug_context->stack_size;
444
+ memcpy(new_debug_context->frames, debug_context->frames, sizeof(debug_frame_t) * debug_context->stack_size);
445
+ for(i = 0; i < debug_context->stack_size; i++)
446
+ {
447
+ new_frame = &(new_debug_context->frames[i]);
448
+ old_frame = &(debug_context->frames[i]);
449
+ new_frame->dead = 1;
450
+ new_frame->info.copy.locals = context_copy_locals(old_frame);
451
+ }
452
+ return Data_Wrap_Struct(cContext, debug_context_mark, debug_context_free, new_debug_context);
453
+ }
454
+
455
+ static void
456
+ thread_context_lookup(VALUE thread, VALUE *context, debug_context_t **debug_context)
457
+ {
458
+ threads_table_t *threads_table;
459
+ VALUE thread_id;
460
+ debug_context_t *l_debug_context;
461
+
462
+ debug_check_started();
463
+
464
+ if(last_thread == thread && last_context != Qnil)
465
+ {
466
+ *context = last_context;
467
+ if(debug_context)
468
+ *debug_context = last_debug_context;
469
+ return;
470
+ }
471
+ thread_id = ref2id(thread);
472
+ Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
473
+ if(!st_lookup(threads_table->tbl, thread_id, context))
474
+ {
475
+ *context = debug_context_create(thread);
476
+ st_insert(threads_table->tbl, thread_id, *context);
477
+ }
478
+
479
+ Data_Get_Struct(*context, debug_context_t, l_debug_context);
480
+ if(debug_context)
481
+ *debug_context = l_debug_context;
482
+
483
+ last_thread = thread;
484
+ last_context = *context;
485
+ last_debug_context = l_debug_context;
486
+ }
487
+
488
+ static VALUE
489
+ call_at_line_unprotected(VALUE args)
490
+ {
491
+ VALUE context;
492
+ context = *RARRAY(args)->ptr;
493
+ return rb_funcall2(context, idAtLine, RARRAY(args)->len - 1, RARRAY(args)->ptr + 1);
494
+ }
495
+
496
+ static VALUE
497
+ call_at_line(VALUE context, debug_context_t *debug_context, VALUE file, VALUE line)
498
+ {
499
+ VALUE args;
500
+
501
+ last_debugged_thnum = debug_context->thnum;
502
+ save_current_position(debug_context);
503
+
504
+ args = rb_ary_new3(3, context, file, line);
505
+ return rb_protect(call_at_line_unprotected, args, 0);
506
+ }
507
+
508
+ static void
509
+ save_call_frame(rb_event_t event, VALUE self, char *file, int line, ID mid, debug_context_t *debug_context)
510
+ {
511
+ VALUE binding;
512
+ debug_frame_t *debug_frame;
513
+ int frame_n;
514
+
515
+ binding = self && RTEST(keep_frame_binding)? create_binding(self) : Qnil;
516
+
517
+ frame_n = debug_context->stack_size++;
518
+ if(frame_n >= debug_context->stack_len)
519
+ {
520
+ debug_context->stack_len += STACK_SIZE_INCREMENT;
521
+ debug_context->frames = REALLOC_N(debug_context->frames, debug_frame_t, debug_context->stack_len);
522
+ }
523
+ debug_frame = &debug_context->frames[frame_n];
524
+ debug_frame->file = file;
525
+ debug_frame->line = line;
526
+ debug_frame->binding = binding;
527
+ debug_frame->id = mid;
528
+ debug_frame->orig_id = mid;
529
+ debug_frame->dead = 0;
530
+ debug_frame->self = self;
531
+ debug_frame->info.runtime.frame = ruby_frame;
532
+ debug_frame->info.runtime.scope = ruby_scope;
533
+ debug_frame->info.runtime.dyna_vars = event == RUBY_EVENT_LINE ? ruby_dyna_vars : NULL;
534
+ }
535
+
536
+ #if defined DOSISH
537
+ #define isdirsep(x) ((x) == '/' || (x) == '\\')
538
+ #else
539
+ #define isdirsep(x) ((x) == '/')
540
+ #endif
541
+
542
+ static int
543
+ filename_cmp(VALUE source, char *file)
544
+ {
545
+ char *source_ptr, *file_ptr;
546
+ int s_len, f_len, min_len;
547
+ int s,f;
548
+ int dirsep_flag = 0;
549
+
550
+ s_len = RSTRING(source)->len;
551
+ f_len = strlen(file);
552
+ min_len = min(s_len, f_len);
553
+
554
+ source_ptr = RSTRING(source)->ptr;
555
+ file_ptr = file;
556
+
557
+ for( s = s_len - 1, f = f_len - 1; s >= s_len - min_len && f >= f_len - min_len; s--, f-- )
558
+ {
559
+ if((source_ptr[s] == '.' || file_ptr[f] == '.') && dirsep_flag)
560
+ return 1;
561
+ if(source_ptr[s] != file_ptr[f])
562
+ return 0;
563
+ if(isdirsep(source_ptr[s]))
564
+ dirsep_flag = 1;
565
+ }
566
+ return 1;
567
+ }
568
+
569
+ static int
570
+ check_breakpoints_by_pos(debug_context_t *debug_context, char *file, int line)
571
+ {
572
+ VALUE breakpoint;
573
+ debug_breakpoint_t *debug_breakpoint;
574
+ int i;
575
+
576
+ if(RARRAY(breakpoints)->len == 0)
577
+ return -1;
578
+ if(!CTX_FL_TEST(debug_context, CTX_FL_MOVED))
579
+ return -1;
580
+
581
+ for(i = 0; i < RARRAY(breakpoints)->len; i++)
582
+ {
583
+ breakpoint = rb_ary_entry(breakpoints, i);
584
+ Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
585
+ if(debug_breakpoint->type != BP_POS_TYPE)
586
+ continue;
587
+ if(debug_breakpoint->pos.line != line)
588
+ continue;
589
+ if(filename_cmp(debug_breakpoint->source, file))
590
+ return i;
591
+ }
592
+ return -1;
593
+ }
594
+
595
+ inline static int
596
+ classname_cmp(VALUE name, VALUE klass)
597
+ {
598
+ return (klass != Qnil && rb_str_cmp(name, rb_mod_name(klass)) == 0);
599
+ }
600
+
601
+ static int
602
+ check_breakpoints_by_method(debug_context_t *debug_context, VALUE klass, ID mid)
603
+ {
604
+ VALUE breakpoint;
605
+ debug_breakpoint_t *debug_breakpoint;
606
+ int i;
607
+
608
+ if(RARRAY(breakpoints)->len == 0)
609
+ return -1;
610
+ if(!CTX_FL_TEST(debug_context, CTX_FL_MOVED))
611
+ return -1;
612
+ for(i = 0; i < RARRAY(breakpoints)->len; i++)
613
+ {
614
+ breakpoint = rb_ary_entry(breakpoints, i);
615
+ Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
616
+ if(debug_breakpoint->type != BP_METHOD_TYPE)
617
+ continue;
618
+ if(debug_breakpoint->pos.mid != mid)
619
+ continue;
620
+ if(classname_cmp(debug_breakpoint->source, klass))
621
+ return i;
622
+ }
623
+ return -1;
624
+ }
625
+
626
+ /*
627
+ * This is a NASTY HACK. For some reasons rb_f_binding is decalred
628
+ * static in eval.c
629
+ */
630
+ static VALUE
631
+ create_binding(VALUE self)
632
+ {
633
+ typedef VALUE (*bind_func_t)(VALUE);
634
+ static bind_func_t f_binding = NULL;
635
+
636
+ if(f_binding == NULL)
637
+ {
638
+ f_binding = (bind_func_t)ruby_method_ptr(rb_mKernel, rb_intern("binding"));
639
+ }
640
+ return f_binding(self);
641
+ }
642
+
643
+ static VALUE
644
+ get_breakpoint_at(int index)
645
+ {
646
+ return rb_ary_entry(breakpoints, index);
647
+ }
648
+
649
+ static VALUE
650
+ eval_expression(VALUE args)
651
+ {
652
+ return rb_funcall2(rb_mKernel, idEval, 2, RARRAY(args)->ptr);
653
+ }
654
+
655
+ inline static int
656
+ check_breakpoint_expression(VALUE breakpoint, VALUE binding)
657
+ {
658
+ debug_breakpoint_t *debug_breakpoint;
659
+ VALUE args, expr_result;
660
+
661
+ Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
662
+ if(NIL_P(debug_breakpoint->expr))
663
+ return 1;
664
+
665
+ args = rb_ary_new3(2, debug_breakpoint->expr, binding);
666
+ expr_result = rb_protect(eval_expression, args, 0);
667
+ return RTEST(expr_result);
668
+ }
669
+
670
+ inline static debug_frame_t *
671
+ get_top_frame(debug_context_t *debug_context)
672
+ {
673
+ if(debug_context->stack_size == 0)
674
+ return NULL;
675
+ else
676
+ return &(debug_context->frames[debug_context->stack_size-1]);
677
+ }
678
+
679
+ inline static void
680
+ save_top_binding(debug_context_t *debug_context, VALUE binding)
681
+ {
682
+ debug_frame_t *debug_frame;
683
+ debug_frame = get_top_frame(debug_context);
684
+ if(debug_frame)
685
+ debug_frame->binding = binding;
686
+ }
687
+
688
+ inline static void
689
+ set_frame_source(rb_event_t event, debug_context_t *debug_context, VALUE self, char *file, int line, ID mid)
690
+ {
691
+ debug_frame_t *top_frame;
692
+ top_frame = get_top_frame(debug_context);
693
+ if(top_frame)
694
+ {
695
+ top_frame->self = self;
696
+ top_frame->file = file;
697
+ top_frame->line = line;
698
+ top_frame->id = mid;
699
+ top_frame->info.runtime.dyna_vars = event == RUBY_EVENT_C_CALL ? NULL : ruby_dyna_vars;
700
+ }
701
+ }
702
+
703
+ static void
704
+ save_current_position(debug_context_t *debug_context)
705
+ {
706
+ debug_frame_t *debug_frame;
707
+
708
+ debug_frame = get_top_frame(debug_context);
709
+ if(!debug_frame) return;
710
+ debug_context->last_file = debug_frame->file;
711
+ debug_context->last_line = debug_frame->line;
712
+ CTX_FL_UNSET(debug_context, CTX_FL_MOVED);
713
+ }
714
+
715
+ static char *
716
+ get_event_name(rb_event_t event)
717
+ {
718
+ switch (event) {
719
+ case RUBY_EVENT_LINE:
720
+ return "line";
721
+ case RUBY_EVENT_CLASS:
722
+ return "class";
723
+ case RUBY_EVENT_END:
724
+ return "end";
725
+ case RUBY_EVENT_CALL:
726
+ return "call";
727
+ case RUBY_EVENT_RETURN:
728
+ return "return";
729
+ case RUBY_EVENT_C_CALL:
730
+ return "c-call";
731
+ case RUBY_EVENT_C_RETURN:
732
+ return "c-return";
733
+ case RUBY_EVENT_RAISE:
734
+ return "raise";
735
+ default:
736
+ return "unknown";
737
+ }
738
+ }
739
+
740
+
741
+ static void
742
+ debug_event_hook(rb_event_t event, NODE *node, VALUE self, ID mid, VALUE klass)
743
+ {
744
+ VALUE thread, context, breakpoint;
745
+ VALUE binding = Qnil;
746
+ debug_context_t *debug_context;
747
+ char *file;
748
+ int line;
749
+ int breakpoint_index = -1;
750
+
751
+ hook_count++;
752
+
753
+ if (mid == ID_ALLOCATOR) return;
754
+
755
+ thread = rb_thread_current();
756
+ thread_context_lookup(thread, &context, &debug_context);
757
+
758
+ /* return if thread is marked as 'ignored'.
759
+ debugger's threads are marked this way
760
+ */
761
+ if(CTX_FL_TEST(debug_context, CTX_FL_IGNORE)) return;
762
+
763
+ while(1)
764
+ {
765
+ /* halt execution of the current thread if the debugger
766
+ is activated in another
767
+ */
768
+ while(locker != Qnil && locker != thread)
769
+ {
770
+ add_to_locked(thread);
771
+ rb_thread_stop();
772
+ }
773
+
774
+ /* stop the current thread if it's marked as suspended */
775
+ if(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) && locker != thread)
776
+ {
777
+ CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
778
+ rb_thread_stop();
779
+ }
780
+ else break;
781
+ }
782
+
783
+ /* return if the current thread is the locker */
784
+ if(locker != Qnil) return;
785
+
786
+ /* only the current thread can proceed */
787
+ locker = thread;
788
+
789
+ /* ignore a skipped section of code */
790
+ if(CTX_FL_TEST(debug_context, CTX_FL_SKIPPED)) goto cleanup;
791
+
792
+ if(node)
793
+ {
794
+ file = node->nd_file;
795
+ line = nd_line(node);
796
+
797
+ if(debug == Qtrue)
798
+ fprintf(stderr, "%s:%d [%s] %s\n", file, line, get_event_name(event), rb_id2name(mid));
799
+
800
+ if(DID_MOVED)
801
+ CTX_FL_SET(debug_context, CTX_FL_MOVED);
802
+ }
803
+ else if(event != RUBY_EVENT_RETURN && event != RUBY_EVENT_C_RETURN)
804
+ {
805
+ if(debug == Qtrue)
806
+ fprintf(stderr, "return [%s] %s\n", get_event_name(event), rb_id2name(mid));
807
+ goto cleanup;
808
+ }
809
+
810
+ switch(event)
811
+ {
812
+ case RUBY_EVENT_LINE:
813
+ {
814
+ set_frame_source(event, debug_context, self, file, line, mid);
815
+
816
+ if(RTEST(tracing) || CTX_FL_TEST(debug_context, CTX_FL_TRACING))
817
+ rb_funcall(context, idAtTracing, 2, rb_str_new2(file), INT2FIX(line));
818
+
819
+ if(debug_context->dest_frame == -1 ||
820
+ debug_context->stack_size == debug_context->dest_frame)
821
+ {
822
+ debug_context->stop_next--;
823
+ if(debug_context->stop_next < 0)
824
+ debug_context->stop_next = -1;
825
+ /* we check that we actualy moved to another line */
826
+ if(DID_MOVED)
827
+ debug_context->stop_line--;
828
+ }
829
+ else if(debug_context->stack_size < debug_context->dest_frame)
830
+ {
831
+ debug_context->stop_next = 0;
832
+ }
833
+
834
+ if(debug_context->stack_size == 0)
835
+ save_call_frame(event, self, file, line, mid, debug_context);
836
+
837
+ if(debug_context->stop_next == 0 || debug_context->stop_line == 0 ||
838
+ (breakpoint_index = check_breakpoints_by_pos(debug_context, file, line)) != -1)
839
+ {
840
+ binding = self? create_binding(self) : Qnil;
841
+ /* check breakpoint expression */
842
+ if(breakpoint_index != -1)
843
+ {
844
+ breakpoint = get_breakpoint_at(breakpoint_index);
845
+ if(check_breakpoint_expression(breakpoint, binding))
846
+ rb_funcall(context, idAtBreakpoint, 1, breakpoint);
847
+ else
848
+ break;
849
+ }
850
+
851
+ /* reset all pointers */
852
+ debug_context->dest_frame = -1;
853
+ debug_context->stop_line = -1;
854
+ debug_context->stop_next = -1;
855
+
856
+ save_top_binding(debug_context, binding);
857
+ call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
858
+ }
859
+ break;
860
+ }
861
+ case RUBY_EVENT_C_CALL:
862
+ {
863
+ set_frame_source(event, debug_context, self, file, line, mid);
864
+ break;
865
+ }
866
+ case RUBY_EVENT_CALL:
867
+ {
868
+ save_call_frame(event, self, file, line, mid, debug_context);
869
+ breakpoint_index = check_breakpoints_by_method(debug_context, klass, mid);
870
+ if(breakpoint_index != -1)
871
+ {
872
+ debug_frame_t *debug_frame;
873
+ debug_frame = get_top_frame(debug_context);
874
+ if(debug_frame)
875
+ binding = debug_frame->binding;
876
+ if(NIL_P(binding) && self)
877
+ binding = create_binding(self);
878
+ breakpoint = get_breakpoint_at(breakpoint_index);
879
+ if(check_breakpoint_expression(breakpoint, binding))
880
+ {
881
+ save_top_binding(debug_context, binding);
882
+ rb_funcall(context, idAtBreakpoint, 1, breakpoint);
883
+ call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
884
+ }
885
+ }
886
+ break;
887
+ }
888
+ case RUBY_EVENT_RETURN:
889
+ case RUBY_EVENT_END:
890
+ {
891
+ if(debug_context->stack_size == debug_context->stop_frame)
892
+ {
893
+ debug_context->stop_next = 1;
894
+ debug_context->stop_frame = 0;
895
+ }
896
+ while(debug_context->stack_size > 0)
897
+ {
898
+ debug_context->stack_size--;
899
+ if(debug_context->frames[debug_context->stack_size].orig_id == mid)
900
+ break;
901
+ }
902
+ break;
903
+ }
904
+ case RUBY_EVENT_CLASS:
905
+ {
906
+ save_call_frame(event, self, file, line, mid, debug_context);
907
+ break;
908
+ }
909
+ case RUBY_EVENT_RAISE:
910
+ {
911
+ VALUE ancestors;
912
+ VALUE expn_class, aclass;
913
+ int i;
914
+
915
+ set_frame_source(event, debug_context, self, file, line, mid);
916
+
917
+ if(post_mortem == Qtrue && self)
918
+ {
919
+ binding = create_binding(self);
920
+ rb_ivar_set(ruby_errinfo, rb_intern("@__debug_file"), rb_str_new2(file));
921
+ rb_ivar_set(ruby_errinfo, rb_intern("@__debug_line"), INT2FIX(line));
922
+ rb_ivar_set(ruby_errinfo, rb_intern("@__debug_binding"), binding);
923
+ rb_ivar_set(ruby_errinfo, rb_intern("@__debug_context"), debug_context_dup(debug_context));
924
+ }
925
+
926
+ expn_class = rb_obj_class(ruby_errinfo);
927
+ if( !NIL_P(rb_class_inherited_p(expn_class, rb_eSystemExit)) )
928
+ {
929
+ debug_stop(mDebugger);
930
+ break;
931
+ }
932
+
933
+ if(catchpoint == Qnil)
934
+ break;
935
+
936
+ ancestors = rb_mod_ancestors(expn_class);
937
+ for(i = 0; i < RARRAY(ancestors)->len; i++)
938
+ {
939
+ aclass = rb_ary_entry(ancestors, i);
940
+ if(rb_str_cmp(rb_mod_name(aclass), catchpoint) == 0)
941
+ {
942
+ rb_funcall(context, idAtCatchpoint, 1, ruby_errinfo);
943
+ if(self && binding == Qnil)
944
+ binding = create_binding(self);
945
+ save_top_binding(debug_context, binding);
946
+ call_at_line(context, debug_context, rb_str_new2(file), INT2FIX(line));
947
+ break;
948
+ }
949
+ }
950
+
951
+ break;
952
+ }
953
+ case RUBY_EVENT_C_RETURN:
954
+ {
955
+ break;
956
+ }
957
+ }
958
+
959
+ cleanup:
960
+
961
+ /* check that all contexts point to alive threads */
962
+ if(hook_count - last_check > 3000)
963
+ {
964
+ check_thread_contexts();
965
+ last_check = hook_count;
966
+ }
967
+
968
+ /* release a lock */
969
+ locker = Qnil;
970
+ /* let the next thread to run */
971
+ thread = remove_from_locked();
972
+ if(thread != Qnil)
973
+ rb_thread_run(thread);
974
+ }
975
+
976
+ static VALUE
977
+ debug_stop_i(VALUE self)
978
+ {
979
+ if(IS_STARTED)
980
+ debug_stop(self);
981
+ return Qnil;
982
+ }
983
+
984
+ /*
985
+ * call-seq:
986
+ * Debugger.start -> bool
987
+ * Debugger.start { ... } -> obj
988
+ *
989
+ * This method activates the debugger.
990
+ * If it's called without a block it returns +true+, unless debugger was already started.
991
+ * If a block is given, it starts debugger and yields to block. When the block is finished
992
+ * executing it stops the debugger with Debugger.stop method.
993
+ *
994
+ * <i>Note that if you want to stop debugger, you must call Debugger.stop as many time as you
995
+ * called Debugger.start method.</i>
996
+ */
997
+ static VALUE
998
+ debug_start(VALUE self)
999
+ {
1000
+ VALUE result;
1001
+ start_count++;
1002
+
1003
+ if(IS_STARTED)
1004
+ result = Qfalse;
1005
+ else
1006
+ {
1007
+ breakpoints = rb_ary_new();
1008
+ locker = Qnil;
1009
+ threads_tbl = threads_table_create();
1010
+
1011
+ rb_add_event_hook(debug_event_hook, RUBY_EVENT_ALL);
1012
+ result = Qtrue;
1013
+ }
1014
+
1015
+ if(rb_block_given_p())
1016
+ return rb_ensure(rb_yield, self, debug_stop_i, self);
1017
+ return result;
1018
+ }
1019
+
1020
+ /*
1021
+ * call-seq:
1022
+ * Debugger.stop -> bool
1023
+ *
1024
+ * This method disacivates the debugger. It returns +true+ if the debugger is disacivated,
1025
+ * otherwise it returns +false+.
1026
+ *
1027
+ * <i>Note that if you want to stop debugger, you must call Debugger.stop as many time as you
1028
+ * called Debugger.start method.</i>
1029
+ */
1030
+ static VALUE
1031
+ debug_stop(VALUE self)
1032
+ {
1033
+ debug_check_started();
1034
+
1035
+ start_count--;
1036
+ if(start_count)
1037
+ return Qfalse;
1038
+
1039
+ rb_remove_event_hook(debug_event_hook);
1040
+
1041
+ locker = Qnil;
1042
+ breakpoints = Qnil;
1043
+ threads_tbl = Qnil;
1044
+
1045
+ return Qtrue;
1046
+ }
1047
+
1048
+ static void
1049
+ breakpoint_mark(void *data)
1050
+ {
1051
+ debug_breakpoint_t *breakpoint;
1052
+ breakpoint = (debug_breakpoint_t *)data;
1053
+ rb_gc_mark(breakpoint->source);
1054
+ rb_gc_mark(breakpoint->expr);
1055
+ }
1056
+
1057
+ /*
1058
+ * call-seq:
1059
+ * Debugger.add_breakpoint(source, pos, condition = nil) -> breakpoint
1060
+ *
1061
+ * Adds a new breakpoint.
1062
+ * <i>source</i> is a name of a file or a class.
1063
+ * <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
1064
+ * <i>condition</i> is a string which is evaluated to +true+ when this breakpoint
1065
+ * is activated.
1066
+ */
1067
+ static VALUE
1068
+ debug_add_breakpoint(int argc, VALUE *argv, VALUE self)
1069
+ {
1070
+ VALUE source, pos, expr;
1071
+ VALUE result;
1072
+ debug_breakpoint_t *breakpoint;
1073
+ int type;
1074
+
1075
+ debug_check_started();
1076
+
1077
+ if(rb_scan_args(argc, argv, "21", &source, &pos, &expr) == 2)
1078
+ {
1079
+ expr = Qnil;
1080
+ }
1081
+ type = FIXNUM_P(pos) ? BP_POS_TYPE : BP_METHOD_TYPE;
1082
+ if(type == BP_POS_TYPE)
1083
+ source = StringValue(source);
1084
+ else
1085
+ pos = StringValue(pos);
1086
+ breakpoint = ALLOC(debug_breakpoint_t);
1087
+ breakpoint->id = ++bkp_count;
1088
+ breakpoint->source = source;
1089
+ breakpoint->type = type;
1090
+ if(type == BP_POS_TYPE)
1091
+ breakpoint->pos.line = FIX2INT(pos);
1092
+ else
1093
+ breakpoint->pos.mid = rb_intern(RSTRING(pos)->ptr);
1094
+ breakpoint->expr = NIL_P(expr) ? expr: StringValue(expr);
1095
+ result = Data_Wrap_Struct(cBreakpoint, breakpoint_mark, xfree, breakpoint);
1096
+ rb_ary_push(breakpoints, result);
1097
+ return result;
1098
+ }
1099
+
1100
+ /*
1101
+ * call-seq:
1102
+ * Debugger.remove_breakpoint(id) -> breakpoint
1103
+ *
1104
+ * Removes breakpoint by its id.
1105
+ * <i>id</i> is an identificator of a breakpoint.
1106
+ */
1107
+ static VALUE
1108
+ debug_remove_breakpoint(VALUE self, VALUE id_value)
1109
+ {
1110
+ int i;
1111
+ int id;
1112
+ VALUE breakpoint;
1113
+ debug_breakpoint_t *debug_breakpoint;
1114
+
1115
+ id = FIX2INT(id_value);
1116
+
1117
+ for( i = 0; i < RARRAY(breakpoints)->len; i += 1 )
1118
+ {
1119
+ breakpoint = rb_ary_entry(breakpoints, i);
1120
+ Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
1121
+ if(debug_breakpoint->id == id)
1122
+ {
1123
+ rb_ary_delete_at(breakpoints, i);
1124
+ return breakpoint;
1125
+ }
1126
+ }
1127
+ return Qnil;
1128
+ }
1129
+
1130
+ /*
1131
+ * call-seq:
1132
+ * Debugger.breakpoints -> array
1133
+ *
1134
+ * Returns an array of breakpoints.
1135
+ */
1136
+ static VALUE
1137
+ debug_breakpoints(VALUE self)
1138
+ {
1139
+ debug_check_started();
1140
+
1141
+ return breakpoints;
1142
+ }
1143
+
1144
+ /*
1145
+ * call-seq:
1146
+ * Debugger.checkpoint -> string
1147
+ *
1148
+ * Returns a current checkpoint, which is a name of exception that will
1149
+ * trigger a debugger when raised.
1150
+ */
1151
+ static VALUE
1152
+ debug_catchpoint(VALUE self)
1153
+ {
1154
+ debug_check_started();
1155
+
1156
+ return catchpoint;
1157
+ }
1158
+
1159
+ /*
1160
+ * call-seq:
1161
+ * Debugger.checkpoint = string -> string
1162
+ *
1163
+ * Sets checkpoint.
1164
+ */
1165
+ static VALUE
1166
+ debug_set_catchpoint(VALUE self, VALUE value)
1167
+ {
1168
+ debug_check_started();
1169
+
1170
+ if (!NIL_P(value) && TYPE(value) != T_STRING) {
1171
+ rb_raise(rb_eTypeError, "value of checkpoint must be String");
1172
+ }
1173
+ if(NIL_P(value))
1174
+ catchpoint = Qnil;
1175
+ else
1176
+ catchpoint = rb_str_dup(value);
1177
+ return value;
1178
+ }
1179
+
1180
+ static int
1181
+ find_last_context_func(VALUE key, VALUE value, VALUE *result)
1182
+ {
1183
+ debug_context_t *debug_context;
1184
+ Data_Get_Struct(value, debug_context_t, debug_context);
1185
+ if(debug_context->thnum == last_debugged_thnum)
1186
+ {
1187
+ *result = value;
1188
+ return ST_STOP;
1189
+ }
1190
+ return ST_CONTINUE;
1191
+ }
1192
+
1193
+ /*
1194
+ * call-seq:
1195
+ * Debugger.last_interrupted -> context
1196
+ *
1197
+ * Returns last debugged context.
1198
+ */
1199
+ static VALUE
1200
+ debug_last_interrupted(VALUE self)
1201
+ {
1202
+ VALUE result = Qnil;
1203
+ threads_table_t *threads_table;
1204
+
1205
+ debug_check_started();
1206
+
1207
+ Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
1208
+
1209
+ st_foreach(threads_table->tbl, find_last_context_func, (st_data_t)&result);
1210
+ return result;
1211
+ }
1212
+
1213
+ /*
1214
+ * call-seq:
1215
+ * Debugger.current_context -> context
1216
+ *
1217
+ * Returns current context.
1218
+ * <i>Note:</i> Debugger.current_context.thread == Thread.current
1219
+ */
1220
+ static VALUE
1221
+ debug_current_context(VALUE self)
1222
+ {
1223
+ VALUE thread, context;
1224
+
1225
+ debug_check_started();
1226
+
1227
+ thread = rb_thread_current();
1228
+ thread_context_lookup(thread, &context, NULL);
1229
+
1230
+ return context;
1231
+ }
1232
+
1233
+ /*
1234
+ * call-seq:
1235
+ * Debugger.thread_context(thread) -> context
1236
+ *
1237
+ * Returns context of the thread passed as an argument.
1238
+ */
1239
+ static VALUE
1240
+ debug_thread_context(VALUE self, VALUE thread)
1241
+ {
1242
+ VALUE context;
1243
+
1244
+ debug_check_started();
1245
+ thread_context_lookup(thread, &context, NULL);
1246
+ return context;
1247
+ }
1248
+
1249
+ /*
1250
+ * call-seq:
1251
+ * Debugger.contexts -> array
1252
+ *
1253
+ * Returns an array of all contexts.
1254
+ */
1255
+ static VALUE
1256
+ debug_contexts(VALUE self)
1257
+ {
1258
+ volatile VALUE list;
1259
+ volatile VALUE new_list;
1260
+ VALUE thread, context;
1261
+ threads_table_t *threads_table;
1262
+ debug_context_t *debug_context;
1263
+ int i;
1264
+
1265
+ debug_check_started();
1266
+
1267
+ new_list = rb_ary_new();
1268
+ list = rb_funcall(rb_cThread, idList, 0);
1269
+ for(i = 0; i < RARRAY(list)->len; i++)
1270
+ {
1271
+ thread = rb_ary_entry(list, i);
1272
+ thread_context_lookup(thread, &context, NULL);
1273
+ rb_ary_push(new_list, context);
1274
+ }
1275
+ threads_table_clear(threads_tbl);
1276
+ Data_Get_Struct(threads_tbl, threads_table_t, threads_table);
1277
+ for(i = 0; i < RARRAY(new_list)->len; i++)
1278
+ {
1279
+ context = rb_ary_entry(new_list, i);
1280
+ Data_Get_Struct(context, debug_context_t, debug_context);
1281
+ st_insert(threads_table->tbl, debug_context->thread_id, context);
1282
+ }
1283
+
1284
+ return new_list;
1285
+ }
1286
+
1287
+ /*
1288
+ * call-seq:
1289
+ * Debugger.suspend -> Debugger
1290
+ *
1291
+ * Suspends all contexts.
1292
+ */
1293
+ static VALUE
1294
+ debug_suspend(VALUE self)
1295
+ {
1296
+ VALUE current, context;
1297
+ VALUE saved_crit;
1298
+ VALUE context_list;
1299
+ debug_context_t *debug_context;
1300
+ int i;
1301
+
1302
+ debug_check_started();
1303
+
1304
+ saved_crit = rb_thread_critical;
1305
+ rb_thread_critical = Qtrue;
1306
+ context_list = debug_contexts(self);
1307
+ thread_context_lookup(rb_thread_current(), &current, NULL);
1308
+
1309
+ for(i = 0; i < RARRAY(context_list)->len; i++)
1310
+ {
1311
+ context = rb_ary_entry(context_list, i);
1312
+ if(current == context)
1313
+ continue;
1314
+ Data_Get_Struct(context, debug_context_t, debug_context);
1315
+ context_suspend_0(debug_context);
1316
+ }
1317
+ rb_thread_critical = saved_crit;
1318
+
1319
+ if(rb_thread_critical == Qfalse)
1320
+ rb_thread_schedule();
1321
+
1322
+ return self;
1323
+ }
1324
+
1325
+ /*
1326
+ * call-seq:
1327
+ * Debugger.resume -> Debugger
1328
+ *
1329
+ * Resumes all contexts.
1330
+ */
1331
+ static VALUE
1332
+ debug_resume(VALUE self)
1333
+ {
1334
+ VALUE current, context;
1335
+ VALUE saved_crit;
1336
+ VALUE context_list;
1337
+ debug_context_t *debug_context;
1338
+ int i;
1339
+
1340
+ debug_check_started();
1341
+
1342
+ saved_crit = rb_thread_critical;
1343
+ rb_thread_critical = Qtrue;
1344
+ context_list = debug_contexts(self);
1345
+
1346
+ thread_context_lookup(rb_thread_current(), &current, NULL);
1347
+ for(i = 0; i < RARRAY(context_list)->len; i++)
1348
+ {
1349
+ context = rb_ary_entry(context_list, i);
1350
+ if(current == context)
1351
+ continue;
1352
+ Data_Get_Struct(context, debug_context_t, debug_context);
1353
+ context_resume_0(debug_context);
1354
+ }
1355
+ rb_thread_critical = saved_crit;
1356
+
1357
+ rb_thread_schedule();
1358
+
1359
+ return self;
1360
+ }
1361
+
1362
+ /*
1363
+ * call-seq:
1364
+ * Debugger.tracing -> bool
1365
+ *
1366
+ * Returns +true+ if the global tracing is activated.
1367
+ */
1368
+ static VALUE
1369
+ debug_tracing(VALUE self)
1370
+ {
1371
+ return tracing;
1372
+ }
1373
+
1374
+ /*
1375
+ * call-seq:
1376
+ * Debugger.tracing = bool
1377
+ *
1378
+ * Sets the global tracing flag.
1379
+ */
1380
+ static VALUE
1381
+ debug_set_tracing(VALUE self, VALUE value)
1382
+ {
1383
+ tracing = RTEST(value) ? Qtrue : Qfalse;
1384
+ return value;
1385
+ }
1386
+
1387
+ /*
1388
+ * call-seq:
1389
+ * Debugger.post_mortem? -> bool
1390
+ *
1391
+ * Returns +true+ if post-moterm debugging is enabled.
1392
+ */
1393
+ static VALUE
1394
+ debug_post_mortem(VALUE self)
1395
+ {
1396
+ return post_mortem;
1397
+ }
1398
+
1399
+ /*
1400
+ * call-seq:
1401
+ * Debugger.post_mortem = bool
1402
+ *
1403
+ * Sets post-moterm flag.
1404
+ * FOR INTERNAL USE ONLY.
1405
+ */
1406
+ static VALUE
1407
+ debug_set_post_mortem(VALUE self, VALUE value)
1408
+ {
1409
+ debug_check_started();
1410
+
1411
+ post_mortem = RTEST(value) ? Qtrue : Qfalse;
1412
+ return value;
1413
+ }
1414
+
1415
+ /*
1416
+ * call-seq:
1417
+ * Debugger.keep_frame_binding? -> bool
1418
+ *
1419
+ * Returns +true+ if the debugger will collect frame bindings.
1420
+ */
1421
+ static VALUE
1422
+ debug_keep_frame_binding(VALUE self)
1423
+ {
1424
+ return keep_frame_binding;
1425
+ }
1426
+
1427
+ /*
1428
+ * call-seq:
1429
+ * Debugger.keep_frame_binding = bool
1430
+ *
1431
+ * Setting to +true+ will make the debugger create frame bindings.
1432
+ */
1433
+ static VALUE
1434
+ debug_set_keep_frame_binding(VALUE self, VALUE value)
1435
+ {
1436
+ keep_frame_binding = RTEST(value) ? Qtrue : Qfalse;
1437
+ return value;
1438
+ }
1439
+
1440
+ /* :nodoc: */
1441
+ static VALUE
1442
+ debug_debug(VALUE self)
1443
+ {
1444
+ return debug;
1445
+ }
1446
+
1447
+ /* :nodoc: */
1448
+ static VALUE
1449
+ debug_set_debug(VALUE self, VALUE value)
1450
+ {
1451
+ debug = RTEST(value) ? Qtrue : Qfalse;
1452
+ return value;
1453
+ }
1454
+
1455
+ /* :nodoc: */
1456
+ static VALUE
1457
+ debug_thread_inherited(VALUE klass)
1458
+ {
1459
+ rb_raise(rb_eRuntimeError, "Can't inherite Debugger::DebugThread class");
1460
+ }
1461
+
1462
+ /*
1463
+ * call-seq:
1464
+ * Debugger.debug_load(file, stop = false) -> nil
1465
+ *
1466
+ * Same as Kernel#load but resets current context's frames.
1467
+ * +stop+ parameter force the debugger to stop at the first line of code in the +file+
1468
+ * FOR INTERNAL USE ONLY.
1469
+ */
1470
+ static VALUE
1471
+ debug_debug_load(int argc, VALUE *argv, VALUE self)
1472
+ {
1473
+ VALUE file, stop, context;
1474
+ debug_context_t *debug_context;
1475
+
1476
+ if(rb_scan_args(argc, argv, "11", &file, &stop) == 1)
1477
+ stop = Qfalse;
1478
+
1479
+ debug_start(self);
1480
+ context = debug_current_context(self);
1481
+ Data_Get_Struct(context, debug_context_t, debug_context);
1482
+ debug_context->stack_size = 0;
1483
+ if(RTEST(stop))
1484
+ debug_context->stop_next = 1;
1485
+ rb_load(file, 0);
1486
+
1487
+ debug_stop(self);
1488
+ return Qnil;
1489
+ }
1490
+
1491
+ static VALUE
1492
+ set_current_skipped_status(VALUE status)
1493
+ {
1494
+ VALUE context;
1495
+ debug_context_t *debug_context;
1496
+
1497
+ context = debug_current_context(Qnil);
1498
+ Data_Get_Struct(context, debug_context_t, debug_context);
1499
+ if(status)
1500
+ CTX_FL_SET(debug_context, CTX_FL_SKIPPED);
1501
+ else
1502
+ CTX_FL_UNSET(debug_context, CTX_FL_SKIPPED);
1503
+ return Qnil;
1504
+ }
1505
+
1506
+ /*
1507
+ * call-seq:
1508
+ * Debugger.skip { block } -> obj or nil
1509
+ *
1510
+ * The code inside of the block is escaped from the debugger.
1511
+ */
1512
+ static VALUE
1513
+ debug_skip(VALUE self)
1514
+ {
1515
+ if (!rb_block_given_p()) {
1516
+ rb_raise(rb_eArgError, "called without a block");
1517
+ }
1518
+ if(!IS_STARTED)
1519
+ return rb_yield(Qnil);
1520
+ set_current_skipped_status(Qtrue);
1521
+ return rb_ensure(rb_yield, Qnil, set_current_skipped_status, Qfalse);
1522
+ }
1523
+
1524
+ static VALUE
1525
+ debug_at_exit_c(VALUE proc)
1526
+ {
1527
+ return rb_funcall(proc, rb_intern("call"), 0);
1528
+ }
1529
+
1530
+ static void
1531
+ debug_at_exit_i(VALUE proc)
1532
+ {
1533
+ if(!IS_STARTED)
1534
+ {
1535
+ debug_at_exit_c(proc);
1536
+ }
1537
+ else
1538
+ {
1539
+ set_current_skipped_status(Qtrue);
1540
+ rb_ensure(debug_at_exit_c, proc, set_current_skipped_status, Qfalse);
1541
+ }
1542
+ }
1543
+
1544
+ /*
1545
+ * call-seq:
1546
+ * Debugger.debug_at_exit { block } -> proc
1547
+ *
1548
+ * Register <tt>at_exit</tt> hook which is escaped from the debugger.
1549
+ * FOR INTERNAL USE ONLY.
1550
+ */
1551
+ static VALUE
1552
+ debug_at_exit(VALUE self)
1553
+ {
1554
+ VALUE proc;
1555
+ if (!rb_block_given_p()) {
1556
+ rb_raise(rb_eArgError, "called without a block");
1557
+ }
1558
+ proc = rb_block_proc();
1559
+ rb_set_end_proc(debug_at_exit_i, proc);
1560
+ return proc;
1561
+ }
1562
+
1563
+ /*
1564
+ * call-seq:
1565
+ * context.stop_next = steps
1566
+ *
1567
+ * Stops the current context after a number +steps+ are made.
1568
+ */
1569
+ static VALUE
1570
+ context_stop_next(VALUE self, VALUE steps)
1571
+ {
1572
+ debug_context_t *debug_context;
1573
+
1574
+ debug_check_started();
1575
+ Data_Get_Struct(self, debug_context_t, debug_context);
1576
+ if(FIX2INT(steps) < 0)
1577
+ rb_raise(rb_eRuntimeError, "Steps argument can't be negative.");
1578
+ debug_context->stop_next = FIX2INT(steps);
1579
+
1580
+ return steps;
1581
+ }
1582
+
1583
+ /*
1584
+ * call-seq:
1585
+ * context.step_over(steps)
1586
+ *
1587
+ * Steps over a +steps+ number of times.
1588
+ */
1589
+ static VALUE
1590
+ context_step_over(int argc, VALUE *argv, VALUE self)
1591
+ {
1592
+ VALUE lines, frame;
1593
+ debug_context_t *debug_context;
1594
+
1595
+ debug_check_started();
1596
+ Data_Get_Struct(self, debug_context_t, debug_context);
1597
+ if(debug_context->stack_size == 0)
1598
+ rb_raise(rb_eRuntimeError, "No frames collected.");
1599
+
1600
+ rb_scan_args(argc, argv, "11", &lines, &frame);
1601
+ debug_context->stop_line = FIX2INT(lines);
1602
+ if(argc == 1)
1603
+ {
1604
+ debug_context->dest_frame = debug_context->stack_size;
1605
+ }
1606
+ else
1607
+ {
1608
+ if(FIX2INT(frame) < 0 && FIX2INT(frame) >= debug_context->stack_size)
1609
+ rb_raise(rb_eRuntimeError, "Destination frame is out of range.");
1610
+ debug_context->dest_frame = debug_context->stack_size - FIX2INT(frame);
1611
+ }
1612
+
1613
+ return Qnil;
1614
+ }
1615
+
1616
+ /*
1617
+ * call-seq:
1618
+ * context.stop_frame(frame)
1619
+ *
1620
+ * Stops when a frame with number +frame+ is activated. Implements +up+ and +down+ commands.
1621
+ */
1622
+ static VALUE
1623
+ context_stop_frame(VALUE self, VALUE frame)
1624
+ {
1625
+ debug_context_t *debug_context;
1626
+
1627
+ debug_check_started();
1628
+ Data_Get_Struct(self, debug_context_t, debug_context);
1629
+ if(FIX2INT(frame) < 0 && FIX2INT(frame) >= debug_context->stack_size)
1630
+ rb_raise(rb_eRuntimeError, "Stop frame is out of range.");
1631
+ debug_context->stop_frame = debug_context->stack_size - FIX2INT(frame);
1632
+
1633
+ return frame;
1634
+ }
1635
+
1636
+ inline static int
1637
+ check_frame_number(debug_context_t *debug_context, VALUE frame)
1638
+ {
1639
+ int frame_n;
1640
+
1641
+ frame_n = FIX2INT(frame);
1642
+ if(frame_n < 0 || frame_n >= debug_context->stack_size)
1643
+ rb_raise(rb_eArgError, "Invalid frame number %d, stack (0...%d)",
1644
+ frame_n, debug_context->stack_size);
1645
+ return frame_n;
1646
+ }
1647
+
1648
+ /*
1649
+ * call-seq:
1650
+ * context.frame_binding(frame) -> binding
1651
+ *
1652
+ * Returns frame's binding.
1653
+ */
1654
+ static VALUE
1655
+ context_frame_binding(VALUE self, VALUE frame)
1656
+ {
1657
+ debug_context_t *debug_context;
1658
+
1659
+ debug_check_started();
1660
+ Data_Get_Struct(self, debug_context_t, debug_context);
1661
+ return GET_FRAME->binding;
1662
+ }
1663
+
1664
+ /*
1665
+ * call-seq:
1666
+ * context.frame_id(frame) -> sym
1667
+ *
1668
+ * Returns the sym of the called method.
1669
+ */
1670
+ static VALUE
1671
+ context_frame_id(VALUE self, VALUE frame)
1672
+ {
1673
+
1674
+ debug_context_t *debug_context;
1675
+ ID id;
1676
+
1677
+ debug_check_started();
1678
+ Data_Get_Struct(self, debug_context_t, debug_context);
1679
+
1680
+ id = GET_FRAME->id;
1681
+ return id ? ID2SYM(id): Qnil;
1682
+ }
1683
+
1684
+ /*
1685
+ * call-seq:
1686
+ * context.frame_line(frame) -> int
1687
+ *
1688
+ * Returns the line number in the file.
1689
+ */
1690
+ static VALUE
1691
+ context_frame_line(VALUE self, VALUE frame)
1692
+ {
1693
+ debug_context_t *debug_context;
1694
+
1695
+ debug_check_started();
1696
+ Data_Get_Struct(self, debug_context_t, debug_context);
1697
+
1698
+ return INT2FIX(GET_FRAME->line);
1699
+ }
1700
+
1701
+ /*
1702
+ * call-seq:
1703
+ * context.frame_file(frame) -> string
1704
+ *
1705
+ * Returns the name of the file.
1706
+ */
1707
+ static VALUE
1708
+ context_frame_file(VALUE self, VALUE frame)
1709
+ {
1710
+ debug_context_t *debug_context;
1711
+
1712
+ debug_check_started();
1713
+ Data_Get_Struct(self, debug_context_t, debug_context);
1714
+
1715
+ return rb_str_new2(GET_FRAME->file);
1716
+ }
1717
+
1718
+ static VALUE
1719
+ context_copy_locals(debug_frame_t *debug_frame)
1720
+ {
1721
+ ID *tbl;
1722
+ int n, i;
1723
+ struct SCOPE *scope;
1724
+ struct RVarmap *vars;
1725
+ VALUE hash = rb_hash_new();
1726
+
1727
+ scope = debug_frame->info.runtime.scope;
1728
+ tbl = scope->local_tbl;
1729
+
1730
+ if (tbl && scope->local_vars)
1731
+ {
1732
+ n = *tbl++;
1733
+ for (i=2; i<n; i++)
1734
+ { /* skip first 2 ($_ and $~) */
1735
+ if (!rb_is_local_id(tbl[i])) continue; /* skip flip states */
1736
+ rb_hash_aset(hash, rb_str_new2(rb_id2name(tbl[i])), scope->local_vars[i]);
1737
+ }
1738
+ }
1739
+
1740
+ vars = debug_frame->info.runtime.dyna_vars;
1741
+ while (vars)
1742
+ {
1743
+ if (vars->id && rb_is_local_id(vars->id))
1744
+ { /* skip $_, $~ and flip states */
1745
+ rb_hash_aset(hash, rb_str_new2(rb_id2name(vars->id)), vars->val);
1746
+ }
1747
+ vars = vars->next;
1748
+ }
1749
+ return hash;
1750
+ }
1751
+
1752
+ /*
1753
+ * call-seq:
1754
+ * context.frame_locals(frame) -> hash
1755
+ *
1756
+ * Returns frame's local variables.
1757
+ */
1758
+ static VALUE
1759
+ context_frame_locals(VALUE self, VALUE frame)
1760
+ {
1761
+ debug_context_t *debug_context;
1762
+ debug_frame_t *debug_frame;
1763
+
1764
+ debug_check_started();
1765
+ Data_Get_Struct(self, debug_context_t, debug_context);
1766
+
1767
+ debug_frame = GET_FRAME;
1768
+ if(debug_frame->dead)
1769
+ return debug_frame->info.copy.locals;
1770
+ else
1771
+ return context_copy_locals(debug_frame);
1772
+ }
1773
+
1774
+ /*
1775
+ * call-seq:
1776
+ * context.frame_self(frame) -> obj
1777
+ *
1778
+ * Returns self object of the frame.
1779
+ */
1780
+ static VALUE
1781
+ context_frame_self(VALUE self, VALUE frame)
1782
+ {
1783
+ debug_context_t *debug_context;
1784
+ debug_frame_t *debug_frame;
1785
+
1786
+ debug_check_started();
1787
+ Data_Get_Struct(self, debug_context_t, debug_context);
1788
+
1789
+ debug_frame = GET_FRAME;
1790
+ return debug_frame->self;
1791
+ }
1792
+
1793
+ /*
1794
+ * call-seq:
1795
+ * context.stack_size-> int
1796
+ *
1797
+ * Returns the size of the context stack.
1798
+ */
1799
+ static VALUE
1800
+ context_stack_size(VALUE self)
1801
+ {
1802
+ debug_context_t *debug_context;
1803
+
1804
+ debug_check_started();
1805
+ Data_Get_Struct(self, debug_context_t, debug_context);
1806
+
1807
+ return INT2FIX(debug_context->stack_size);
1808
+ }
1809
+
1810
+ /*
1811
+ * call-seq:
1812
+ * context.thread -> trhread
1813
+ *
1814
+ * Returns a thread this context is associated with.
1815
+ */
1816
+ static VALUE
1817
+ context_thread(VALUE self)
1818
+ {
1819
+ debug_context_t *debug_context;
1820
+
1821
+ debug_check_started();
1822
+ Data_Get_Struct(self, debug_context_t, debug_context);
1823
+ return context_thread_0(debug_context);
1824
+ }
1825
+
1826
+ /*
1827
+ * call-seq:
1828
+ * context.thnum -> int
1829
+ *
1830
+ * Returns the context's number.
1831
+ */
1832
+ static VALUE
1833
+ context_thnum(VALUE self)
1834
+ {
1835
+ debug_context_t *debug_context;
1836
+
1837
+ Data_Get_Struct(self, debug_context_t, debug_context);
1838
+ return INT2FIX(debug_context->thnum);
1839
+ }
1840
+
1841
+ static void
1842
+ context_suspend_0(debug_context_t *debug_context)
1843
+ {
1844
+ VALUE status;
1845
+
1846
+ status = rb_funcall(context_thread_0(debug_context), rb_intern("status"), 0);
1847
+ if(rb_str_cmp(status, rb_str_new2("run")) == 0)
1848
+ CTX_FL_SET(debug_context, CTX_FL_WAS_RUNNING);
1849
+ else if(rb_str_cmp(status, rb_str_new2("sleep")) == 0)
1850
+ CTX_FL_UNSET(debug_context, CTX_FL_WAS_RUNNING);
1851
+ else
1852
+ return;
1853
+ CTX_FL_SET(debug_context, CTX_FL_SUSPEND);
1854
+ }
1855
+
1856
+ static void
1857
+ context_resume_0(debug_context_t *debug_context)
1858
+ {
1859
+ if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
1860
+ return;
1861
+ CTX_FL_UNSET(debug_context, CTX_FL_SUSPEND);
1862
+ if(CTX_FL_TEST(debug_context, CTX_FL_WAS_RUNNING))
1863
+ rb_thread_wakeup(context_thread_0(debug_context));
1864
+ }
1865
+
1866
+ /*
1867
+ * call-seq:
1868
+ * context.suspend -> nil
1869
+ *
1870
+ * Suspends the thread when it is running.
1871
+ */
1872
+ static VALUE
1873
+ context_suspend(VALUE self)
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(CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
1881
+ rb_raise(rb_eRuntimeError, "Already suspended.");
1882
+ context_suspend_0(debug_context);
1883
+ return Qnil;
1884
+ }
1885
+
1886
+ /*
1887
+ * call-seq:
1888
+ * context.suspended? -> bool
1889
+ *
1890
+ * Returns +true+ if the thread is suspended by debugger.
1891
+ */
1892
+ static VALUE
1893
+ context_is_suspended(VALUE self)
1894
+ {
1895
+ debug_context_t *debug_context;
1896
+
1897
+ debug_check_started();
1898
+
1899
+ Data_Get_Struct(self, debug_context_t, debug_context);
1900
+ return CTX_FL_TEST(debug_context, CTX_FL_SUSPEND) ? Qtrue : Qfalse;
1901
+ }
1902
+
1903
+ /*
1904
+ * call-seq:
1905
+ * context.resume -> nil
1906
+ *
1907
+ * Resumes the thread from the suspended mode.
1908
+ */
1909
+ static VALUE
1910
+ context_resume(VALUE self)
1911
+ {
1912
+ debug_context_t *debug_context;
1913
+
1914
+ debug_check_started();
1915
+
1916
+ Data_Get_Struct(self, debug_context_t, debug_context);
1917
+ if(!CTX_FL_TEST(debug_context, CTX_FL_SUSPEND))
1918
+ rb_raise(rb_eRuntimeError, "Thread is not suspended.");
1919
+ context_resume_0(debug_context);
1920
+ return Qnil;
1921
+ }
1922
+
1923
+ /*
1924
+ * call-seq:
1925
+ * context.tracing -> bool
1926
+ *
1927
+ * Returns the tracing flag for the current context.
1928
+ */
1929
+ static VALUE
1930
+ context_tracing(VALUE self)
1931
+ {
1932
+ debug_context_t *debug_context;
1933
+
1934
+ debug_check_started();
1935
+
1936
+ Data_Get_Struct(self, debug_context_t, debug_context);
1937
+ return CTX_FL_TEST(debug_context, CTX_FL_TRACING) ? Qtrue : Qfalse;
1938
+ }
1939
+
1940
+ /*
1941
+ * call-seq:
1942
+ * context.tracking = bool
1943
+ *
1944
+ * Controls the tracing for this context.
1945
+ */
1946
+ static VALUE
1947
+ context_set_tracing(VALUE self, VALUE value)
1948
+ {
1949
+ debug_context_t *debug_context;
1950
+
1951
+ debug_check_started();
1952
+
1953
+ Data_Get_Struct(self, debug_context_t, debug_context);
1954
+ if(RTEST(value))
1955
+ CTX_FL_SET(debug_context, CTX_FL_TRACING);
1956
+ else
1957
+ CTX_FL_UNSET(debug_context, CTX_FL_TRACING);
1958
+ return value;
1959
+ }
1960
+
1961
+ /*
1962
+ * call-seq:
1963
+ * context.ignored? -> bool
1964
+ *
1965
+ * Returns the ignore flag for the current context.
1966
+ */
1967
+ static VALUE
1968
+ context_ignored(VALUE self)
1969
+ {
1970
+ debug_context_t *debug_context;
1971
+
1972
+ debug_check_started();
1973
+
1974
+ Data_Get_Struct(self, debug_context_t, debug_context);
1975
+ return CTX_FL_TEST(debug_context, CTX_FL_IGNORE) ? Qtrue : Qfalse;
1976
+ }
1977
+
1978
+ /*
1979
+ * call-seq:
1980
+ * context.dead? = bool
1981
+ *
1982
+ * Returns +true+ if context doesn't represent a live context and is created
1983
+ * during post-mortem exception handling.
1984
+ */
1985
+ static VALUE
1986
+ context_dead(VALUE self)
1987
+ {
1988
+ debug_context_t *debug_context;
1989
+
1990
+ debug_check_started();
1991
+
1992
+ Data_Get_Struct(self, debug_context_t, debug_context);
1993
+ return CTX_FL_TEST(debug_context, CTX_FL_DEAD) ? Qtrue : Qfalse;
1994
+ }
1995
+
1996
+ /*
1997
+ * call-seq:
1998
+ * breakpoint.source -> string
1999
+ *
2000
+ * Returns a source of the breakpoint.
2001
+ */
2002
+ static VALUE
2003
+ breakpoint_source(VALUE self)
2004
+ {
2005
+ debug_breakpoint_t *breakpoint;
2006
+
2007
+ Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
2008
+ return breakpoint->source;
2009
+ }
2010
+
2011
+ /*
2012
+ * call-seq:
2013
+ * breakpoint.pos -> string or int
2014
+ *
2015
+ * Returns a position of this breakpoint.
2016
+ */
2017
+ static VALUE
2018
+ breakpoint_pos(VALUE self)
2019
+ {
2020
+ debug_breakpoint_t *breakpoint;
2021
+
2022
+ Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
2023
+ if(breakpoint->type == BP_METHOD_TYPE)
2024
+ return rb_str_new2(rb_id2name(breakpoint->pos.mid));
2025
+ else
2026
+ return INT2FIX(breakpoint->pos.line);
2027
+ }
2028
+
2029
+ /*
2030
+ * call-seq:
2031
+ * breakpoint.expr -> string
2032
+ *
2033
+ * Returns a codition expression when this breakpoint should be activated.
2034
+ */
2035
+ static VALUE
2036
+ breakpoint_expr(VALUE self)
2037
+ {
2038
+ debug_breakpoint_t *breakpoint;
2039
+
2040
+ Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
2041
+ return breakpoint->expr;
2042
+ }
2043
+
2044
+ /*
2045
+ * call-seq:
2046
+ * breakpoint.id -> int
2047
+ *
2048
+ * Returns id of the breakpoint.
2049
+ */
2050
+ static VALUE
2051
+ breakpoint_id(VALUE self)
2052
+ {
2053
+ debug_breakpoint_t *breakpoint;
2054
+
2055
+ Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
2056
+ return INT2FIX(breakpoint->id);
2057
+ }
2058
+
2059
+ /*
2060
+ * Document-class: Context
2061
+ *
2062
+ * == Summary
2063
+ *
2064
+ * Debugger keeps a single instance of this class for each Ruby thread.
2065
+ */
2066
+ static void
2067
+ Init_context()
2068
+ {
2069
+ cContext = rb_define_class_under(mDebugger, "Context", rb_cObject);
2070
+ rb_define_method(cContext, "stop_next=", context_stop_next, 1);
2071
+ rb_define_method(cContext, "step_over", context_step_over, -1);
2072
+ rb_define_method(cContext, "stop_frame=", context_stop_frame, 1);
2073
+ rb_define_method(cContext, "thread", context_thread, 0);
2074
+ rb_define_method(cContext, "thnum", context_thnum, 0);
2075
+ rb_define_method(cContext, "suspend", context_suspend, 0);
2076
+ rb_define_method(cContext, "suspended?", context_is_suspended, 0);
2077
+ rb_define_method(cContext, "resume", context_resume, 0);
2078
+ rb_define_method(cContext, "tracing", context_tracing, 0);
2079
+ rb_define_method(cContext, "tracing=", context_set_tracing, 1);
2080
+ rb_define_method(cContext, "ignored?", context_ignored, 0);
2081
+ rb_define_method(cContext, "frame_binding", context_frame_binding, 1);
2082
+ rb_define_method(cContext, "frame_id", context_frame_id, 1);
2083
+ rb_define_method(cContext, "frame_line", context_frame_line, 1);
2084
+ rb_define_method(cContext, "frame_file", context_frame_file, 1);
2085
+ rb_define_method(cContext, "frame_locals", context_frame_locals, 1);
2086
+ rb_define_method(cContext, "frame_self", context_frame_self, 1);
2087
+ rb_define_method(cContext, "stack_size", context_stack_size, 0);
2088
+ rb_define_method(cContext, "dead?", context_dead, 0);
2089
+ }
2090
+
2091
+ /*
2092
+ * Document-class: Breakpoint
2093
+ *
2094
+ * == Summary
2095
+ *
2096
+ * This class represents a breakpoint. It defines position of the breakpoint and
2097
+ * condition when this breakpoint should be triggered.
2098
+ */
2099
+ static void
2100
+ Init_breakpoint()
2101
+ {
2102
+ cBreakpoint = rb_define_class_under(mDebugger, "Breakpoint", rb_cObject);
2103
+ rb_define_method(cBreakpoint, "source", breakpoint_source, 0);
2104
+ rb_define_method(cBreakpoint, "pos", breakpoint_pos, 0);
2105
+ rb_define_method(cBreakpoint, "expr", breakpoint_expr, 0);
2106
+ rb_define_method(cBreakpoint, "id", breakpoint_id, 0);
2107
+ }
2108
+
2109
+
2110
+ /*
2111
+ * Document-class: Debugger
2112
+ *
2113
+ * == Summary
2114
+ *
2115
+ * This is a singleton class allows controlling the debugger. Use it to start/stop debugger,
2116
+ * set/remove breakpoints, etc.
2117
+ */
2118
+ #if defined(_WIN32)
2119
+ __declspec(dllexport)
2120
+ #endif
2121
+ void
2122
+ Init_ruby_debug()
2123
+ {
2124
+ mDebugger = rb_define_module("Debugger");
2125
+ rb_define_const(mDebugger, "VERSION", rb_str_new2(DEBUG_VERSION));
2126
+ rb_define_module_function(mDebugger, "start", debug_start, 0);
2127
+ rb_define_module_function(mDebugger, "stop", debug_stop, 0);
2128
+ rb_define_module_function(mDebugger, "started?", debug_is_started, 0);
2129
+ rb_define_module_function(mDebugger, "breakpoints", debug_breakpoints, 0);
2130
+ rb_define_module_function(mDebugger, "add_breakpoint", debug_add_breakpoint, -1);
2131
+ rb_define_module_function(mDebugger, "remove_breakpoint", debug_remove_breakpoint, 1);
2132
+ rb_define_module_function(mDebugger, "catchpoint", debug_catchpoint, 0);
2133
+ rb_define_module_function(mDebugger, "catchpoint=", debug_set_catchpoint, 1);
2134
+ rb_define_module_function(mDebugger, "last_context", debug_last_interrupted, 0);
2135
+ rb_define_module_function(mDebugger, "contexts", debug_contexts, 0);
2136
+ rb_define_module_function(mDebugger, "current_context", debug_current_context, 0);
2137
+ rb_define_module_function(mDebugger, "thread_context", debug_thread_context, 1);
2138
+ rb_define_module_function(mDebugger, "suspend", debug_suspend, 0);
2139
+ rb_define_module_function(mDebugger, "resume", debug_resume, 0);
2140
+ rb_define_module_function(mDebugger, "tracing", debug_tracing, 0);
2141
+ rb_define_module_function(mDebugger, "tracing=", debug_set_tracing, 1);
2142
+ rb_define_module_function(mDebugger, "debug_load", debug_debug_load, -1);
2143
+ rb_define_module_function(mDebugger, "skip", debug_skip, 0);
2144
+ rb_define_module_function(mDebugger, "debug_at_exit", debug_at_exit, 0);
2145
+ rb_define_module_function(mDebugger, "post_mortem?", debug_post_mortem, 0);
2146
+ rb_define_module_function(mDebugger, "post_mortem=", debug_set_post_mortem, 1);
2147
+ rb_define_module_function(mDebugger, "keep_frame_binding?", debug_keep_frame_binding, 0);
2148
+ rb_define_module_function(mDebugger, "keep_frame_binding=", debug_set_keep_frame_binding, 1);
2149
+ rb_define_module_function(mDebugger, "debug", debug_debug, 0);
2150
+ rb_define_module_function(mDebugger, "debug=", debug_set_debug, 1);
2151
+
2152
+ cThreadsTable = rb_define_class_under(mDebugger, "ThreadsTable", rb_cObject);
2153
+
2154
+ cDebugThread = rb_define_class_under(mDebugger, "DebugThread", rb_cThread);
2155
+ rb_define_singleton_method(cDebugThread, "inherited", debug_thread_inherited, 1);
2156
+
2157
+ Init_context();
2158
+ Init_breakpoint();
2159
+
2160
+ idAtLine = rb_intern("at_line");
2161
+ idAtBreakpoint = rb_intern("at_breakpoint");
2162
+ idAtCatchpoint = rb_intern("at_catchpoint");
2163
+ idAtTracing = rb_intern("at_tracing");
2164
+ idEval = rb_intern("eval");
2165
+ idList = rb_intern("list");
2166
+
2167
+ rb_mObjectSpace = rb_const_get(rb_mKernel, rb_intern("ObjectSpace"));
2168
+
2169
+ rb_global_variable(&threads_tbl);
2170
+ rb_global_variable(&breakpoints);
2171
+ rb_global_variable(&catchpoint);
2172
+ rb_global_variable(&locker);
2173
+ rb_global_variable(&last_context);
2174
+ rb_global_variable(&last_thread);
2175
+ }