ruby-debug 0.10.2 → 0.10.3
Sign up to get free protection for your applications and to get access to all the features.
- 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
|