ruby-debug-base 0.10.0 → 0.10.1
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 +94 -2
- data/README +30 -1
- data/Rakefile +55 -24
- data/ext/breakpoint.c +581 -0
- data/ext/ruby_debug.c +178 -754
- data/ext/ruby_debug.h +121 -0
- data/lib/ChangeLog +0 -579
- data/lib/ruby-debug-base.rb +67 -38
- data/test/{test-ruby-debug-base.rb → base/base.rb} +27 -29
- data/test/base/binding.rb +31 -0
- data/test/base/catchpoint.rb +26 -0
- metadata +23 -10
- data/ext/ChangeLog +0 -793
data/CHANGES
CHANGED
@@ -1,4 +1,96 @@
|
|
1
|
+
0.10.1
|
2
|
+
4/10/08 - in honor of the 30th Birthday of Kate Schwarz
|
3
|
+
|
4
|
+
- bin/rdebug
|
5
|
+
|
6
|
+
* "rdebug --post-mortem" now really catches uncaught exceptions and
|
7
|
+
brings you to post-mortem handling. "info program" shows the exception.
|
8
|
+
|
9
|
+
* rdebug now searches using ENV['PATH'] for a Ruby program to debug
|
10
|
+
if the program name is not found and doesn't have any path
|
11
|
+
characters in it.
|
12
|
+
|
13
|
+
* Use ~/.rdboptrc (rdbopt.ini on MS Windows) to change default options.
|
14
|
+
|
15
|
+
* --emacs is now --emacs-basic while --emacs 3 now implies emacs-basic
|
16
|
+
---annotate=3 --post-mortem --no-control --no-start --no-quit
|
17
|
+
|
18
|
+
- rdebug (CLI)
|
19
|
+
|
20
|
+
* "info" command additions and changes:
|
21
|
+
o fix bug in "info variables" when string had embedded %s'
|
22
|
+
o "info program" now shows uncaught exception information
|
23
|
+
o "info files" show what Ruby files are loaded
|
24
|
+
o "info file <f>" specific file information of <f> (e.g. time, # of lines, SHA1)
|
25
|
+
o "info catch" - Exceptions that can be caught in the current stack frame.
|
26
|
+
o "info "variables" shows "self" and class variables
|
27
|
+
o "info threads verbose" shows stack trace of all threads
|
28
|
+
o "info thread <t> verbose" shows stack trace of thread <t>.
|
29
|
+
|
30
|
+
* "frame" command now accepts an optional thread number argument
|
31
|
+
|
32
|
+
* Long information added to commands with subcommands: show, set,
|
33
|
+
info, enable, and disable. For example "help info <xxx>" will give
|
34
|
+
more detailed information about the "info <xxx>" command.
|
35
|
+
|
36
|
+
* columnize now pulled in from a separate package.
|
37
|
+
|
38
|
+
* add "var cl[ass]" command. Note "var const" can no longer be
|
39
|
+
abbreviated "var c"; use "var co" (or const or constant).
|
40
|
+
|
41
|
+
* add "condition" command. Allow removal of condition.
|
42
|
+
|
43
|
+
* $0 == __FILE__ when running rdebug should work -- most of the
|
44
|
+
time. See comments in code for a better solution.
|
45
|
+
|
46
|
+
* rdebug command history can be displayed with "show commands". Fix a bug
|
47
|
+
in history saving.
|
48
|
+
|
49
|
+
* INCOMPATIBLE CHANGE: "finish" works like gdb - stop just before the most
|
50
|
+
recent method finishes. Will now accept a number which stops that many
|
51
|
+
frames completed. (Note that return line numbers will be funny, the
|
52
|
+
first line of the method until Ruby 1.8.7.)
|
53
|
+
|
54
|
+
* fix bug in 'list' command when wrapping off the end.
|
55
|
+
|
56
|
+
- Emacs interaction drastically reworked, expanded, and improved.
|
57
|
+
|
58
|
+
- rdebug base
|
59
|
+
* allow catching multiple exceptions.
|
60
|
+
INCOMPATIBLE CHANGE: variable "Debugger.catchpoint", a String, was turned
|
61
|
+
into "Debugger.catchpoints", a Hash. Method "Debugger.catchpoint=" no
|
62
|
+
longer exists. Debugger.set_catchpoint was turned into
|
63
|
+
Debugger.add_catchpoint
|
64
|
+
|
65
|
+
* Add Debugger.last_exception which is set in post-mortem.
|
66
|
+
|
67
|
+
* remove Debugger.stop() when an exception is raised that would terminate the
|
68
|
+
debugged program. This may allow catchpoints to work and allow tracing user
|
69
|
+
code which handles "Exit" exceptions
|
70
|
+
|
71
|
+
* split off breakpoint code in ruby_debug.c.
|
72
|
+
|
73
|
+
* preface ruby_debug global Ruby variables with rdebug_.
|
74
|
+
|
75
|
+
* Change Debugger.start() to accept an optional options argument
|
76
|
+
:init => true saves things (like $0 and ARGV) necessary to
|
77
|
+
allow restart. Default: true
|
78
|
+
:post_mortem => true runs post-mortem on an uncaught exception
|
79
|
+
Default: false
|
80
|
+
|
81
|
+
The old Debugger.start() is now renamed to Debugger.start_()
|
82
|
+
|
83
|
+
* split of line caching to an external gem. We now only allow setting
|
84
|
+
breakpoints on lines where it makes sense to do so.
|
85
|
+
|
86
|
+
* Incompatible enhancement: even return/end will now call event handler
|
87
|
+
|
88
|
+
See ChangeLog for full details, and the reference guide for more complete
|
89
|
+
documentation of these changes.
|
90
|
+
|
1
91
|
0.10.0
|
92
|
+
12/25/07
|
93
|
+
|
2
94
|
- '-r' option can be used to require additional libraries.
|
3
95
|
- --noquit option added to stay in debugger when the program exits
|
4
96
|
- gdb-like --annotate option added. Can be used by front-ends to get information
|
@@ -27,7 +119,7 @@
|
|
27
119
|
- INCOMPATIBLE CHANGE: "script" command removed. Use "source" command instead
|
28
120
|
(same as gdb).
|
29
121
|
- Run .rdebugrc on Debugger.start. Look for a file in the current directory and
|
30
|
-
run that instead the one in $HOME if that exists. Again, inspired and compatible
|
122
|
+
run that instead of the one in $HOME if that exists. Again, inspired by and compatible
|
31
123
|
with gdb.
|
32
124
|
- Changes compatible with Ruby 1.9. NOTE: this debugger will NOT work with
|
33
125
|
Ruby 1.9
|
@@ -35,7 +127,7 @@
|
|
35
127
|
is also cleared when leaving irb
|
36
128
|
- help "foo" gives message "Undefined command "foo" rather than a list
|
37
129
|
of help commands. (Message test is gdb's)
|
38
|
-
- Add set linetrace+ - similar step+ for linetrace
|
130
|
+
- Add set linetrace+ - similar to step+ for linetrace
|
39
131
|
- Start unit tests.
|
40
132
|
- Start a reference guide.
|
41
133
|
|
data/README
CHANGED
@@ -14,6 +14,12 @@ are sometimes available), you'll need a C compiler and Ruby
|
|
14
14
|
development headers, and a Make program so the extension in
|
15
15
|
ruby-debug-base can be compiled when it is installed.
|
16
16
|
|
17
|
+
To install on Microsoft Windows, unless you run under cygwin or mingw
|
18
|
+
you'll need Microsoft Visual C++ 6.0 also known as VC6.
|
19
|
+
http://rubyforge.org/tracker/index.php?func=detail&aid=16774&group_id=1900&atid=7436
|
20
|
+
suggests why.
|
21
|
+
|
22
|
+
|
17
23
|
== Install
|
18
24
|
|
19
25
|
ruby-debug is provided as a RubyGem. To install:
|
@@ -22,6 +28,10 @@ ruby-debug is provided as a RubyGem. To install:
|
|
22
28
|
|
23
29
|
This should also pull in <tt>ruby-debug-base</tt> as a dependency.
|
24
30
|
|
31
|
+
(If you install ruby-debug-base explicitly, you can add in the secret
|
32
|
+
--test option after "install" to have the regression test run before
|
33
|
+
installing.)
|
34
|
+
|
25
35
|
For Emacs support and the Reference Manual, get
|
26
36
|
<tt>ruby-debug-extra</tt>. This is not a RubyGem, you'll need a Make
|
27
37
|
program and a POSIX shell. With this installed, run:
|
@@ -29,9 +39,28 @@ program and a POSIX shell. With this installed, run:
|
|
29
39
|
<pre>
|
30
40
|
sh ./configure
|
31
41
|
make
|
32
|
-
make
|
42
|
+
make test # optional, but a good idea
|
43
|
+
sudo make install
|
33
44
|
</pre>
|
34
45
|
|
46
|
+
==== Install on MS Windows
|
47
|
+
|
48
|
+
Compiling under cygwin or mingw works like it does on Unix.
|
49
|
+
|
50
|
+
* Have Microsoft Visual C++ 6.0 (VC6) installed - exactly that version.
|
51
|
+
|
52
|
+
* Set the appropriate environment variables.
|
53
|
+
|
54
|
+
* run `nmake'.
|
55
|
+
|
56
|
+
* Copy ruby_debug.so to `win32'.
|
57
|
+
|
58
|
+
* Go to the ruby_debug root.
|
59
|
+
|
60
|
+
* rake win32_gem
|
61
|
+
|
62
|
+
* The file is in named `rdebug-debug-base-0.10.0-mswin32.gem'.
|
63
|
+
|
35
64
|
== Usage
|
36
65
|
|
37
66
|
There are two ways of running ruby-debug.
|
data/Rakefile
CHANGED
@@ -8,7 +8,9 @@ require 'rake/testtask'
|
|
8
8
|
SO_NAME = "ruby_debug.so"
|
9
9
|
|
10
10
|
# ------- Default Package ----------
|
11
|
-
RUBY_DEBUG_VERSION = open("ext/ruby_debug.c")
|
11
|
+
RUBY_DEBUG_VERSION = open("ext/ruby_debug.c") do |f|
|
12
|
+
f.grep(/^#define DEBUG_VERSION/).first[/"(.+)"/,1]
|
13
|
+
end
|
12
14
|
|
13
15
|
COMMON_FILES = FileList[
|
14
16
|
'AUTHORS',
|
@@ -18,35 +20,35 @@ COMMON_FILES = FileList[
|
|
18
20
|
'Rakefile',
|
19
21
|
]
|
20
22
|
|
21
|
-
CLI_TEST_FILE_LIST = 'test
|
23
|
+
CLI_TEST_FILE_LIST = 'test/test-*.rb'
|
22
24
|
CLI_FILES = COMMON_FILES + FileList[
|
23
25
|
"cli/**/*",
|
24
26
|
'ChangeLog',
|
25
27
|
'bin/*',
|
26
28
|
'doc/rdebug.1',
|
27
|
-
'test
|
28
|
-
'test
|
29
|
-
'test
|
30
|
-
'
|
31
|
-
'test/**/info-var-bug.rb',
|
32
|
-
'test/**/tdebug.rb',
|
33
|
-
'test/**/test-*.cmd',
|
34
|
-
'runner.sh',
|
29
|
+
'test/**/data/*.cmd',
|
30
|
+
'test/**/data/*.right',
|
31
|
+
'test/**/*.rb',
|
32
|
+
'rdbg.rb',
|
35
33
|
CLI_TEST_FILE_LIST,
|
36
34
|
]
|
37
35
|
|
38
|
-
BASE_TEST_FILE_LIST =
|
36
|
+
BASE_TEST_FILE_LIST = %w(
|
37
|
+
test/base/base.rb
|
38
|
+
test/base/binding.rb
|
39
|
+
test/base/catchpoint.rb)
|
39
40
|
BASE_FILES = COMMON_FILES + FileList[
|
40
|
-
'
|
41
|
-
'ext/ChangeLog',
|
42
|
-
'ext/ruby_debug.c',
|
41
|
+
'ext/breakpoint.c',
|
43
42
|
'ext/extconf.rb',
|
43
|
+
'ext/ruby_debug.c',
|
44
|
+
'ext/ruby_debug.h',
|
44
45
|
'ext/win32/*',
|
46
|
+
'lib/**/*',
|
45
47
|
BASE_TEST_FILE_LIST,
|
46
48
|
]
|
47
49
|
|
48
50
|
desc "Test everything."
|
49
|
-
test_task = task :test => :lib do
|
51
|
+
test_task = task :test => [:lib, :test_base] do
|
50
52
|
Rake::TestTask.new(:test) do |t|
|
51
53
|
t.libs << ['./ext', './lib', './cli']
|
52
54
|
t.pattern = CLI_TEST_FILE_LIST
|
@@ -54,6 +56,15 @@ test_task = task :test => :lib do
|
|
54
56
|
end
|
55
57
|
end
|
56
58
|
|
59
|
+
desc "Test ruby-debug-base."
|
60
|
+
test_task = task :test_base => :lib do
|
61
|
+
Rake::TestTask.new(:test_base) do |t|
|
62
|
+
t.libs << ['./ext', './lib']
|
63
|
+
t.test_files = FileList[BASE_TEST_FILE_LIST]
|
64
|
+
t.verbose = true
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
57
68
|
desc "Test everything - same as test."
|
58
69
|
task :check => :test
|
59
70
|
|
@@ -99,12 +110,12 @@ EOF
|
|
99
110
|
spec.platform = Gem::Platform::RUBY
|
100
111
|
spec.require_path = "lib"
|
101
112
|
spec.extensions = ["ext/extconf.rb"]
|
102
|
-
spec.autorequire = "ruby-debug-base"
|
103
113
|
spec.files = BASE_FILES.to_a
|
104
114
|
|
105
115
|
spec.required_ruby_version = '>= 1.8.2'
|
106
116
|
spec.date = Time.now
|
107
117
|
spec.rubyforge_project = 'ruby-debug'
|
118
|
+
spec.add_dependency('linecache', '>= 0.3')
|
108
119
|
|
109
120
|
spec.test_files = FileList[BASE_TEST_FILE_LIST]
|
110
121
|
|
@@ -130,12 +141,12 @@ EOF
|
|
130
141
|
spec.require_path = "cli"
|
131
142
|
spec.bindir = "bin"
|
132
143
|
spec.executables = ["rdebug"]
|
133
|
-
spec.autorequire = "ruby-debug"
|
134
144
|
spec.files = CLI_FILES.to_a
|
135
145
|
|
136
146
|
spec.required_ruby_version = '>= 1.8.2'
|
137
147
|
spec.date = Time.now
|
138
148
|
spec.rubyforge_project = 'ruby-debug'
|
149
|
+
spec.add_dependency('columnize', '>= 0.1')
|
139
150
|
spec.add_dependency('ruby-debug-base', RUBY_DEBUG_VERSION)
|
140
151
|
|
141
152
|
# FIXME: work out operational logistics for this
|
@@ -159,7 +170,8 @@ task :default => [:package]
|
|
159
170
|
# Windows specification
|
160
171
|
win_spec = base_spec.clone
|
161
172
|
win_spec.extensions = []
|
162
|
-
win_spec.platform = Gem::Platform::WIN32
|
173
|
+
## win_spec.platform = Gem::Platform::WIN32 # deprecated
|
174
|
+
win_spec.platform = 'mswin32'
|
163
175
|
win_spec.files += ["lib/#{SO_NAME}"]
|
164
176
|
|
165
177
|
desc "Create Windows Gem"
|
@@ -170,20 +182,20 @@ task :win32_gem do
|
|
170
182
|
target = File.join(current_dir, "lib", SO_NAME)
|
171
183
|
cp(source, target)
|
172
184
|
|
173
|
-
# Create the gem, then move it to pkg
|
174
|
-
|
175
|
-
|
185
|
+
# Create the gem, then move it to pkg.
|
186
|
+
Gem::Builder.new(win_spec).build
|
187
|
+
gem_file = "#{win_spec.name}-#{win_spec.version}-#{win_spec.platform}.gem"
|
176
188
|
mv(gem_file, "pkg/#{gem_file}")
|
177
189
|
|
178
|
-
# Remove win extension
|
179
|
-
|
190
|
+
# Remove win extension from top level directory.
|
191
|
+
rm(target)
|
180
192
|
end
|
181
193
|
|
182
194
|
desc "Publish ruby-debug to RubyForge."
|
183
195
|
task :publish do
|
184
196
|
require 'rake/contrib/sshpublisher'
|
185
197
|
|
186
|
-
# Get ruby-debug path
|
198
|
+
# Get ruby-debug path.
|
187
199
|
ruby_debug_path = File.expand_path(File.dirname(__FILE__))
|
188
200
|
|
189
201
|
publisher = Rake::SshDirPublisher.new("kent@rubyforge.org",
|
@@ -218,3 +230,22 @@ Rake::RDocTask.new("rdoc") do |rdoc|
|
|
218
230
|
'LICENSE')
|
219
231
|
end
|
220
232
|
|
233
|
+
desc "Publish the release files to RubyForge."
|
234
|
+
task :rubyforge_upload do
|
235
|
+
`rubyforge login`
|
236
|
+
release_command = "rubyforge add_release #{PKG_NAME} #{PKG_NAME} '#{PKG_NAME}-#{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.gem"
|
237
|
+
puts release_command
|
238
|
+
system(release_command)
|
239
|
+
end
|
240
|
+
|
241
|
+
PKG_NAME = 'ruby-debug'
|
242
|
+
desc "Publish the release files to RubyForge."
|
243
|
+
task :rubyforge_upload do
|
244
|
+
`rubyforge login`
|
245
|
+
for pkg_name in ['ruby-debug', 'ruby-debug-base'] do
|
246
|
+
pkg_file_name = "#{pkg_name}-#{pkg_version}"
|
247
|
+
release_command = "rubyforge add_release ruby-debug #{pkg_name} '#{pkg_file_name}' pkg/#{pkg_file_name}.gem"
|
248
|
+
puts release_command
|
249
|
+
system(release_command)
|
250
|
+
end
|
251
|
+
end
|
data/ext/breakpoint.c
ADDED
@@ -0,0 +1,581 @@
|
|
1
|
+
#include "ruby_debug.h"
|
2
|
+
|
3
|
+
VALUE rdebug_breakpoints = Qnil;
|
4
|
+
VALUE rdebug_catchpoints = Qnil;
|
5
|
+
|
6
|
+
static VALUE cBreakpoint;
|
7
|
+
static ID idEval;
|
8
|
+
|
9
|
+
static VALUE
|
10
|
+
eval_expression(VALUE args)
|
11
|
+
{
|
12
|
+
return rb_funcall2(rb_mKernel, idEval, 2, RARRAY(args)->ptr);
|
13
|
+
}
|
14
|
+
|
15
|
+
int
|
16
|
+
check_breakpoint_hit_condition(VALUE breakpoint)
|
17
|
+
{
|
18
|
+
debug_breakpoint_t *debug_breakpoint;
|
19
|
+
|
20
|
+
if(breakpoint == Qnil)
|
21
|
+
return 0;
|
22
|
+
Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
|
23
|
+
|
24
|
+
debug_breakpoint->hit_count++;
|
25
|
+
if (!Qtrue == debug_breakpoint->enabled) return 0;
|
26
|
+
switch(debug_breakpoint->hit_condition)
|
27
|
+
{
|
28
|
+
case HIT_COND_NONE:
|
29
|
+
return 1;
|
30
|
+
case HIT_COND_GE:
|
31
|
+
{
|
32
|
+
if(debug_breakpoint->hit_count >= debug_breakpoint->hit_value)
|
33
|
+
return 1;
|
34
|
+
break;
|
35
|
+
}
|
36
|
+
case HIT_COND_EQ:
|
37
|
+
{
|
38
|
+
if(debug_breakpoint->hit_count == debug_breakpoint->hit_value)
|
39
|
+
return 1;
|
40
|
+
break;
|
41
|
+
}
|
42
|
+
case HIT_COND_MOD:
|
43
|
+
{
|
44
|
+
if(debug_breakpoint->hit_count % debug_breakpoint->hit_value == 0)
|
45
|
+
return 1;
|
46
|
+
break;
|
47
|
+
}
|
48
|
+
}
|
49
|
+
return 0;
|
50
|
+
}
|
51
|
+
|
52
|
+
static int
|
53
|
+
check_breakpoint_by_pos(VALUE breakpoint, char *file, int line)
|
54
|
+
{
|
55
|
+
debug_breakpoint_t *debug_breakpoint;
|
56
|
+
|
57
|
+
if(breakpoint == Qnil)
|
58
|
+
return 0;
|
59
|
+
Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
|
60
|
+
if (!Qtrue == debug_breakpoint->enabled) return 0;
|
61
|
+
if(debug_breakpoint->type != BP_POS_TYPE)
|
62
|
+
return 0;
|
63
|
+
if(debug_breakpoint->pos.line != line)
|
64
|
+
return 0;
|
65
|
+
if(filename_cmp(debug_breakpoint->source, file))
|
66
|
+
return 1;
|
67
|
+
return 0;
|
68
|
+
}
|
69
|
+
|
70
|
+
static int
|
71
|
+
check_breakpoint_by_method(VALUE breakpoint, VALUE klass, ID mid)
|
72
|
+
{
|
73
|
+
debug_breakpoint_t *debug_breakpoint;
|
74
|
+
|
75
|
+
if(breakpoint == Qnil)
|
76
|
+
return 0;
|
77
|
+
Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
|
78
|
+
if (!Qtrue == debug_breakpoint->enabled) return 0;
|
79
|
+
if(debug_breakpoint->type != BP_METHOD_TYPE)
|
80
|
+
return 0;
|
81
|
+
if(debug_breakpoint->pos.mid != mid)
|
82
|
+
return 0;
|
83
|
+
if(classname_cmp(debug_breakpoint->source, klass))
|
84
|
+
return 1;
|
85
|
+
return 0;
|
86
|
+
}
|
87
|
+
|
88
|
+
VALUE
|
89
|
+
check_breakpoints_by_pos(debug_context_t *debug_context, char *file, int line)
|
90
|
+
{
|
91
|
+
VALUE breakpoint;
|
92
|
+
int i;
|
93
|
+
|
94
|
+
if(!CTX_FL_TEST(debug_context, CTX_FL_ENABLE_BKPT))
|
95
|
+
return Qnil;
|
96
|
+
|
97
|
+
if(check_breakpoint_by_pos(debug_context->breakpoint, file, line))
|
98
|
+
return debug_context->breakpoint;
|
99
|
+
|
100
|
+
if(RARRAY(rdebug_breakpoints)->len == 0)
|
101
|
+
return Qnil;
|
102
|
+
for(i = 0; i < RARRAY(rdebug_breakpoints)->len; i++)
|
103
|
+
{
|
104
|
+
breakpoint = rb_ary_entry(rdebug_breakpoints, i);
|
105
|
+
if(check_breakpoint_by_pos(breakpoint, file, line))
|
106
|
+
return breakpoint;
|
107
|
+
}
|
108
|
+
return Qnil;
|
109
|
+
}
|
110
|
+
|
111
|
+
VALUE
|
112
|
+
check_breakpoints_by_method(debug_context_t *debug_context, VALUE klass, ID mid)
|
113
|
+
{
|
114
|
+
VALUE breakpoint;
|
115
|
+
int i;
|
116
|
+
|
117
|
+
if(!CTX_FL_TEST(debug_context, CTX_FL_ENABLE_BKPT))
|
118
|
+
return Qnil;
|
119
|
+
|
120
|
+
if(check_breakpoint_by_method(debug_context->breakpoint, klass, mid))
|
121
|
+
return debug_context->breakpoint;
|
122
|
+
|
123
|
+
if(RARRAY(rdebug_breakpoints)->len == 0)
|
124
|
+
return Qnil;
|
125
|
+
for(i = 0; i < RARRAY(rdebug_breakpoints)->len; i++)
|
126
|
+
{
|
127
|
+
breakpoint = rb_ary_entry(rdebug_breakpoints, i);
|
128
|
+
if(check_breakpoint_by_method(breakpoint, klass, mid))
|
129
|
+
return breakpoint;
|
130
|
+
}
|
131
|
+
return Qnil;
|
132
|
+
}
|
133
|
+
|
134
|
+
int
|
135
|
+
check_breakpoint_expression(VALUE breakpoint, VALUE binding)
|
136
|
+
{
|
137
|
+
debug_breakpoint_t *debug_breakpoint;
|
138
|
+
VALUE args, expr_result;
|
139
|
+
|
140
|
+
Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
|
141
|
+
if(NIL_P(debug_breakpoint->expr))
|
142
|
+
return 1;
|
143
|
+
|
144
|
+
args = rb_ary_new3(2, debug_breakpoint->expr, binding);
|
145
|
+
expr_result = rb_protect(eval_expression, args, 0);
|
146
|
+
return RTEST(expr_result);
|
147
|
+
}
|
148
|
+
|
149
|
+
static void
|
150
|
+
breakpoint_mark(void *data)
|
151
|
+
{
|
152
|
+
debug_breakpoint_t *breakpoint;
|
153
|
+
breakpoint = (debug_breakpoint_t *)data;
|
154
|
+
rb_gc_mark(breakpoint->source);
|
155
|
+
rb_gc_mark(breakpoint->expr);
|
156
|
+
}
|
157
|
+
|
158
|
+
VALUE
|
159
|
+
create_breakpoint_from_args(int argc, VALUE *argv, int id)
|
160
|
+
{
|
161
|
+
VALUE source, pos, expr;
|
162
|
+
debug_breakpoint_t *breakpoint;
|
163
|
+
int type;
|
164
|
+
|
165
|
+
if(rb_scan_args(argc, argv, "21", &source, &pos, &expr) == 2)
|
166
|
+
{
|
167
|
+
expr = Qnil;
|
168
|
+
}
|
169
|
+
type = FIXNUM_P(pos) ? BP_POS_TYPE : BP_METHOD_TYPE;
|
170
|
+
if(type == BP_POS_TYPE)
|
171
|
+
source = StringValue(source);
|
172
|
+
else
|
173
|
+
pos = StringValue(pos);
|
174
|
+
breakpoint = ALLOC(debug_breakpoint_t);
|
175
|
+
breakpoint->id = id;
|
176
|
+
breakpoint->source = source;
|
177
|
+
breakpoint->type = type;
|
178
|
+
if(type == BP_POS_TYPE)
|
179
|
+
breakpoint->pos.line = FIX2INT(pos);
|
180
|
+
else
|
181
|
+
breakpoint->pos.mid = rb_intern(RSTRING(pos)->ptr);
|
182
|
+
breakpoint->enabled = Qtrue;
|
183
|
+
breakpoint->expr = NIL_P(expr) ? expr: StringValue(expr);
|
184
|
+
breakpoint->hit_count = 0;
|
185
|
+
breakpoint->hit_value = 0;
|
186
|
+
breakpoint->hit_condition = HIT_COND_NONE;
|
187
|
+
return Data_Wrap_Struct(cBreakpoint, breakpoint_mark, xfree, breakpoint);
|
188
|
+
}
|
189
|
+
|
190
|
+
/*
|
191
|
+
* call-seq:
|
192
|
+
* Debugger.remove_breakpoint(id) -> breakpoint
|
193
|
+
*
|
194
|
+
* Removes breakpoint by its id.
|
195
|
+
* <i>id</i> is an identificator of a breakpoint.
|
196
|
+
*/
|
197
|
+
VALUE
|
198
|
+
rdebug_remove_breakpoint(VALUE self, VALUE id_value)
|
199
|
+
{
|
200
|
+
int i;
|
201
|
+
int id;
|
202
|
+
VALUE breakpoint;
|
203
|
+
debug_breakpoint_t *debug_breakpoint;
|
204
|
+
|
205
|
+
id = FIX2INT(id_value);
|
206
|
+
|
207
|
+
for( i = 0; i < RARRAY(rdebug_breakpoints)->len; i += 1 )
|
208
|
+
{
|
209
|
+
breakpoint = rb_ary_entry(rdebug_breakpoints, i);
|
210
|
+
Data_Get_Struct(breakpoint, debug_breakpoint_t, debug_breakpoint);
|
211
|
+
if(debug_breakpoint->id == id)
|
212
|
+
{
|
213
|
+
rb_ary_delete_at(rdebug_breakpoints, i);
|
214
|
+
return breakpoint;
|
215
|
+
}
|
216
|
+
}
|
217
|
+
return Qnil;
|
218
|
+
}
|
219
|
+
|
220
|
+
/*
|
221
|
+
* call-seq:
|
222
|
+
* Debugger.catchpoints -> string
|
223
|
+
*
|
224
|
+
* Returns a current catchpoints, which is a hash exception names that will
|
225
|
+
* trigger a debugger when raised. The values are the number of times taht
|
226
|
+
* catchpoint was hit, initially 0.
|
227
|
+
*/
|
228
|
+
VALUE
|
229
|
+
debug_catchpoints(VALUE self)
|
230
|
+
{
|
231
|
+
debug_check_started();
|
232
|
+
|
233
|
+
return rdebug_catchpoints;
|
234
|
+
}
|
235
|
+
|
236
|
+
/*
|
237
|
+
* call-seq:
|
238
|
+
* Debugger.checkpoint = string -> string
|
239
|
+
*
|
240
|
+
* Sets catchpoint.
|
241
|
+
*/
|
242
|
+
VALUE
|
243
|
+
rdebug_add_catchpoint(VALUE self, VALUE value)
|
244
|
+
{
|
245
|
+
debug_check_started();
|
246
|
+
|
247
|
+
if (!NIL_P(value) && TYPE(value) != T_STRING) {
|
248
|
+
rb_raise(rb_eTypeError, "value of chatchpoint must be String");
|
249
|
+
}
|
250
|
+
if(NIL_P(value))
|
251
|
+
rdebug_catchpoints = Qnil;
|
252
|
+
else
|
253
|
+
rb_hash_aset(rdebug_catchpoints, rb_str_dup(value),
|
254
|
+
INT2FIX(0));
|
255
|
+
return value;
|
256
|
+
}
|
257
|
+
|
258
|
+
/*
|
259
|
+
* call-seq:
|
260
|
+
* context.breakpoint -> breakpoint
|
261
|
+
*
|
262
|
+
* Returns a context-specific temporary Breakpoint object.
|
263
|
+
*/
|
264
|
+
VALUE
|
265
|
+
context_breakpoint(VALUE self)
|
266
|
+
{
|
267
|
+
debug_context_t *debug_context;
|
268
|
+
|
269
|
+
debug_check_started();
|
270
|
+
|
271
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
272
|
+
return debug_context->breakpoint;
|
273
|
+
}
|
274
|
+
|
275
|
+
/*
|
276
|
+
* call-seq:
|
277
|
+
* context.set_breakpoint(source, pos, condition = nil) -> breakpoint
|
278
|
+
*
|
279
|
+
* Sets a context-specific temporary breakpoint, which can be used to implement
|
280
|
+
* 'Run to Cursor' debugger function. When this breakpoint is reached, it will be
|
281
|
+
* cleared out.
|
282
|
+
*
|
283
|
+
* <i>source</i> is a name of a file or a class.
|
284
|
+
* <i>pos</i> is a line number or a method name if <i>source</i> is a class name.
|
285
|
+
* <i>condition</i> is a string which is evaluated to +true+ when this breakpoint
|
286
|
+
* is activated.
|
287
|
+
*/
|
288
|
+
VALUE
|
289
|
+
context_set_breakpoint(int argc, VALUE *argv, VALUE self)
|
290
|
+
{
|
291
|
+
VALUE result;
|
292
|
+
debug_context_t *debug_context;
|
293
|
+
|
294
|
+
debug_check_started();
|
295
|
+
|
296
|
+
Data_Get_Struct(self, debug_context_t, debug_context);
|
297
|
+
result = create_breakpoint_from_args(argc, argv, 0);
|
298
|
+
debug_context->breakpoint = result;
|
299
|
+
return result;
|
300
|
+
}
|
301
|
+
|
302
|
+
/*
|
303
|
+
* call-seq:
|
304
|
+
* breakpoint.enabled?
|
305
|
+
*
|
306
|
+
* Returns whether breakpoint is enabled or not.
|
307
|
+
*/
|
308
|
+
static VALUE
|
309
|
+
breakpoint_enabled(VALUE self)
|
310
|
+
{
|
311
|
+
debug_breakpoint_t *breakpoint;
|
312
|
+
|
313
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
314
|
+
return breakpoint->enabled;
|
315
|
+
}
|
316
|
+
|
317
|
+
/*
|
318
|
+
* call-seq:
|
319
|
+
* breakpoint.enabled = bool
|
320
|
+
*
|
321
|
+
* Enables or disables breakpoint.
|
322
|
+
*/
|
323
|
+
static VALUE
|
324
|
+
breakpoint_set_enabled(VALUE self, VALUE bool)
|
325
|
+
{
|
326
|
+
debug_breakpoint_t *breakpoint;
|
327
|
+
|
328
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
329
|
+
return breakpoint->enabled = bool;
|
330
|
+
}
|
331
|
+
|
332
|
+
/*
|
333
|
+
* call-seq:
|
334
|
+
* breakpoint.source -> string
|
335
|
+
*
|
336
|
+
* Returns a source of the breakpoint.
|
337
|
+
*/
|
338
|
+
static VALUE
|
339
|
+
breakpoint_source(VALUE self)
|
340
|
+
{
|
341
|
+
debug_breakpoint_t *breakpoint;
|
342
|
+
|
343
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
344
|
+
return breakpoint->source;
|
345
|
+
}
|
346
|
+
|
347
|
+
/*
|
348
|
+
* call-seq:
|
349
|
+
* breakpoint.source = string
|
350
|
+
*
|
351
|
+
* Sets the source of the breakpoint.
|
352
|
+
*/
|
353
|
+
static VALUE
|
354
|
+
breakpoint_set_source(VALUE self, VALUE value)
|
355
|
+
{
|
356
|
+
debug_breakpoint_t *breakpoint;
|
357
|
+
|
358
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
359
|
+
breakpoint->source = StringValue(value);
|
360
|
+
return value;
|
361
|
+
}
|
362
|
+
|
363
|
+
/*
|
364
|
+
* call-seq:
|
365
|
+
* breakpoint.pos -> string or int
|
366
|
+
*
|
367
|
+
* Returns the position of this breakpoint.
|
368
|
+
*/
|
369
|
+
static VALUE
|
370
|
+
breakpoint_pos(VALUE self)
|
371
|
+
{
|
372
|
+
debug_breakpoint_t *breakpoint;
|
373
|
+
|
374
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
375
|
+
if(breakpoint->type == BP_METHOD_TYPE)
|
376
|
+
return rb_str_new2(rb_id2name(breakpoint->pos.mid));
|
377
|
+
else
|
378
|
+
return INT2FIX(breakpoint->pos.line);
|
379
|
+
}
|
380
|
+
|
381
|
+
/*
|
382
|
+
* call-seq:
|
383
|
+
* breakpoint.pos = string or int
|
384
|
+
*
|
385
|
+
* Sets the position of this breakpoint.
|
386
|
+
*/
|
387
|
+
static VALUE
|
388
|
+
breakpoint_set_pos(VALUE self, VALUE value)
|
389
|
+
{
|
390
|
+
debug_breakpoint_t *breakpoint;
|
391
|
+
|
392
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
393
|
+
if(breakpoint->type == BP_METHOD_TYPE)
|
394
|
+
{
|
395
|
+
breakpoint->pos.mid = rb_to_id(StringValue(value));
|
396
|
+
}
|
397
|
+
else
|
398
|
+
breakpoint->pos.line = FIX2INT(value);
|
399
|
+
return value;
|
400
|
+
}
|
401
|
+
|
402
|
+
/*
|
403
|
+
* call-seq:
|
404
|
+
* breakpoint.expr -> string
|
405
|
+
*
|
406
|
+
* Returns a codition expression when this breakpoint should be activated.
|
407
|
+
*/
|
408
|
+
static VALUE
|
409
|
+
breakpoint_expr(VALUE self)
|
410
|
+
{
|
411
|
+
debug_breakpoint_t *breakpoint;
|
412
|
+
|
413
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
414
|
+
return breakpoint->expr;
|
415
|
+
}
|
416
|
+
|
417
|
+
/*
|
418
|
+
* call-seq:
|
419
|
+
* breakpoint.expr = string | nil
|
420
|
+
*
|
421
|
+
* Sets the codition expression when this breakpoint should be activated.
|
422
|
+
*/
|
423
|
+
static VALUE
|
424
|
+
breakpoint_set_expr(VALUE self, VALUE expr)
|
425
|
+
{
|
426
|
+
debug_breakpoint_t *breakpoint;
|
427
|
+
|
428
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
429
|
+
breakpoint->expr = NIL_P(expr) ? expr: StringValue(expr);
|
430
|
+
return expr;
|
431
|
+
}
|
432
|
+
|
433
|
+
/*
|
434
|
+
* call-seq:
|
435
|
+
* breakpoint.id -> int
|
436
|
+
*
|
437
|
+
* Returns id of the breakpoint.
|
438
|
+
*/
|
439
|
+
static VALUE
|
440
|
+
breakpoint_id(VALUE self)
|
441
|
+
{
|
442
|
+
debug_breakpoint_t *breakpoint;
|
443
|
+
|
444
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
445
|
+
return INT2FIX(breakpoint->id);
|
446
|
+
}
|
447
|
+
|
448
|
+
/*
|
449
|
+
* call-seq:
|
450
|
+
* breakpoint.hit_count -> int
|
451
|
+
*
|
452
|
+
* Returns the hit count of the breakpoint.
|
453
|
+
*/
|
454
|
+
static VALUE
|
455
|
+
breakpoint_hit_count(VALUE self)
|
456
|
+
{
|
457
|
+
debug_breakpoint_t *breakpoint;
|
458
|
+
|
459
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
460
|
+
return INT2FIX(breakpoint->hit_count);
|
461
|
+
}
|
462
|
+
|
463
|
+
/*
|
464
|
+
* call-seq:
|
465
|
+
* breakpoint.hit_value -> int
|
466
|
+
*
|
467
|
+
* Returns the hit value of the breakpoint.
|
468
|
+
*/
|
469
|
+
static VALUE
|
470
|
+
breakpoint_hit_value(VALUE self)
|
471
|
+
{
|
472
|
+
debug_breakpoint_t *breakpoint;
|
473
|
+
|
474
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
475
|
+
return INT2FIX(breakpoint->hit_value);
|
476
|
+
}
|
477
|
+
|
478
|
+
/*
|
479
|
+
* call-seq:
|
480
|
+
* breakpoint.hit_value = int
|
481
|
+
*
|
482
|
+
* Sets the hit value of the breakpoint.
|
483
|
+
*/
|
484
|
+
static VALUE
|
485
|
+
breakpoint_set_hit_value(VALUE self, VALUE value)
|
486
|
+
{
|
487
|
+
debug_breakpoint_t *breakpoint;
|
488
|
+
|
489
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
490
|
+
breakpoint->hit_value = FIX2INT(value);
|
491
|
+
return value;
|
492
|
+
}
|
493
|
+
|
494
|
+
/*
|
495
|
+
* call-seq:
|
496
|
+
* breakpoint.hit_condition -> symbol
|
497
|
+
*
|
498
|
+
* Returns the hit condition of the breakpoint:
|
499
|
+
*
|
500
|
+
* +nil+ if it is an unconditional breakpoint, or
|
501
|
+
* :greater_or_equal, :equal, :modulo
|
502
|
+
*/
|
503
|
+
static VALUE
|
504
|
+
breakpoint_hit_condition(VALUE self)
|
505
|
+
{
|
506
|
+
debug_breakpoint_t *breakpoint;
|
507
|
+
|
508
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
509
|
+
switch(breakpoint->hit_condition)
|
510
|
+
{
|
511
|
+
case HIT_COND_GE:
|
512
|
+
return ID2SYM(rb_intern("greater_or_equal"));
|
513
|
+
case HIT_COND_EQ:
|
514
|
+
return ID2SYM(rb_intern("equal"));
|
515
|
+
case HIT_COND_MOD:
|
516
|
+
return ID2SYM(rb_intern("modulo"));
|
517
|
+
case HIT_COND_NONE:
|
518
|
+
default:
|
519
|
+
return Qnil;
|
520
|
+
}
|
521
|
+
}
|
522
|
+
|
523
|
+
/*
|
524
|
+
* call-seq:
|
525
|
+
* breakpoint.hit_condition = symbol
|
526
|
+
*
|
527
|
+
* Sets the hit condition of the breakpoint which must be one of the following values:
|
528
|
+
*
|
529
|
+
* +nil+ if it is an unconditional breakpoint, or
|
530
|
+
* :greater_or_equal(:ge), :equal(:eq), :modulo(:mod)
|
531
|
+
*/
|
532
|
+
static VALUE
|
533
|
+
breakpoint_set_hit_condition(VALUE self, VALUE value)
|
534
|
+
{
|
535
|
+
debug_breakpoint_t *breakpoint;
|
536
|
+
ID id_value;
|
537
|
+
|
538
|
+
Data_Get_Struct(self, debug_breakpoint_t, breakpoint);
|
539
|
+
id_value = rb_to_id(value);
|
540
|
+
|
541
|
+
if(rb_intern("greater_or_equal") == id_value || rb_intern("ge") == id_value)
|
542
|
+
breakpoint->hit_condition = HIT_COND_GE;
|
543
|
+
else if(rb_intern("equal") == id_value || rb_intern("eq") == id_value)
|
544
|
+
breakpoint->hit_condition = HIT_COND_EQ;
|
545
|
+
else if(rb_intern("modulo") == id_value || rb_intern("mod") == id_value)
|
546
|
+
breakpoint->hit_condition = HIT_COND_MOD;
|
547
|
+
else
|
548
|
+
rb_raise(rb_eArgError, "Invalid condition parameter");
|
549
|
+
return value;
|
550
|
+
}
|
551
|
+
|
552
|
+
/*
|
553
|
+
* Document-class: Breakpoint
|
554
|
+
*
|
555
|
+
* == Summary
|
556
|
+
*
|
557
|
+
* This class represents a breakpoint. It defines position of the breakpoint and
|
558
|
+
* condition when this breakpoint should be triggered.
|
559
|
+
*/
|
560
|
+
void
|
561
|
+
Init_breakpoint()
|
562
|
+
{
|
563
|
+
cBreakpoint = rb_define_class_under(mDebugger, "Breakpoint", rb_cObject);
|
564
|
+
rb_define_method(cBreakpoint, "enabled=", breakpoint_set_enabled, 1);
|
565
|
+
rb_define_method(cBreakpoint, "enabled?", breakpoint_enabled, 0);
|
566
|
+
rb_define_method(cBreakpoint, "expr", breakpoint_expr, 0);
|
567
|
+
rb_define_method(cBreakpoint, "expr=", breakpoint_set_expr, 1);
|
568
|
+
rb_define_method(cBreakpoint, "hit_condition", breakpoint_hit_condition, 0);
|
569
|
+
rb_define_method(cBreakpoint, "hit_condition=", breakpoint_set_hit_condition, 1);
|
570
|
+
rb_define_method(cBreakpoint, "hit_count", breakpoint_hit_count, 0);
|
571
|
+
rb_define_method(cBreakpoint, "hit_value", breakpoint_hit_value, 0);
|
572
|
+
rb_define_method(cBreakpoint, "hit_value=", breakpoint_set_hit_value, 1);
|
573
|
+
rb_define_method(cBreakpoint, "id", breakpoint_id, 0);
|
574
|
+
rb_define_method(cBreakpoint, "pos", breakpoint_pos, 0);
|
575
|
+
rb_define_method(cBreakpoint, "pos=", breakpoint_set_pos, 1);
|
576
|
+
rb_define_method(cBreakpoint, "source", breakpoint_source, 0);
|
577
|
+
rb_define_method(cBreakpoint, "source=", breakpoint_set_source, 1);
|
578
|
+
idEval = rb_intern("eval");
|
579
|
+
}
|
580
|
+
|
581
|
+
|