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 +16 -0
- data/LICENSE +23 -0
- data/README +63 -0
- data/Rakefile +102 -0
- data/bin/rdebug +13 -0
- data/ext/extconf.rb +18 -0
- data/ext/ruby_debug.c +991 -0
- data/lib/ruby-debug.rb +633 -0
- data/lib/ruby_debug.so +0 -0
- metadata +55 -0
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
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
|
+
}
|