ruby-debug 0.10.3 → 0.10.4
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGES +15 -0
- data/ChangeLog +461 -104
- data/Rakefile +69 -13
- data/VERSION +3 -0
- data/bin/rdebug +2 -1
- data/cli/ruby-debug.rb +8 -5
- data/cli/ruby-debug/command.rb +43 -4
- data/cli/ruby-debug/commands/breakpoints.rb +3 -1
- data/cli/ruby-debug/commands/catchpoint.rb +5 -3
- data/cli/ruby-debug/commands/{continue.RB.save → continue.RB} +0 -0
- data/cli/ruby-debug/commands/eval.rb +4 -1
- data/cli/ruby-debug/commands/frame.rb +11 -10
- data/cli/ruby-debug/commands/help.rb +5 -0
- data/cli/ruby-debug/commands/info.rb +6 -3
- data/cli/ruby-debug/commands/irb.rb +115 -12
- data/cli/ruby-debug/commands/kill.rb +50 -0
- data/cli/ruby-debug/commands/list.rb +3 -3
- data/cli/ruby-debug/commands/quit.rb +12 -6
- data/cli/ruby-debug/commands/raise.RB +41 -0
- data/cli/ruby-debug/commands/show.rb +26 -28
- data/cli/ruby-debug/helper.rb +5 -0
- data/cli/ruby-debug/interface.rb +53 -25
- data/cli/ruby-debug/processor.RB +484 -0
- data/cli/ruby-debug/processor.rb +140 -56
- data/rdbg.rb +0 -0
- data/runner.sh +7 -0
- data/test/base/base.rb +0 -0
- data/test/base/binding.rb +0 -0
- data/test/base/catchpoint.rb +0 -0
- data/test/base/reload_bug.rb +8 -0
- data/test/brkpt-class-bug.rb +8 -0
- data/test/cli/commands/unit/regexp.rb +11 -0
- data/test/config.yaml +8 -0
- data/test/data/annotate.cmd +1 -1
- data/test/data/annotate.right +3 -3
- data/test/data/break_bad.cmd +1 -1
- data/test/data/break_bad.right +1 -1
- data/test/data/break_loop_bug.right +1 -1
- data/test/data/breakpoints.cmd +1 -1
- data/test/data/breakpoints.right +3 -3
- data/test/data/brkpt-class-bug.cmd +9 -0
- data/test/data/brkpt-class-bug.right +18 -0
- data/test/data/catch.right +2 -0
- data/test/data/condition.cmd +1 -0
- data/test/data/condition.right +5 -3
- data/test/data/ctrl.right +2 -2
- data/test/data/display.right +1 -1
- data/test/data/emacs_basic.right +2 -2
- data/test/data/except-bug1.cmd +7 -0
- data/test/data/except-bug1.right +13 -0
- data/test/data/file-with-space.cmd +7 -0
- data/test/data/file-with-space.right +9 -0
- data/test/data/finish.right +1 -1
- data/test/data/frame.cmd +7 -1
- data/test/data/frame.right +12 -1
- data/test/data/info-var.right +1 -1
- data/test/data/info.cmd +1 -0
- data/test/data/info.right +21 -2
- data/test/data/list.right +1 -1
- data/test/data/method.right +1 -1
- data/test/data/post-mortem.right +5 -4
- data/test/data/save.right +1 -1
- data/test/data/setshow.cmd +0 -10
- data/test/data/setshow.right +0 -17
- data/test/dollar-0.rb +0 -0
- data/test/except-bug1.rb +4 -0
- data/test/file with space.rb +1 -0
- data/test/gcd-dbg.rb +0 -0
- data/test/helper.rb +7 -1
- data/test/pm-base.rb +0 -0
- data/test/pm.rb +1 -1
- data/test/raise.rb +0 -0
- data/test/rdebug-save.1 +7 -0
- data/test/tdebug.rb +2 -2
- 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-brkpt-class-bug.rb +26 -0
- data/test/test-catch.rb +1 -1
- data/test/test-condition.rb +1 -1
- data/test/test-ctrl.rb +1 -0
- data/test/test-display.rb +0 -0
- data/test/test-dollar-0.rb +0 -0
- 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-except-bug1.rb +31 -0
- data/test/test-file-with-space.rb +30 -0
- data/test/test-finish.rb +0 -0
- data/test/test-frame.rb +0 -0
- 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 +7 -1
- 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 +0 -0
- data/test/test-quit.rb +0 -0
- data/test/test-raise.rb +1 -1
- data/test/test-save.rb +7 -1
- 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
- metadata +211 -180
data/cli/ruby-debug/helper.rb
CHANGED
data/cli/ruby-debug/interface.rb
CHANGED
@@ -1,9 +1,22 @@
|
|
1
1
|
module Debugger
|
2
|
-
|
2
|
+
|
3
|
+
# An _Interface_ is the kind of input/output interaction that goes
|
4
|
+
# on between the user and the debugged program. It includes things
|
5
|
+
# like how one wants to show error messages, or read input. This is
|
6
|
+
# the base class. Subclasses inlcude Debugger::LocalInterface,
|
7
|
+
# Debugger::RemoteInterface and Debugger::ScriptInterface.
|
8
|
+
class Interface
|
3
9
|
attr_writer :have_readline # true if Readline is available
|
4
10
|
|
5
11
|
def initialize
|
6
|
-
|
12
|
+
begin
|
13
|
+
require 'readline'
|
14
|
+
@have_readline = true
|
15
|
+
@history_save = true
|
16
|
+
rescue LoadError
|
17
|
+
@have_readline = false
|
18
|
+
@history_save = false
|
19
|
+
end
|
7
20
|
end
|
8
21
|
|
9
22
|
# Common routine for reporting debugger error messages.
|
@@ -28,9 +41,16 @@ module Debugger
|
|
28
41
|
print afmt(msg)
|
29
42
|
end
|
30
43
|
|
44
|
+
# Things we do before terminating.
|
45
|
+
def finalize
|
46
|
+
end
|
47
|
+
|
31
48
|
end
|
32
49
|
|
33
|
-
|
50
|
+
# A _LocalInterface_ is the kind of I/O interactive performed when
|
51
|
+
# the user interface is in the same process as the debugged program.
|
52
|
+
# Compare with Debugger::RemoteInterface.
|
53
|
+
class LocalInterface < Interface
|
34
54
|
attr_accessor :command_queue
|
35
55
|
attr_accessor :histfile
|
36
56
|
attr_accessor :history_save
|
@@ -43,19 +63,20 @@ module Debugger
|
|
43
63
|
def initialize()
|
44
64
|
super
|
45
65
|
@command_queue = []
|
46
|
-
@have_readline = false
|
47
|
-
@history_save = true
|
48
|
-
# take gdb's default
|
49
|
-
@history_length = ENV["HISTSIZE"] ? ENV["HISTSIZE"].to_i : 256
|
50
|
-
@histfile = File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
|
51
|
-
FILE_HISTORY)
|
52
|
-
open(@histfile, 'r') do |file|
|
53
|
-
file.each do |line|
|
54
|
-
line.chomp!
|
55
|
-
Readline::HISTORY << line
|
56
|
-
end
|
57
|
-
end if File.exist?(@histfile)
|
58
66
|
@restart_file = nil
|
67
|
+
|
68
|
+
if @have_readline
|
69
|
+
# take gdb's default
|
70
|
+
@history_length = ENV['HISTSIZE'] ? ENV['HISTSIZE'].to_i : 256
|
71
|
+
@histfile = File.join(ENV['HOME']||ENV['HOMEPATH']||'.',
|
72
|
+
FILE_HISTORY)
|
73
|
+
open(@histfile, 'r') do |file|
|
74
|
+
file.each do |line|
|
75
|
+
line.chomp!
|
76
|
+
Readline::HISTORY << line
|
77
|
+
end
|
78
|
+
end if File.exist?(@histfile)
|
79
|
+
end
|
59
80
|
end
|
60
81
|
|
61
82
|
def read_command(prompt)
|
@@ -94,7 +115,7 @@ module Debugger
|
|
94
115
|
@have_readline = true
|
95
116
|
define_method(:save_history) do
|
96
117
|
iface = self.handler.interface
|
97
|
-
iface.histfile ||= File.join(ENV[
|
118
|
+
iface.histfile ||= File.join(ENV['HOME']||ENV['HOMEPATH']||'.',
|
98
119
|
FILE_HISTORY)
|
99
120
|
open(iface.histfile, 'w') do |file|
|
100
121
|
Readline::HISTORY.to_a.last(iface.history_length).each do |line|
|
@@ -125,7 +146,11 @@ module Debugger
|
|
125
146
|
end
|
126
147
|
end
|
127
148
|
|
128
|
-
|
149
|
+
# A _RemoteInterface_ is the kind of I/O interactive performed when
|
150
|
+
# the user interface is in a different process (and possibly
|
151
|
+
# different computer) than the debugged program. Compare with
|
152
|
+
# Debugger::LocalInterface.
|
153
|
+
class RemoteInterface < Interface
|
129
154
|
attr_accessor :command_queue
|
130
155
|
attr_accessor :histfile
|
131
156
|
attr_accessor :history_save
|
@@ -157,9 +182,6 @@ module Debugger
|
|
157
182
|
send_command "CONFIRM #{prompt}"
|
158
183
|
end
|
159
184
|
|
160
|
-
def finalize
|
161
|
-
end
|
162
|
-
|
163
185
|
def read_command(prompt)
|
164
186
|
send_command "PROMPT #{prompt}"
|
165
187
|
end
|
@@ -182,7 +204,11 @@ module Debugger
|
|
182
204
|
end
|
183
205
|
end
|
184
206
|
|
185
|
-
|
207
|
+
# A _ScriptInterface_ is used when we are reading debugger commands
|
208
|
+
# from a command-file rather than an interactive user. Command files
|
209
|
+
# appear in a users initialization script (e.g. .rdebugrc) and appear
|
210
|
+
# when running the debugger command _source_ (Debugger::SourceCommand).
|
211
|
+
class ScriptInterface < Interface
|
186
212
|
attr_accessor :command_queue
|
187
213
|
attr_accessor :histfile
|
188
214
|
attr_accessor :history_save
|
@@ -198,10 +224,7 @@ module Debugger
|
|
198
224
|
@history_length = 256 # take gdb default
|
199
225
|
@histfile = ''
|
200
226
|
end
|
201
|
-
|
202
|
-
def finalize
|
203
|
-
end
|
204
|
-
|
227
|
+
|
205
228
|
def read_command(prompt)
|
206
229
|
while result = @file.gets
|
207
230
|
puts "# #{result}" if @verbose
|
@@ -213,10 +236,15 @@ module Debugger
|
|
213
236
|
result.chomp!
|
214
237
|
end
|
215
238
|
|
239
|
+
# Do we have ReadLine support? When running an debugger command
|
240
|
+
# script, we are not interactive so we just return _false_.
|
216
241
|
def readline_support?
|
217
242
|
false
|
218
243
|
end
|
219
244
|
|
245
|
+
# _confirm_ is called before performing a dangerous action. In
|
246
|
+
# running a debugger script, we don't want to prompt, so we'll pretend
|
247
|
+
# the user has unconditionally said "yes" and return String "y".
|
220
248
|
def confirm(prompt)
|
221
249
|
'y'
|
222
250
|
end
|
@@ -0,0 +1,484 @@
|
|
1
|
+
require 'ruby-debug/interface'
|
2
|
+
require 'ruby-debug/command'
|
3
|
+
|
4
|
+
module Debugger
|
5
|
+
|
6
|
+
# Should this be a mixin?
|
7
|
+
class Processor # :nodoc
|
8
|
+
attr_accessor :interface
|
9
|
+
attr_reader :processor
|
10
|
+
attr_reader :commands
|
11
|
+
|
12
|
+
# Format msg with gdb-style annotation header
|
13
|
+
def afmt(msg, newline="\n")
|
14
|
+
"\032\032#{msg}#{newline}"
|
15
|
+
end
|
16
|
+
|
17
|
+
def aprint(msg)
|
18
|
+
print afmt(msg) if Debugger.annotate.to_i > 2
|
19
|
+
end
|
20
|
+
|
21
|
+
# FIXME: use delegate?
|
22
|
+
def errmsg(*args)
|
23
|
+
@interface.errmsg(*args)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Callers of this routine should make sure to use comma to
|
27
|
+
# separate format argments rather than %. Otherwise it seems that
|
28
|
+
# if the string you want to print has format specifier, which
|
29
|
+
# could happen if you are trying to show say a source-code line
|
30
|
+
# with "puts" or "print" in it, this print routine will give an
|
31
|
+
# error saying it is looking for more arguments.
|
32
|
+
def print(*args)
|
33
|
+
@interface.print(*args)
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class CommandProcessor < Processor # :nodoc:
|
39
|
+
attr_reader :display
|
40
|
+
|
41
|
+
# FIXME: get from Command regexp method.
|
42
|
+
@@Show_breakpoints_postcmd = [
|
43
|
+
/^\s*b(?:reak)?/,
|
44
|
+
/^\s* cond(?:ition)? (?:\s+(\d+)\s*(.*))?$/ix,
|
45
|
+
/^\s*del(?:ete)?(?:\s+(.*))?$/ix,
|
46
|
+
/^\s* dis(?:able)? (?:\s+(.*))?$/ix,
|
47
|
+
/^\s* en(?:able)? (?:\s+(.*))?$/ix,
|
48
|
+
# "tbreak", "clear",
|
49
|
+
]
|
50
|
+
@@Show_annotations_run = [
|
51
|
+
/^\s*c(?:ont(?:inue)?)?(?:\s+(.*))?$/,
|
52
|
+
/^\s*fin(?:ish)?$/,
|
53
|
+
/^\s*n(?:ext)?([+-])?(?:\s+(.*))?$/,
|
54
|
+
/^\s*s(?:tep)?([+-])?(?:\s+(.*))?$/
|
55
|
+
]
|
56
|
+
|
57
|
+
@@Show_annotations_postcmd = [
|
58
|
+
/^\s* down (?:\s+(.*))? .*$/x,
|
59
|
+
/^\s* f(?:rame)? (?:\s+ (.*))? \s*$/x,
|
60
|
+
/^\s* u(?:p)? (?:\s+(.*))?$/x
|
61
|
+
]
|
62
|
+
|
63
|
+
def initialize(interface = LocalInterface.new)
|
64
|
+
@interface = interface
|
65
|
+
@commands = []
|
66
|
+
@display = []
|
67
|
+
|
68
|
+
@mutex = Mutex.new
|
69
|
+
@last_cmd = nil
|
70
|
+
@last_file = nil # Filename the last time we stopped
|
71
|
+
@last_line = nil # line number the last time we stopped
|
72
|
+
@debugger_breakpoints_were_empty = false # Show breakpoints 1st time
|
73
|
+
@debugger_displays_were_empty = true # No display 1st time
|
74
|
+
@debugger_context_was_dead = true # Assume we haven't started.
|
75
|
+
end
|
76
|
+
|
77
|
+
def interface=(interface)
|
78
|
+
@mutex.synchronize do
|
79
|
+
@interface.close if @interface
|
80
|
+
@interface = interface
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
require 'pathname' # For cleanpath
|
85
|
+
|
86
|
+
# Regularize file name.
|
87
|
+
# This is also used as a common funnel place if basename is
|
88
|
+
# desired or if we are working remotely and want to change the
|
89
|
+
# basename. Or we are eliding filenames.
|
90
|
+
def self.canonic_file(filename)
|
91
|
+
# For now we want resolved filenames
|
92
|
+
if Command.settings[:basename]
|
93
|
+
File.basename(filename)
|
94
|
+
else
|
95
|
+
# Cache this?
|
96
|
+
Pathname.new(filename).cleanpath.to_s
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.print_location_and_text(file, line)
|
101
|
+
file_line = "%s:%s\n%s" % [canonic_file(file), line,
|
102
|
+
Debugger.line_at(file, line)]
|
103
|
+
# FIXME: use annotations routines
|
104
|
+
if Debugger.annotate.to_i > 2
|
105
|
+
file_line = "\032\032source #{file_line}"
|
106
|
+
elsif ENV['EMACS']
|
107
|
+
file_line = "\032\032#{file_line}"
|
108
|
+
end
|
109
|
+
print file_line
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.protect(mname)
|
113
|
+
alias_method "__#{mname}", mname
|
114
|
+
module_eval %{
|
115
|
+
def #{mname}(*args)
|
116
|
+
@mutex.synchronize do
|
117
|
+
return unless @interface
|
118
|
+
__#{mname}(*args)
|
119
|
+
end
|
120
|
+
rescue IOError, Errno::EPIPE
|
121
|
+
self.interface = nil
|
122
|
+
rescue SignalException
|
123
|
+
raise
|
124
|
+
rescue Exception => e
|
125
|
+
unless
|
126
|
+
print "INTERNAL ERROR!!! #\{$!\}\n" rescue nil
|
127
|
+
print $!.backtrace.map{|l| "\t#\{l\}"}.join("\n") rescue nil
|
128
|
+
end
|
129
|
+
}
|
130
|
+
end
|
131
|
+
|
132
|
+
def at_breakpoint(context, breakpoint)
|
133
|
+
aprint 'stopped' if Debugger.annotate.to_i > 2
|
134
|
+
n = Debugger.breakpoints.index(breakpoint) + 1
|
135
|
+
file = CommandProcessor.canonic_file(breakpoint.source)
|
136
|
+
line = breakpoint.pos
|
137
|
+
if Debugger.annotate.to_i > 2
|
138
|
+
print afmt("source #{file}:#{line}")
|
139
|
+
end
|
140
|
+
print "Breakpoint %d at %s:%s\n", n, file, line
|
141
|
+
end
|
142
|
+
protect :at_breakpoint
|
143
|
+
|
144
|
+
def at_catchpoint(context, excpt)
|
145
|
+
aprint 'stopped' if Debugger.annotate.to_i > 2
|
146
|
+
file = CommandProcessor.canonic_file(context.frame_file(0))
|
147
|
+
line = context.frame_line(0)
|
148
|
+
print afmt("%s:%d" % [file, line]) if ENV['EMACS']
|
149
|
+
print "Catchpoint at %s:%d: `%s' (%s)\n", file, line, excpt, excpt.class
|
150
|
+
fs = context.stack_size
|
151
|
+
tb = caller(0)[-fs..-1]
|
152
|
+
if tb
|
153
|
+
for i in tb
|
154
|
+
print "\tfrom %s\n", i
|
155
|
+
end
|
156
|
+
end
|
157
|
+
end
|
158
|
+
protect :at_catchpoint
|
159
|
+
|
160
|
+
def at_tracing(context, file, line)
|
161
|
+
return if defined?(Debugger::RDEBUG_FILE) &&
|
162
|
+
Debugger::RDEBUG_FILE == file # Don't trace ourself
|
163
|
+
@last_file = CommandProcessor.canonic_file(file)
|
164
|
+
file = CommandProcessor.canonic_file(file)
|
165
|
+
unless file == @last_file and @last_line == line and
|
166
|
+
Command.settings[:tracing_plus]
|
167
|
+
print "Tracing(%d):%s:%s %s",
|
168
|
+
context.thnum, file, line, Debugger.line_at(file, line)
|
169
|
+
@last_file = file
|
170
|
+
@last_line = line
|
171
|
+
end
|
172
|
+
always_run(context, file, line, 2)
|
173
|
+
end
|
174
|
+
protect :at_tracing
|
175
|
+
|
176
|
+
def at_line(context, file, line)
|
177
|
+
process_commands(context, file, line)
|
178
|
+
end
|
179
|
+
protect :at_line
|
180
|
+
|
181
|
+
def at_return(context, file, line)
|
182
|
+
context.stop_frame = -1
|
183
|
+
process_commands(context, file, line)
|
184
|
+
end
|
185
|
+
|
186
|
+
def one_cmd(commands, context, input)
|
187
|
+
if cmd = commands.find{ |c| c.match(input) }
|
188
|
+
if context.dead? && cmd.class.need_context
|
189
|
+
p cmd
|
190
|
+
print "Command is unavailable\n"
|
191
|
+
else
|
192
|
+
cmd.execute
|
193
|
+
end
|
194
|
+
else
|
195
|
+
unknown_cmd = commands.find{|cmd| cmd.class.unknown }
|
196
|
+
if unknown_cmd
|
197
|
+
unknown_cmd.execute
|
198
|
+
else
|
199
|
+
errmsg "Unknown command: \"#{input}\". Try \"help\".\n"
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
private
|
205
|
+
|
206
|
+
# The prompt shown before reading a command.
|
207
|
+
def prompt(context)
|
208
|
+
p = '(rdb:%s) ' % (context.dead? ? 'post-mortem' : context.thnum)
|
209
|
+
p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
|
210
|
+
Debugger.annotate.to_i > 2
|
211
|
+
return p
|
212
|
+
end
|
213
|
+
|
214
|
+
# Run these commands, for example display commands or possibly
|
215
|
+
# the list or irb in an "autolist" or "autoirb".
|
216
|
+
# We return a list of commands that are acceptable to run bound
|
217
|
+
# to the current state.
|
218
|
+
def always_run(context, file, line, run_level)
|
219
|
+
event_cmds = Command.commands.select{|cmd| cmd.event }
|
220
|
+
|
221
|
+
# Remove some commands in post-mortem
|
222
|
+
event_cmds = event_cmds.find_all do |cmd|
|
223
|
+
cmd.allow_in_post_mortem
|
224
|
+
end if context.dead?
|
225
|
+
|
226
|
+
state = State.new(self) do |s|
|
227
|
+
s.context = context
|
228
|
+
s.file = file
|
229
|
+
s.line = line
|
230
|
+
s.binding = context.frame_binding(0)
|
231
|
+
s.display = display
|
232
|
+
s.interface = interface
|
233
|
+
s.commands = event_cmds
|
234
|
+
end
|
235
|
+
@interface.state = state if @interface.respond_to?('state=')
|
236
|
+
|
237
|
+
# Bind commands to the current state.
|
238
|
+
commands = event_cmds.map{|cmd| cmd.new(state)}
|
239
|
+
|
240
|
+
commands.select do |cmd|
|
241
|
+
cmd.class.always_run >= run_level
|
242
|
+
end.each {|cmd| cmd.execute}
|
243
|
+
return state, commands
|
244
|
+
end
|
245
|
+
|
246
|
+
# Handle debugger commands
|
247
|
+
def process_commands(context, file, line)
|
248
|
+
state, @commands = always_run(context, file, line, 1)
|
249
|
+
$rdebug_state = state if Command.settings[:debuggertesting]
|
250
|
+
splitter = lambda do |str|
|
251
|
+
str.split(/;/).inject([]) do |m, v|
|
252
|
+
if m.empty?
|
253
|
+
m << v
|
254
|
+
else
|
255
|
+
if m.last[-1] == ?\\
|
256
|
+
m.last[-1,1] = ''
|
257
|
+
m.last << ';' << v
|
258
|
+
else
|
259
|
+
m << v
|
260
|
+
end
|
261
|
+
end
|
262
|
+
m
|
263
|
+
end
|
264
|
+
end
|
265
|
+
|
266
|
+
preloop(@commands, context)
|
267
|
+
CommandProcessor.print_location_and_text(file, line)
|
268
|
+
while !state.proceed?
|
269
|
+
input = if @interface.command_queue.empty?
|
270
|
+
@interface.read_command(prompt(context))
|
271
|
+
else
|
272
|
+
@interface.command_queue.shift
|
273
|
+
end
|
274
|
+
break unless input
|
275
|
+
catch(:debug_error) do
|
276
|
+
if input == ""
|
277
|
+
next unless @last_cmd
|
278
|
+
input = @last_cmd
|
279
|
+
else
|
280
|
+
@last_cmd = input
|
281
|
+
end
|
282
|
+
splitter[input].each do |cmd|
|
283
|
+
one_cmd(@commands, context, cmd)
|
284
|
+
postcmd(@commands, context, cmd)
|
285
|
+
end
|
286
|
+
end
|
287
|
+
end
|
288
|
+
postloop(@commands, context)
|
289
|
+
exception = state.exception
|
290
|
+
@exception = nil
|
291
|
+
puts "raising exception #{state.exception}"
|
292
|
+
raise exception if exception
|
293
|
+
end # process_commands
|
294
|
+
|
295
|
+
def preloop(commands, context)
|
296
|
+
aprint('stopped') if Debugger.annotate.to_i > 2
|
297
|
+
if context.dead?
|
298
|
+
unless @debugger_context_was_dead
|
299
|
+
if Debugger.annotate.to_i > 2
|
300
|
+
aprint('exited')
|
301
|
+
print "The program finished.\n"
|
302
|
+
end
|
303
|
+
@debugger_context_was_dead = true
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
if Debugger.annotate.to_i > 2
|
308
|
+
# if we are here, the stack frames have changed outside the
|
309
|
+
# command loop (e.g. after a "continue" command), so we show
|
310
|
+
# the annotations again
|
311
|
+
breakpoint_annotations(commands, context)
|
312
|
+
display_annotations(commands, context)
|
313
|
+
annotation('stack', commands, context, "where")
|
314
|
+
annotation('variables', commands, context, "info variables") unless
|
315
|
+
context.dead?
|
316
|
+
end
|
317
|
+
end
|
318
|
+
|
319
|
+
def postcmd(commands, context, cmd)
|
320
|
+
if Debugger.annotate.to_i > 0
|
321
|
+
cmd = @last_cmd unless cmd
|
322
|
+
breakpoint_annotations(commands, context) if
|
323
|
+
@@Show_breakpoints_postcmd.find{|pat| cmd =~ pat}
|
324
|
+
display_annotations(commands, context)
|
325
|
+
if @@Show_annotations_postcmd.find{|pat| cmd =~ pat}
|
326
|
+
annotation('stack', commands, context, "where") if
|
327
|
+
context.stack_size > 0
|
328
|
+
annotation('variables', commands, context, "info variables") unless
|
329
|
+
context.dead?
|
330
|
+
end
|
331
|
+
if not context.dead? and @@Show_annotations_run.find{|pat| cmd =~ pat}
|
332
|
+
aprint 'starting' if Debugger.annotate.to_i > 2
|
333
|
+
|
334
|
+
@debugger_context_was_dead = false
|
335
|
+
end
|
336
|
+
end
|
337
|
+
end
|
338
|
+
|
339
|
+
def postloop(commands, context)
|
340
|
+
end
|
341
|
+
|
342
|
+
def annotation(label, commands, context, cmd)
|
343
|
+
print afmt(label)
|
344
|
+
one_cmd(commands, context, cmd)
|
345
|
+
### FIXME ANNOTATE: the following line should be deleted
|
346
|
+
print "\032\032\n"
|
347
|
+
end
|
348
|
+
|
349
|
+
def breakpoint_annotations(commands, context)
|
350
|
+
unless Debugger.breakpoints.empty? and @debugger_breakpoints_were_empty
|
351
|
+
annotation('breakpoints', commands, context, "info breakpoints")
|
352
|
+
@debugger_breakpoints_were_empty = Debugger.breakpoints.empty?
|
353
|
+
end
|
354
|
+
end
|
355
|
+
|
356
|
+
def display_annotations(commands, context)
|
357
|
+
return if display.empty?
|
358
|
+
# have_display = display.find{|d| d[0]}
|
359
|
+
# return unless have_display and @debugger_displays_were_empty
|
360
|
+
# @debugger_displays_were_empty = have_display
|
361
|
+
annotation('display', commands, context, "display")
|
362
|
+
end
|
363
|
+
|
364
|
+
class State # :nodoc:
|
365
|
+
attr_accessor :context, :file, :line, :binding
|
366
|
+
attr_accessor :frame_pos, :previous_line, :display
|
367
|
+
attr_accessor :interface, :commands, :processor
|
368
|
+
attr_accessor :exception
|
369
|
+
|
370
|
+
def initialize(processor=nil)
|
371
|
+
super()
|
372
|
+
@frame_pos = 0
|
373
|
+
@previous_line = nil
|
374
|
+
@proceed = false
|
375
|
+
@processor = processor
|
376
|
+
yield self
|
377
|
+
end
|
378
|
+
|
379
|
+
# FIXME: use delegate?
|
380
|
+
def errmsg(*args)
|
381
|
+
@interface.errmsg(*args)
|
382
|
+
end
|
383
|
+
|
384
|
+
def print(*args)
|
385
|
+
@interface.print(*args)
|
386
|
+
end
|
387
|
+
|
388
|
+
def confirm(*args)
|
389
|
+
@interface.confirm(*args)
|
390
|
+
end
|
391
|
+
|
392
|
+
def proceed?
|
393
|
+
@proceed
|
394
|
+
end
|
395
|
+
|
396
|
+
def proceed
|
397
|
+
@proceed = true
|
398
|
+
end
|
399
|
+
end
|
400
|
+
end
|
401
|
+
|
402
|
+
class ControlCommandProcessor < Processor # :nodoc:
|
403
|
+
def initialize(interface)
|
404
|
+
super()
|
405
|
+
@interface = interface
|
406
|
+
@debugger_context_was_dead = true # Assume we haven't started.
|
407
|
+
end
|
408
|
+
|
409
|
+
def process_commands(verbose=false)
|
410
|
+
control_cmds = Command.commands.select do |cmd|
|
411
|
+
cmd.allow_in_control
|
412
|
+
end
|
413
|
+
state = State.new(@interface, control_cmds)
|
414
|
+
@commands = control_cmds.map{|cmd| cmd.new(state) }
|
415
|
+
|
416
|
+
unless @debugger_context_was_dead
|
417
|
+
if Debugger.annotate.to_i > 2
|
418
|
+
aprint 'exited'
|
419
|
+
print "The program finished.\n"
|
420
|
+
end
|
421
|
+
@debugger_context_was_dead = true
|
422
|
+
end
|
423
|
+
|
424
|
+
while input = @interface.read_command(prompt(nil))
|
425
|
+
print "+#{input}" if verbose
|
426
|
+
catch(:debug_error) do
|
427
|
+
if cmd = @commands.find{|c| c.match(input) }
|
428
|
+
cmd.execute
|
429
|
+
else
|
430
|
+
errmsg "Unknown command\n"
|
431
|
+
end
|
432
|
+
end
|
433
|
+
end
|
434
|
+
rescue IOError, Errno::EPIPE
|
435
|
+
rescue Exception
|
436
|
+
print "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
437
|
+
print $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
438
|
+
ensure
|
439
|
+
@interface.close
|
440
|
+
end
|
441
|
+
|
442
|
+
# The prompt shown before reading a command.
|
443
|
+
# Note: have an unused 'context' parameter to match the local interface.
|
444
|
+
def prompt(context)
|
445
|
+
p = '(rdb:ctrl) '
|
446
|
+
p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
|
447
|
+
Debugger.annotate.to_i > 2
|
448
|
+
return p
|
449
|
+
end
|
450
|
+
|
451
|
+
class State # :nodoc:
|
452
|
+
attr_reader :commands, :interface
|
453
|
+
|
454
|
+
def initialize(interface, commands)
|
455
|
+
@interface = interface
|
456
|
+
@commands = commands
|
457
|
+
end
|
458
|
+
|
459
|
+
def proceed
|
460
|
+
end
|
461
|
+
|
462
|
+
def errmsg(*args)
|
463
|
+
@interface.print(*args)
|
464
|
+
end
|
465
|
+
|
466
|
+
def print(*args)
|
467
|
+
@interface.print(*args)
|
468
|
+
end
|
469
|
+
|
470
|
+
def confirm(*args)
|
471
|
+
'y'
|
472
|
+
end
|
473
|
+
|
474
|
+
def context
|
475
|
+
nil
|
476
|
+
end
|
477
|
+
|
478
|
+
def file
|
479
|
+
errmsg "No filename given.\n"
|
480
|
+
throw :debug_error
|
481
|
+
end
|
482
|
+
end # State
|
483
|
+
end
|
484
|
+
end
|