ruby-debug 0.1.5-mswin32

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGES 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
+ }