ruby-debug 0.1.5-mswin32

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