ruby-debug 0.10.3 → 0.10.4
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 +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
|