ruby-debug19 0.11.5
Sign up to get free protection for your applications and to get access to all the features.
- data/AUTHORS +9 -0
- data/LICENSE +23 -0
- data/bin/rdebug +415 -0
- data/cli/ruby-debug.rb +176 -0
- data/cli/ruby-debug/command.rb +228 -0
- data/cli/ruby-debug/commands/breakpoints.rb +153 -0
- data/cli/ruby-debug/commands/catchpoint.rb +55 -0
- data/cli/ruby-debug/commands/condition.rb +49 -0
- data/cli/ruby-debug/commands/continue.rb +38 -0
- data/cli/ruby-debug/commands/control.rb +107 -0
- data/cli/ruby-debug/commands/display.rb +120 -0
- data/cli/ruby-debug/commands/edit.rb +48 -0
- data/cli/ruby-debug/commands/enable.rb +202 -0
- data/cli/ruby-debug/commands/eval.rb +176 -0
- data/cli/ruby-debug/commands/finish.rb +42 -0
- data/cli/ruby-debug/commands/frame.rb +301 -0
- data/cli/ruby-debug/commands/help.rb +56 -0
- data/cli/ruby-debug/commands/info.rb +469 -0
- data/cli/ruby-debug/commands/irb.rb +123 -0
- data/cli/ruby-debug/commands/kill.rb +51 -0
- data/cli/ruby-debug/commands/list.rb +94 -0
- data/cli/ruby-debug/commands/method.rb +84 -0
- data/cli/ruby-debug/commands/quit.rb +39 -0
- data/cli/ruby-debug/commands/reload.rb +40 -0
- data/cli/ruby-debug/commands/save.rb +90 -0
- data/cli/ruby-debug/commands/set.rb +237 -0
- data/cli/ruby-debug/commands/show.rb +253 -0
- data/cli/ruby-debug/commands/source.rb +36 -0
- data/cli/ruby-debug/commands/stepping.rb +81 -0
- data/cli/ruby-debug/commands/threads.rb +189 -0
- data/cli/ruby-debug/commands/tmate.rb +36 -0
- data/cli/ruby-debug/commands/trace.rb +57 -0
- data/cli/ruby-debug/commands/variables.rb +199 -0
- data/cli/ruby-debug/debugger.rb +5 -0
- data/cli/ruby-debug/helper.rb +69 -0
- data/cli/ruby-debug/interface.rb +232 -0
- data/cli/ruby-debug/processor.rb +474 -0
- data/rdbg.rb +33 -0
- metadata +122 -0
data/AUTHORS
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Copyright (C) 2005 Kent Sibilev <ksibilev@yahoo.com>
|
2
|
+
All rights reserved.
|
3
|
+
*
|
4
|
+
Redistribution and use in source and binary forms, with or without
|
5
|
+
modification, are permitted provided that the following conditions
|
6
|
+
are met:
|
7
|
+
1. Redistributions of source code must retain the above copyright
|
8
|
+
notice, this list of conditions and the following disclaimer.
|
9
|
+
2. Redistributions in binary form must reproduce the above copyright
|
10
|
+
notice, this list of conditions and the following disclaimer in the
|
11
|
+
documentation and/or other materials provided with the distribution.
|
12
|
+
*
|
13
|
+
THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
|
14
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
15
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
16
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
|
17
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
18
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
19
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
20
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
21
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
22
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
23
|
+
SUCH DAMAGE.
|
data/bin/rdebug
ADDED
@@ -0,0 +1,415 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
#=== Summary
|
4
|
+
#
|
5
|
+
#A command-line front-end to the Ruby debugger, <tt>ruby-debug</tt>, the
|
6
|
+
#Fast Ruby Debugger.
|
7
|
+
#
|
8
|
+
#Command invocation:
|
9
|
+
#
|
10
|
+
# rdebug [options] [--] [script-options] ruby-script-to-debug
|
11
|
+
# rdebug [options] [script-options] [--client]
|
12
|
+
# rdebug [--version | --help]
|
13
|
+
#
|
14
|
+
#=== Options
|
15
|
+
#
|
16
|
+
#<tt>-A | --annotate</tt> <i>level</i>::
|
17
|
+
# Set gdb-style annotation to <i>level</i>, a number. Additional
|
18
|
+
# information is output automatically when program state is
|
19
|
+
# changed. This can be used by front-ends such as GNU Emacs to post
|
20
|
+
# this updated information without having to poll for it.
|
21
|
+
#
|
22
|
+
#<tt>--client</tt>::
|
23
|
+
# Connect to a remote debugger. Used with another rdebug invocation
|
24
|
+
# using <tt>--server</tt>. See also <tt>--host</tt> and
|
25
|
+
# <tt>--cport</tt> options
|
26
|
+
#
|
27
|
+
#<tt>--cport=</tt><i>port</i>::
|
28
|
+
# Use port <i>port</i> for access to debugger control.
|
29
|
+
#
|
30
|
+
#<tt>-d | --debug</tt>::
|
31
|
+
# Set $DEBUG true.
|
32
|
+
#
|
33
|
+
#<tt>--emacs</tt>::
|
34
|
+
# Activates full GNU Emacs mode. Is the equivalent of setting the
|
35
|
+
# options <tt>--emacs-basic --annotate=3 --no-stop --no-control
|
36
|
+
# --post-mortem</tt>.
|
37
|
+
#
|
38
|
+
#<tt>--emacs-basic</tt>::
|
39
|
+
# Activates GNU Emacs mode. Debugger prompts are prefaced with two
|
40
|
+
# octal 032 characters.
|
41
|
+
#
|
42
|
+
#<tt>-h | --host=</tt><i>host</i>::
|
43
|
+
# Use host name <i>host</i> for remote debugging.
|
44
|
+
#
|
45
|
+
#<tt>-I | --include</tt> <i>path</i>
|
46
|
+
# Add <i>path</i> to <tt>$LOAD_PATH</tt>
|
47
|
+
#
|
48
|
+
#<tt>-m | --post-mortem</tt>::
|
49
|
+
# Activate post-mortem mode.
|
50
|
+
#
|
51
|
+
#<tt>--no-control</tt>::
|
52
|
+
# Do not automatically start control thread.
|
53
|
+
#
|
54
|
+
#<tt>--no-stop</tt>::
|
55
|
+
# Do not stop when script is loaded.
|
56
|
+
#
|
57
|
+
#<tt>-p | --port=PORT</tt>::
|
58
|
+
# Host name used for remote debugging.
|
59
|
+
#
|
60
|
+
#<tt>-r | --require</tt><i>script</i>::
|
61
|
+
# Require the library, before executing your script.
|
62
|
+
#
|
63
|
+
#<tt>--script</tt> <i>file</i>::
|
64
|
+
# Run debugger script file <i>file</i>
|
65
|
+
#
|
66
|
+
#<tt>-x | --trace</tt>::
|
67
|
+
# Show lines before executing them.
|
68
|
+
#
|
69
|
+
#<tt>--no-quit</tt>::
|
70
|
+
# Do not quit when script terminates. Instead rerun the
|
71
|
+
# program.
|
72
|
+
#
|
73
|
+
#<tt>--version</tt>::
|
74
|
+
# Show the version number and exit.
|
75
|
+
#
|
76
|
+
#<tt>--verbose</tt>::
|
77
|
+
# Turn on verbose mode.
|
78
|
+
#
|
79
|
+
#<tt>--v</tt>::
|
80
|
+
# Print the version number, then turn on verbose mode if
|
81
|
+
# a script name is given. If no script name is given
|
82
|
+
# just exit after printing the version number.
|
83
|
+
#
|
84
|
+
#<tt>--nx</tt>::
|
85
|
+
# Don’t execute commands found in any initialization
|
86
|
+
# files, e.g. <tt>.rdebugrc</tt>.
|
87
|
+
#
|
88
|
+
#<tt>--keep-frame-binding</tt>::
|
89
|
+
# Keep frame bindings.
|
90
|
+
#
|
91
|
+
#<tt>--script=</tt><i>file</i>::
|
92
|
+
# Name of the script file to run
|
93
|
+
#
|
94
|
+
#<tt>-s | --server</tt>::
|
95
|
+
# Listen for remote connections. Another rdebug session
|
96
|
+
# accesses using the <tt>--client</tt> option. See also the
|
97
|
+
# <tt>--host</tt>, <tt>--port</tt> and <tt>--cport</tt> options
|
98
|
+
#
|
99
|
+
#<tt>-w | --wait</tt>::
|
100
|
+
# Wait for a client connection; implies <tt>-s</tt> option.
|
101
|
+
#
|
102
|
+
#<tt>--help</tt>::
|
103
|
+
# Show invocation help and exit.
|
104
|
+
|
105
|
+
require 'rubygems'
|
106
|
+
require 'optparse'
|
107
|
+
require 'ostruct'
|
108
|
+
require_relative '../cli/ruby-debug'
|
109
|
+
|
110
|
+
def debug_program(options)
|
111
|
+
# Make sure Ruby script syntax checks okay.
|
112
|
+
# Otherwise we get a load message that looks like rdebug has
|
113
|
+
# a problem.
|
114
|
+
output = `ruby -c "#{Debugger::PROG_SCRIPT}" 2>&1`
|
115
|
+
if $?.exitstatus != 0 and RUBY_PLATFORM !~ /mswin/
|
116
|
+
puts output
|
117
|
+
exit $?.exitstatus
|
118
|
+
end
|
119
|
+
print "\032\032starting\n" if Debugger.annotate and Debugger.annotate > 2
|
120
|
+
|
121
|
+
# Record where we are we can know if the call stack has been
|
122
|
+
# truncated or not.
|
123
|
+
Debugger.start_sentinal=caller(0)[1]
|
124
|
+
|
125
|
+
bt = Debugger.debug_load(Debugger::PROG_SCRIPT, options.stop, false)
|
126
|
+
if bt
|
127
|
+
if options.post_mortem
|
128
|
+
Debugger.handle_post_mortem(bt)
|
129
|
+
else
|
130
|
+
print bt.backtrace.map{|l| "\t#{l}"}.join("\n"), "\n"
|
131
|
+
print "Uncaught exception: #{bt}\n"
|
132
|
+
end
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
# Do a shell-like path lookup for prog_script and return the results.
|
137
|
+
# If we can't find anything return prog_script.
|
138
|
+
def whence_file(prog_script)
|
139
|
+
if prog_script.index(File::SEPARATOR)
|
140
|
+
# Don't search since this name has path separator components
|
141
|
+
return prog_script
|
142
|
+
end
|
143
|
+
for dirname in ENV['PATH'].split(File::PATH_SEPARATOR) do
|
144
|
+
prog_script_try = File.join(dirname, prog_script)
|
145
|
+
return prog_script_try if File.exist?(prog_script_try)
|
146
|
+
end
|
147
|
+
# Failure
|
148
|
+
return prog_script
|
149
|
+
end
|
150
|
+
|
151
|
+
options = OpenStruct.new(
|
152
|
+
'annotate' => Debugger.annotate,
|
153
|
+
'client' => false,
|
154
|
+
'control' => true,
|
155
|
+
'cport' => Debugger::PORT + 1,
|
156
|
+
'frame_bind' => false,
|
157
|
+
'host' => nil,
|
158
|
+
'quit' => true,
|
159
|
+
'no_rewrite_program' => false,
|
160
|
+
'stop' => true,
|
161
|
+
'nx' => false,
|
162
|
+
'port' => Debugger::PORT,
|
163
|
+
'post_mortem' => false,
|
164
|
+
'restart_script' => nil,
|
165
|
+
'script' => nil,
|
166
|
+
'server' => false,
|
167
|
+
'tracing' => false,
|
168
|
+
'verbose_long' => false,
|
169
|
+
'wait' => false
|
170
|
+
)
|
171
|
+
|
172
|
+
def process_options(options)
|
173
|
+
program = File.basename($0)
|
174
|
+
opts = OptionParser.new do |opts|
|
175
|
+
opts.banner = <<EOB
|
176
|
+
#{program} #{Debugger::VERSION}
|
177
|
+
Usage: #{program} [options] <script.rb> -- <script.rb parameters>
|
178
|
+
EOB
|
179
|
+
opts.separator ""
|
180
|
+
opts.separator "Options:"
|
181
|
+
opts.on("-A", "--annotate LEVEL", Integer, "Set annotation level") do
|
182
|
+
|annotate|
|
183
|
+
Debugger.annotate = annotate
|
184
|
+
end
|
185
|
+
opts.on("-c", "--client", "Connect to remote debugger") do
|
186
|
+
options.client = true
|
187
|
+
end
|
188
|
+
opts.on("--cport PORT", Integer, "Port used for control commands") do
|
189
|
+
|cport|
|
190
|
+
options.cport = cport
|
191
|
+
end
|
192
|
+
opts.on("-d", "--debug", "Set $DEBUG=true") {$DEBUG = true}
|
193
|
+
opts.on("--emacs LEVEL", Integer,
|
194
|
+
"Activates full Emacs support at annotation level LEVEL") do
|
195
|
+
|level|
|
196
|
+
Debugger.annotate = level.to_i
|
197
|
+
ENV['EMACS'] = '1'
|
198
|
+
ENV['COLUMNS'] = '120' if ENV['COLUMNS'].to_i < 120
|
199
|
+
options.control = false
|
200
|
+
options.quit = false
|
201
|
+
options.post_mortem = true
|
202
|
+
end
|
203
|
+
opts.on('--emacs-basic', 'Activates basic Emacs mode') do
|
204
|
+
ENV['EMACS'] = '1'
|
205
|
+
end
|
206
|
+
opts.on('-h', '--host HOST', 'Host name used for remote debugging') do
|
207
|
+
|host|
|
208
|
+
options.host = host
|
209
|
+
end
|
210
|
+
opts.on('-I', '--include PATH', String, 'Add PATH to $LOAD_PATH') do |path|
|
211
|
+
$LOAD_PATH.unshift(path)
|
212
|
+
end
|
213
|
+
opts.on('--keep-frame-binding', 'Keep frame bindings') do
|
214
|
+
options.frame_bind = true
|
215
|
+
end
|
216
|
+
opts.on('-m', '--post-mortem', 'Activate post-mortem mode') do
|
217
|
+
options.post_mortem = true
|
218
|
+
end
|
219
|
+
opts.on('--no-control', 'Do not automatically start control thread') do
|
220
|
+
options.control = false
|
221
|
+
end
|
222
|
+
opts.on('--no-quit', 'Do not quit when script finishes') do
|
223
|
+
options.quit = false
|
224
|
+
end
|
225
|
+
opts.on('--no-rewrite-program',
|
226
|
+
'Do not set $0 to the program being debugged') do
|
227
|
+
options.no_rewrite_program = true
|
228
|
+
end
|
229
|
+
opts.on('--no-stop', 'Do not stop when script is loaded') do
|
230
|
+
options.stop = false
|
231
|
+
end
|
232
|
+
opts.on('-nx', 'Not run debugger initialization files (e.g. .rdebugrc') do
|
233
|
+
options.nx = true
|
234
|
+
end
|
235
|
+
opts.on('-p', '--port PORT', Integer, 'Port used for remote debugging') do
|
236
|
+
|port|
|
237
|
+
options.port = port
|
238
|
+
end
|
239
|
+
opts.on('-r', '--require SCRIPT', String,
|
240
|
+
'Require the library, before executing your script') do |name|
|
241
|
+
if name == 'debug'
|
242
|
+
puts "ruby-debug is not compatible with Ruby's 'debug' library. This option is ignored."
|
243
|
+
else
|
244
|
+
require name
|
245
|
+
end
|
246
|
+
end
|
247
|
+
opts.on('--restart-script FILE', String,
|
248
|
+
'Name of the script file to run. Erased after read') do
|
249
|
+
|restart_script|
|
250
|
+
options.restart_script = restart_script
|
251
|
+
unless File.exists?(options.restart_script)
|
252
|
+
puts "Script file '#{options.restart_script}' is not found"
|
253
|
+
exit
|
254
|
+
end
|
255
|
+
end
|
256
|
+
opts.on('--script FILE', String, 'Name of the script file to run') do
|
257
|
+
|script|
|
258
|
+
options.script = script
|
259
|
+
unless File.exists?(options.script)
|
260
|
+
puts "Script file '#{options.script}' is not found"
|
261
|
+
exit
|
262
|
+
end
|
263
|
+
end
|
264
|
+
opts.on('-s', '--server', 'Listen for remote connections') do
|
265
|
+
options.server = true
|
266
|
+
end
|
267
|
+
opts.on('-w', '--wait', 'Wait for a client connection, implies -s option') do
|
268
|
+
options.wait = true
|
269
|
+
end
|
270
|
+
opts.on('-x', '--trace', 'Turn on line tracing') {options.tracing = true}
|
271
|
+
opts.separator ''
|
272
|
+
opts.separator 'Common options:'
|
273
|
+
opts.on_tail('--help', 'Show this message') do
|
274
|
+
puts opts
|
275
|
+
exit
|
276
|
+
end
|
277
|
+
opts.on_tail('--version',
|
278
|
+
'Print the version') do
|
279
|
+
puts "ruby-debug #{Debugger::VERSION}"
|
280
|
+
exit
|
281
|
+
end
|
282
|
+
opts.on('--verbose', 'Turn on verbose mode') do
|
283
|
+
$VERBOSE = true
|
284
|
+
options.verbose_long = true
|
285
|
+
end
|
286
|
+
opts.on_tail('-v',
|
287
|
+
'Print version number, then turn on verbose mode') do
|
288
|
+
puts "ruby-debug #{Debugger::VERSION}"
|
289
|
+
$VERBOSE = true
|
290
|
+
end
|
291
|
+
end
|
292
|
+
return opts
|
293
|
+
end
|
294
|
+
|
295
|
+
# What file is used for debugger startup commands.
|
296
|
+
unless defined?(OPTS_INITFILE)
|
297
|
+
if RUBY_PLATFORM =~ /mswin/
|
298
|
+
# Of course MS Windows has to be different
|
299
|
+
OPTS_INITFILE = 'rdbopt.ini'
|
300
|
+
HOME_DIR = (ENV['HOME'] ||
|
301
|
+
ENV['HOMEDRIVE'].to_s + ENV['HOMEPATH'].to_s).to_s
|
302
|
+
else
|
303
|
+
OPTS_INITFILE = '.rdboptrc'
|
304
|
+
HOME_DIR = ENV['HOME'].to_s
|
305
|
+
end
|
306
|
+
end
|
307
|
+
begin
|
308
|
+
initfile = File.join(HOME_DIR, OPTS_INITFILE)
|
309
|
+
eval(File.read(initfile)) if
|
310
|
+
File.exist?(initfile)
|
311
|
+
rescue
|
312
|
+
end
|
313
|
+
|
314
|
+
opts = process_options(options)
|
315
|
+
begin
|
316
|
+
if not defined? Debugger::ARGV
|
317
|
+
Debugger::ARGV = ARGV.clone
|
318
|
+
end
|
319
|
+
rdebug_path = File.expand_path($0)
|
320
|
+
if RUBY_PLATFORM =~ /mswin/
|
321
|
+
rdebug_path += '.cmd' unless rdebug_path =~ /\.cmd$/i
|
322
|
+
end
|
323
|
+
Debugger::RDEBUG_SCRIPT = rdebug_path
|
324
|
+
Debugger::RDEBUG_FILE = __FILE__
|
325
|
+
Debugger::INITIAL_DIR = Dir.pwd
|
326
|
+
opts.parse! ARGV
|
327
|
+
rescue StandardError => e
|
328
|
+
puts opts
|
329
|
+
puts
|
330
|
+
puts e.message
|
331
|
+
exit(-1)
|
332
|
+
end
|
333
|
+
|
334
|
+
if options.client
|
335
|
+
Debugger.start_client(options.host, options.port)
|
336
|
+
else
|
337
|
+
if ARGV.empty?
|
338
|
+
exit if $VERBOSE and not options.verbose_long
|
339
|
+
puts opts
|
340
|
+
puts
|
341
|
+
puts 'Must specify a script to run'
|
342
|
+
exit(-1)
|
343
|
+
end
|
344
|
+
|
345
|
+
# save script name
|
346
|
+
prog_script = ARGV.shift
|
347
|
+
prog_script = whence_file(prog_script) unless File.exist?(prog_script)
|
348
|
+
Debugger::PROG_SCRIPT = File.expand_path prog_script
|
349
|
+
|
350
|
+
# install interruption handler
|
351
|
+
trap('INT') { Debugger.interrupt_last }
|
352
|
+
|
353
|
+
# set options
|
354
|
+
Debugger.wait_connection = options.wait
|
355
|
+
Debugger.keep_frame_binding = options.frame_bind
|
356
|
+
|
357
|
+
if options.server
|
358
|
+
# start remote mode
|
359
|
+
Debugger.start_remote(options.host, [options.port, options.cport],
|
360
|
+
options.post_mortem) do
|
361
|
+
# load initrc script
|
362
|
+
Debugger.run_init_script(StringIO.new) unless options.nx
|
363
|
+
end
|
364
|
+
debug_program(options)
|
365
|
+
else
|
366
|
+
# Set up trace hook for debugger
|
367
|
+
Debugger.start
|
368
|
+
# start control thread
|
369
|
+
Debugger.start_control(options.host, options.cport) if options.control
|
370
|
+
|
371
|
+
# load initrc script (e.g. .rdebugrc)
|
372
|
+
Debugger.run_init_script(StringIO.new) unless options.nx
|
373
|
+
|
374
|
+
# run restore-settings startup script if specified
|
375
|
+
if options.restart_script
|
376
|
+
require 'fileutils'
|
377
|
+
Debugger.run_script(options.restart_script)
|
378
|
+
FileUtils.rm(options.restart_script)
|
379
|
+
end
|
380
|
+
|
381
|
+
# run startup script if specified
|
382
|
+
if options.script
|
383
|
+
Debugger.run_script(options.script)
|
384
|
+
end
|
385
|
+
|
386
|
+
# activate post-mortem
|
387
|
+
Debugger.post_mortem if options.post_mortem
|
388
|
+
options.stop = false if options.tracing
|
389
|
+
Debugger.tracing = options.tracing
|
390
|
+
|
391
|
+
if !options.quit
|
392
|
+
if Debugger.started?
|
393
|
+
until Debugger.stop do end
|
394
|
+
end
|
395
|
+
begin
|
396
|
+
debug_program(options)
|
397
|
+
rescue SyntaxError
|
398
|
+
puts $!.backtrace.map{|l| "\t#{l}"}.join("\n")
|
399
|
+
puts "Uncaught Syntax Error\n"
|
400
|
+
rescue
|
401
|
+
print $!.backtrace.map{|l| "\t#{l}"}.join("\n"), "\n"
|
402
|
+
print "Uncaught exception: #{$!}\n"
|
403
|
+
end
|
404
|
+
print "The program finished.\n" unless
|
405
|
+
Debugger.annotate.to_i > 1 # annotate has its own way
|
406
|
+
interface = Debugger::LocalInterface.new
|
407
|
+
# Not sure if ControlCommandProcessor is really the right
|
408
|
+
# thing to use. CommandProcessor requires a state.
|
409
|
+
processor = Debugger::ControlCommandProcessor.new(interface)
|
410
|
+
processor.process_commands
|
411
|
+
else
|
412
|
+
debug_program(options)
|
413
|
+
end
|
414
|
+
end
|
415
|
+
end
|
data/cli/ruby-debug.rb
ADDED
@@ -0,0 +1,176 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'stringio'
|
3
|
+
require 'socket'
|
4
|
+
require 'thread'
|
5
|
+
require 'ruby-debug-base'
|
6
|
+
require_relative 'ruby-debug/processor'
|
7
|
+
|
8
|
+
module Debugger
|
9
|
+
self.handler = CommandProcessor.new
|
10
|
+
|
11
|
+
# the port number used for remote debugging
|
12
|
+
PORT = 8989 unless defined?(PORT)
|
13
|
+
|
14
|
+
# What file is used for debugger startup commands.
|
15
|
+
unless defined?(INITFILE)
|
16
|
+
if RUBY_PLATFORM =~ /mswin/
|
17
|
+
# Of course MS Windows has to be different
|
18
|
+
INITFILE = 'rdebug.ini'
|
19
|
+
HOME_DIR = (ENV['HOME'] ||
|
20
|
+
ENV['HOMEDRIVE'].to_s + ENV['HOMEPATH'].to_s).to_s
|
21
|
+
else
|
22
|
+
INITFILE = '.rdebugrc'
|
23
|
+
HOME_DIR = ENV['HOME'].to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# gdb-style annotation mode. Used in GNU Emacs interface
|
29
|
+
attr_accessor :annotate
|
30
|
+
|
31
|
+
# in remote mode, wait for the remote connection
|
32
|
+
attr_accessor :wait_connection
|
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
|
37
|
+
|
38
|
+
attr_reader :thread, :control_thread
|
39
|
+
|
40
|
+
def interface=(value) # :nodoc:
|
41
|
+
handler.interface = value
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Starts a remote debugger.
|
46
|
+
#
|
47
|
+
def start_remote(host = nil, port = PORT, post_mortem = false)
|
48
|
+
return if @thread
|
49
|
+
return if started?
|
50
|
+
|
51
|
+
self.interface = nil
|
52
|
+
start
|
53
|
+
self.post_mortem if post_mortem
|
54
|
+
|
55
|
+
if port.kind_of?(Array)
|
56
|
+
cmd_port, ctrl_port = port
|
57
|
+
else
|
58
|
+
cmd_port, ctrl_port = port, port + 1
|
59
|
+
end
|
60
|
+
|
61
|
+
start_control(host, ctrl_port)
|
62
|
+
|
63
|
+
yield if block_given?
|
64
|
+
|
65
|
+
mutex = Mutex.new
|
66
|
+
proceed = ConditionVariable.new
|
67
|
+
|
68
|
+
@thread = DebugThread.new do
|
69
|
+
server = TCPServer.new(host, cmd_port)
|
70
|
+
while (session = server.accept)
|
71
|
+
self.interface = RemoteInterface.new(session)
|
72
|
+
if wait_connection
|
73
|
+
mutex.synchronize do
|
74
|
+
proceed.signal
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if wait_connection
|
80
|
+
mutex.synchronize do
|
81
|
+
proceed.wait(mutex)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
alias start_server start_remote
|
86
|
+
|
87
|
+
def start_control(host = nil, ctrl_port = PORT + 1) # :nodoc:
|
88
|
+
raise "Debugger is not started" unless started?
|
89
|
+
return if defined?(@control_thread) && @control_thread
|
90
|
+
@control_thread = DebugThread.new do
|
91
|
+
server = TCPServer.new(host, ctrl_port)
|
92
|
+
while (session = server.accept)
|
93
|
+
interface = RemoteInterface.new(session)
|
94
|
+
processor = ControlCommandProcessor.new(interface)
|
95
|
+
processor.process_commands
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
#
|
101
|
+
# Connects to the remote debugger
|
102
|
+
#
|
103
|
+
def start_client(host = 'localhost', port = PORT)
|
104
|
+
require "socket"
|
105
|
+
interface = Debugger::LocalInterface.new
|
106
|
+
socket = TCPSocket.new(host, port)
|
107
|
+
puts "Connected."
|
108
|
+
|
109
|
+
catch(:exit) do
|
110
|
+
while (line = socket.gets)
|
111
|
+
case line
|
112
|
+
when /^PROMPT (.*)$/
|
113
|
+
input = interface.read_command($1)
|
114
|
+
throw :exit unless input
|
115
|
+
socket.puts input
|
116
|
+
when /^CONFIRM (.*)$/
|
117
|
+
input = interface.confirm($1)
|
118
|
+
throw :exit unless input
|
119
|
+
socket.puts input
|
120
|
+
else
|
121
|
+
print line
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
socket.close
|
126
|
+
end
|
127
|
+
|
128
|
+
# Runs normal debugger initialization scripts
|
129
|
+
# Reads and executes the commands from init file (if any) in the
|
130
|
+
# current working directory. This is only done if the current
|
131
|
+
# directory is different from your home directory. Thus, you can
|
132
|
+
# have more than one init file, one generic in your home directory,
|
133
|
+
# and another, specific to the program you are debugging, in the
|
134
|
+
# directory where you invoke ruby-debug.
|
135
|
+
def run_init_script(out = handler.interface)
|
136
|
+
cwd_script_file = File.expand_path(File.join(".", INITFILE))
|
137
|
+
run_script(cwd_script_file, out) if File.exists?(cwd_script_file)
|
138
|
+
|
139
|
+
home_script_file = File.expand_path(File.join(HOME_DIR, INITFILE))
|
140
|
+
run_script(home_script_file, out) if File.exists?(home_script_file) and
|
141
|
+
cwd_script_file != home_script_file
|
142
|
+
end
|
143
|
+
|
144
|
+
#
|
145
|
+
# Runs a script file
|
146
|
+
#
|
147
|
+
def run_script(file, out = handler.interface, verbose=false)
|
148
|
+
interface = ScriptInterface.new(File.expand_path(file), out)
|
149
|
+
processor = ControlCommandProcessor.new(interface)
|
150
|
+
processor.process_commands(verbose)
|
151
|
+
end
|
152
|
+
end
|
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
|