ruby-debug 0.10.2 → 0.10.3
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 +13 -0
- data/ChangeLog +307 -0
- data/README +15 -7
- data/Rakefile +7 -3
- data/bin/rdebug +106 -28
- data/cli/ruby-debug.rb +31 -2
- data/cli/ruby-debug/commands/breakpoints.rb +4 -0
- data/cli/ruby-debug/commands/continue.RB.save +48 -0
- data/cli/ruby-debug/commands/disassemble.RB +38 -0
- data/cli/ruby-debug/commands/frame.rb +46 -0
- data/cli/ruby-debug/commands/source.RB +44 -0
- data/cli/ruby-debug/interface.rb +1 -1
- data/rdbg.rb +0 -0
- data/test/base/base.rb +0 -0
- data/test/base/binding.rb +0 -0
- data/test/base/catchpoint.rb +0 -0
- data/test/cli/commands/catchpoint_test.rb +9 -8
- data/test/cli/commands/unit/regexp.rb +31 -0
- data/test/data/annotate.right +2 -0
- data/test/data/breakpoints.right +11 -7
- data/test/data/emacs_basic.right +7 -2
- data/test/data/finish.right +0 -12
- data/test/data/pm-bug.cmd +7 -0
- data/test/data/pm-bug.right +12 -0
- data/test/data/post-mortem.right +4 -4
- data/test/data/raise.right +3 -3
- data/test/dollar-0.rb +0 -0
- data/test/except-bug2.rb +7 -0
- data/test/gcd-dbg.rb +0 -0
- data/test/helper.rb +5 -1
- data/test/pm-base.rb +0 -0
- data/test/pm-bug.rb +3 -0
- data/test/pm.rb +0 -0
- data/test/raise.rb +0 -0
- data/test/scope-test.rb +8 -0
- data/test/tdebug.rb +10 -1
- data/test/test-annotate.rb +0 -0
- data/test/test-break-bad.rb +0 -0
- data/test/test-breakpoints.rb +0 -0
- data/test/test-catch.rb +0 -0
- data/test/test-condition.rb +0 -0
- data/test/test-ctrl.rb +0 -0
- data/test/test-display.rb +0 -0
- data/test/test-dollar-0.rb +9 -3
- data/test/test-edit.rb +0 -0
- data/test/test-emacs-basic.rb +0 -0
- data/test/test-enable.rb +0 -0
- data/test/test-finish.rb +7 -7
- data/test/test-frame.rb +1 -1
- data/test/test-help.rb +0 -0
- data/test/test-hist.rb +0 -0
- data/test/test-info-thread.rb +0 -0
- data/test/test-info-var.rb +0 -0
- data/test/test-info.rb +0 -0
- data/test/test-init.rb +0 -0
- data/test/test-list.rb +0 -0
- data/test/test-method.rb +0 -0
- data/test/test-output.rb +0 -0
- data/test/test-pm.rb +13 -0
- data/test/test-quit.rb +0 -0
- data/test/test-raise.rb +0 -0
- data/test/test-save.rb +0 -0
- data/test/test-setshow.rb +0 -0
- data/test/test-source.rb +0 -0
- data/test/test-stepping.rb +0 -0
- data/test/test-trace.rb +0 -0
- data/test/trunc-call.rb +31 -0
- data/test/tvar.rb +3 -0
- metadata +179 -177
data/cli/ruby-debug.rb
CHANGED
@@ -25,9 +25,15 @@ module Debugger
|
|
25
25
|
end
|
26
26
|
|
27
27
|
class << self
|
28
|
+
# gdb-style annotation mode. Used in GNU Emacs interface
|
29
|
+
attr_accessor :annotate
|
30
|
+
|
28
31
|
# in remote mode, wait for the remote connection
|
29
32
|
attr_accessor :wait_connection
|
30
|
-
|
33
|
+
|
34
|
+
# If set, a string to look for in caller() and is used to see
|
35
|
+
# if the call stack is truncated.
|
36
|
+
attr_accessor :start_sentinal
|
31
37
|
|
32
38
|
attr_reader :thread, :control_thread
|
33
39
|
|
@@ -80,7 +86,7 @@ module Debugger
|
|
80
86
|
|
81
87
|
def start_control(host = nil, ctrl_port = PORT + 1) # :nodoc:
|
82
88
|
raise "Debugger is not started" unless started?
|
83
|
-
return if @control_thread
|
89
|
+
return if defined?(@control_thread) && @control_thread
|
84
90
|
@control_thread = DebugThread.new do
|
85
91
|
server = TCPServer.new(host, ctrl_port)
|
86
92
|
while (session = server.accept)
|
@@ -145,3 +151,26 @@ module Debugger
|
|
145
151
|
end
|
146
152
|
end
|
147
153
|
end
|
154
|
+
|
155
|
+
module Kernel
|
156
|
+
|
157
|
+
# Enters the debugger in the current thread after _steps_ line events occur.
|
158
|
+
# Before entering the debugger startup script is read.
|
159
|
+
#
|
160
|
+
# Setting _steps_ to 0 will cause a break in the debugger subroutine
|
161
|
+
# and not wait for a line event to occur. You will have to go "up 1"
|
162
|
+
# in order to be back in your debugged program rather than the
|
163
|
+
# debugger. Settings _steps_ to 0 could be useful you want to stop
|
164
|
+
# right after the last statement in some scope, because the next
|
165
|
+
# step will take you out of some scope.
|
166
|
+
def debugger(steps = 1)
|
167
|
+
Debugger.start unless Debugger.started?
|
168
|
+
Debugger.run_init_script(StringIO.new)
|
169
|
+
if 0 == steps
|
170
|
+
Debugger.current_context.stop_frame = 0
|
171
|
+
else
|
172
|
+
Debugger.current_context.stop_next = steps
|
173
|
+
end
|
174
|
+
end
|
175
|
+
alias breakpoint debugger unless respond_to?(:breakpoint)
|
176
|
+
end
|
@@ -72,7 +72,11 @@ module Debugger
|
|
72
72
|
errmsg("Line %d is not a stopping point in file \"%s\".\n", line, file)
|
73
73
|
return
|
74
74
|
end
|
75
|
+
else
|
76
|
+
errmsg("No source file named %s\n" % file)
|
77
|
+
return unless confirm("Set breakpoint anyway? (y/n) ")
|
75
78
|
end
|
79
|
+
|
76
80
|
unless @state.context
|
77
81
|
errmsg "We are not in a state we can add breakpoints.\n"
|
78
82
|
return
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
# Implements debugger "continue" command.
|
4
|
+
class ContinueCommand < Command
|
5
|
+
self.allow_in_post_mortem = false
|
6
|
+
self.need_context = true
|
7
|
+
def regexp
|
8
|
+
/^\s* c(?:ont(?:inue)?)? (?:\s+(.*))? $/x
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
unless @state.context
|
13
|
+
errmsg "We are not in a state we can continue.\n"
|
14
|
+
return
|
15
|
+
end
|
16
|
+
if @match[1] && !@state.context.dead?
|
17
|
+
if '-' == @match[1]
|
18
|
+
Debugger.stop if Debugger.started?
|
19
|
+
else
|
20
|
+
filename = File.expand_path(@state.file)
|
21
|
+
line_number = get_int(@match[1], "Continue", 0, nil, 0)
|
22
|
+
return unless line_number
|
23
|
+
unless LineCache.trace_line_numbers(filename).member?(line_number)
|
24
|
+
errmsg("Line %d is not a stopping point in file \"%s\".\n",
|
25
|
+
line_number, filename)
|
26
|
+
return
|
27
|
+
end
|
28
|
+
@state.context.set_breakpoint(filename, line_number)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
@state.proceed
|
32
|
+
end
|
33
|
+
|
34
|
+
class << self
|
35
|
+
def help_command
|
36
|
+
'continue'
|
37
|
+
end
|
38
|
+
|
39
|
+
def help(cmd)
|
40
|
+
%{
|
41
|
+
c[ont[inue]][ nnn | -]\trun until program ends, hits a breakpoint or reaches line nnn.
|
42
|
+
|
43
|
+
If - is given then we issue a Debugger.stop to remove tracing the program continues at full speed.
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
require 'pp'
|
4
|
+
require 'nodepp'
|
5
|
+
require 'classtree'
|
6
|
+
require 'parse_tree'
|
7
|
+
class Disassemble < Command # :nodoc:
|
8
|
+
self.allow_in_control = false
|
9
|
+
@@parse_tree = ParseTree.new(true)
|
10
|
+
|
11
|
+
def regexp
|
12
|
+
/^\s*(dis(?:assemble)?)\s+/
|
13
|
+
end
|
14
|
+
|
15
|
+
def execute
|
16
|
+
expr = @match ? @match.post_match : @input
|
17
|
+
binding = @state.context ? get_binding : TOPLEVEL_BINDING
|
18
|
+
method_str = "method(:#{expr})"
|
19
|
+
if method_obj = debug_eval(method_str, binding)
|
20
|
+
print @@parse_tree.parse_tree_for_method(method_obj.class,
|
21
|
+
method_str).inspect
|
22
|
+
print "#{method_obj}\n"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class << self
|
27
|
+
def help_command
|
28
|
+
%w|disassemble method-name|
|
29
|
+
end
|
30
|
+
|
31
|
+
def help(cmd)
|
32
|
+
%{
|
33
|
+
dis[assemble] method-name\tdo live unparsing of method name
|
34
|
+
}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end if false
|
38
|
+
end
|
@@ -107,6 +107,48 @@ module Debugger
|
|
107
107
|
print fmt % [CommandProcessor.canonic_file(file), line]
|
108
108
|
end
|
109
109
|
end
|
110
|
+
|
111
|
+
# Check if call stack is truncated. This can happen if
|
112
|
+
# Debugger.start is not called low enough in the call stack. An
|
113
|
+
# array of additional callstack lines from caller is returned if
|
114
|
+
# definitely truncated, false if not, and nil if we don't know.
|
115
|
+
#
|
116
|
+
# We determine truncation based on a passed in sentinal set via
|
117
|
+
# caller which can be nil.
|
118
|
+
#
|
119
|
+
# First we see if we can find our position in caller. If so, then
|
120
|
+
# we compare context position to that in caller using sentinal
|
121
|
+
# as a place to start ignoring additional caller entries. sentinal
|
122
|
+
# is set by rdebug, but if it's not set, i.e. nil then additional
|
123
|
+
# entries are presumably ones that we haven't recorded in context
|
124
|
+
def truncated_callstack?(context, sentinal=nil, cs=caller)
|
125
|
+
recorded_size = context.stack_size
|
126
|
+
to_find_fl = "#{context.frame_file(0)}:#{context.frame_line(0)}"
|
127
|
+
top_discard = false
|
128
|
+
cs.each_with_index do |fl, i|
|
129
|
+
fl.gsub!(/in `.*'$/, '')
|
130
|
+
fl.gsub!(/:$/, '')
|
131
|
+
if fl == to_find_fl
|
132
|
+
top_discard = i
|
133
|
+
break
|
134
|
+
end
|
135
|
+
end
|
136
|
+
if top_discard
|
137
|
+
cs = cs[top_discard..-1]
|
138
|
+
return false unless cs
|
139
|
+
return cs unless sentinal
|
140
|
+
if cs.size > recorded_size+2 && cs[recorded_size+2] != sentinal
|
141
|
+
# caller seems to truncate recursive calls and we don't.
|
142
|
+
# See if we can find sentinal in the first 0..recorded_size+1 entries
|
143
|
+
return false if cs[0..recorded_size+1].any?{ |f| f==sentinal }
|
144
|
+
return cs
|
145
|
+
end
|
146
|
+
return false
|
147
|
+
end
|
148
|
+
return nil
|
149
|
+
end
|
150
|
+
|
151
|
+
|
110
152
|
end
|
111
153
|
|
112
154
|
# Implements debugger "where" or "backtrace" command.
|
@@ -123,6 +165,10 @@ module Debugger
|
|
123
165
|
print " "
|
124
166
|
end
|
125
167
|
print_frame(idx)
|
168
|
+
|
169
|
+
end
|
170
|
+
if truncated_callstack?(@state.context, Debugger.start_sentinal)
|
171
|
+
print "Warning: saved frames may be incomplete; compare with caller(0).\n"
|
126
172
|
end
|
127
173
|
end
|
128
174
|
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Debugger
|
2
|
+
# Implements debugger "source" command.
|
3
|
+
class SourceCommand < Command
|
4
|
+
self.allow_in_control = true
|
5
|
+
|
6
|
+
def regexp
|
7
|
+
/^\s* so(?:urce)? (\s+ -v)? \s+ (.+) $/x
|
8
|
+
end
|
9
|
+
|
10
|
+
def execute
|
11
|
+
if 3 == @match.size then
|
12
|
+
verbose=true
|
13
|
+
file=@match[2]
|
14
|
+
else
|
15
|
+
verbose=false
|
16
|
+
file=@match[1]
|
17
|
+
end
|
18
|
+
|
19
|
+
file = File.expand_path(file).strip
|
20
|
+
unless File.exist?(file)
|
21
|
+
errmsg "Command file '#{file}' is not found\n"
|
22
|
+
return
|
23
|
+
end
|
24
|
+
if @state and @state.interface
|
25
|
+
@state.interface.command_queue += File.open(file).readlines
|
26
|
+
else
|
27
|
+
Debugger.run_script(file, @state, verbose)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class << self
|
32
|
+
def help_command
|
33
|
+
'source'
|
34
|
+
end
|
35
|
+
|
36
|
+
def help(cmd)
|
37
|
+
%{
|
38
|
+
source FILE\texecutes a file containing debugger commands
|
39
|
+
}
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
data/cli/ruby-debug/interface.rb
CHANGED
data/rdbg.rb
CHANGED
File without changes
|
data/test/base/base.rb
CHANGED
File without changes
|
data/test/base/binding.rb
CHANGED
File without changes
|
data/test/base/catchpoint.rb
CHANGED
File without changes
|
@@ -2,16 +2,17 @@
|
|
2
2
|
|
3
3
|
require 'test/unit'
|
4
4
|
|
5
|
-
BASE_DIR = File.join(File.dirname(__FILE__), '..', '..', '..')
|
6
|
-
|
7
|
-
%w(ext lib cli).each do |dir|
|
8
|
-
$: << File.join(BASE_DIR, dir)
|
9
|
-
end
|
10
|
-
|
11
|
-
require File.join(BASE_DIR, 'cli', 'ruby-debug')
|
12
|
-
|
13
5
|
class TestCatchCommand < Test::Unit::TestCase
|
14
6
|
|
7
|
+
base_dir = File.expand_path(File.join(File.dirname(__FILE__),
|
8
|
+
'..', '..', '..'))
|
9
|
+
|
10
|
+
%w(ext lib cli).each do |dir|
|
11
|
+
$: << File.join(base_dir, dir)
|
12
|
+
end
|
13
|
+
|
14
|
+
require File.join(base_dir, 'cli', 'ruby-debug')
|
15
|
+
|
15
16
|
class MockState
|
16
17
|
attr_accessor :message
|
17
18
|
def context; end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'test/unit'
|
2
|
+
|
3
|
+
|
4
|
+
class TestCommandREs < Test::Unit::TestCase
|
5
|
+
base_dir=File.expand_path(File.join(File.dirname(__FILE__),
|
6
|
+
'..', '..', '..', '..',
|
7
|
+
'cli', 'ruby-debug'))
|
8
|
+
require File.join(base_dir, 'command')
|
9
|
+
require File.join(base_dir, 'commands', 'frame')
|
10
|
+
include Debugger
|
11
|
+
|
12
|
+
def test_up
|
13
|
+
c = UpCommand.new(nil)
|
14
|
+
assert c.regexp.match('up')
|
15
|
+
assert c.regexp.match('up 2')
|
16
|
+
assert c.regexp.match('up 2+5')
|
17
|
+
assert c.regexp.match('u')
|
18
|
+
assert c.regexp.match('u 2')
|
19
|
+
assert_equal nil, c.regexp.match('ufoo')
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_down
|
23
|
+
c = DownCommand.new(nil)
|
24
|
+
assert c.regexp.match('down')
|
25
|
+
assert c.regexp.match('down 2')
|
26
|
+
assert_equal(nil, c.regexp.match('d 2'))
|
27
|
+
assert_equal(nil, c.regexp.match('d'))
|
28
|
+
assert_equal(nil, c.regexp.match('dow'))
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
data/test/data/annotate.right
CHANGED
data/test/data/breakpoints.right
CHANGED
@@ -42,16 +42,17 @@ return nil if a <= 0
|
|
42
42
|
# info program
|
43
43
|
Program stopped. It stopped at a breakpoint.
|
44
44
|
# c 6
|
45
|
-
Breakpoint
|
46
|
-
gcd.rb:
|
47
|
-
|
45
|
+
Breakpoint 3 at Object:gcd
|
46
|
+
gcd.rb:4
|
47
|
+
def gcd(a, b)
|
48
48
|
# info break
|
49
49
|
Num Enb What
|
50
50
|
1 y at gcd.rb:6
|
51
51
|
breakpoint already hit 1 time
|
52
52
|
2 y at gcd.rb:10
|
53
|
-
breakpoint already hit
|
53
|
+
breakpoint already hit 1 time
|
54
54
|
3 y at Object:gcd
|
55
|
+
breakpoint already hit 1 time
|
55
56
|
# break foo
|
56
57
|
*** Invalid breakpoint location: foo.
|
57
58
|
# info break
|
@@ -59,23 +60,26 @@ Num Enb What
|
|
59
60
|
1 y at gcd.rb:6
|
60
61
|
breakpoint already hit 1 time
|
61
62
|
2 y at gcd.rb:10
|
62
|
-
breakpoint already hit
|
63
|
+
breakpoint already hit 1 time
|
63
64
|
3 y at Object:gcd
|
65
|
+
breakpoint already hit 1 time
|
64
66
|
# disable 1
|
65
67
|
# info break
|
66
68
|
Num Enb What
|
67
69
|
1 n at gcd.rb:6
|
68
70
|
breakpoint already hit 1 time
|
69
71
|
2 y at gcd.rb:10
|
70
|
-
breakpoint already hit
|
72
|
+
breakpoint already hit 1 time
|
71
73
|
3 y at Object:gcd
|
74
|
+
breakpoint already hit 1 time
|
72
75
|
# delete 1
|
73
76
|
# # We should see breakpoint 2 but not 1
|
74
77
|
# info break
|
75
78
|
Num Enb What
|
76
79
|
2 y at gcd.rb:10
|
77
|
-
breakpoint already hit
|
80
|
+
breakpoint already hit 1 time
|
78
81
|
3 y at Object:gcd
|
82
|
+
breakpoint already hit 1 time
|
79
83
|
# # We should still be able to access 2
|
80
84
|
# disable 2
|
81
85
|
# disable bar
|
data/test/data/emacs_basic.right
CHANGED
@@ -42,8 +42,9 @@ return nil if a <= 0
|
|
42
42
|
# info program
|
43
43
|
Program stopped. It stopped at a breakpoint.
|
44
44
|
# c 10
|
45
|
-
|
46
|
-
|
45
|
+
Breakpoint 3 at Object:gcd
|
46
|
+
gcd.rb:4
|
47
|
+
def gcd(a, b)
|
47
48
|
# info break
|
48
49
|
Num Enb What
|
49
50
|
1 y at gcd.rb:6
|
@@ -51,6 +52,7 @@ Num Enb What
|
|
51
52
|
2 y at gcd.rb:10
|
52
53
|
breakpoint already hit 1 time
|
53
54
|
3 y at Object:gcd
|
55
|
+
breakpoint already hit 1 time
|
54
56
|
# break foo
|
55
57
|
*** Invalid breakpoint location: foo.
|
56
58
|
# info break
|
@@ -60,6 +62,7 @@ Num Enb What
|
|
60
62
|
2 y at gcd.rb:10
|
61
63
|
breakpoint already hit 1 time
|
62
64
|
3 y at Object:gcd
|
65
|
+
breakpoint already hit 1 time
|
63
66
|
# disable 1
|
64
67
|
# info break
|
65
68
|
Num Enb What
|
@@ -68,6 +71,7 @@ Num Enb What
|
|
68
71
|
2 y at gcd.rb:10
|
69
72
|
breakpoint already hit 1 time
|
70
73
|
3 y at Object:gcd
|
74
|
+
breakpoint already hit 1 time
|
71
75
|
# enable breakpoint 1
|
72
76
|
# enable br 10
|
73
77
|
Enable breakpoints argument '10' needs to at most 3.
|
@@ -78,6 +82,7 @@ Num Enb What
|
|
78
82
|
2 y at gcd.rb:10
|
79
83
|
breakpoint already hit 1 time
|
80
84
|
3 y at Object:gcd
|
85
|
+
breakpoint already hit 1 time
|
81
86
|
# # We should still be able to access 2
|
82
87
|
# disable 2
|
83
88
|
# enable
|