ruby-debug19 0.11.5
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/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
@@ -0,0 +1,228 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'columnize'
|
3
|
+
require_relative 'helper'
|
4
|
+
|
5
|
+
module Debugger
|
6
|
+
RUBY_DEBUG_DIR = File.expand_path(File.dirname(__FILE__)) unless
|
7
|
+
defined?(RUBY_DEBUG_DIR)
|
8
|
+
|
9
|
+
class Command # :nodoc:
|
10
|
+
SubcmdStruct=Struct.new(:name, :min, :short_help, :long_help) unless
|
11
|
+
defined?(SubcmdStruct)
|
12
|
+
|
13
|
+
include Columnize
|
14
|
+
|
15
|
+
# Find param in subcmds. param id downcased and can be abbreviated
|
16
|
+
# to the minimum length listed in the subcommands
|
17
|
+
def find(subcmds, param)
|
18
|
+
param.downcase!
|
19
|
+
for try_subcmd in subcmds do
|
20
|
+
if (param.size >= try_subcmd.min) and
|
21
|
+
(try_subcmd.name[0..param.size-1] == param)
|
22
|
+
return try_subcmd
|
23
|
+
end
|
24
|
+
end
|
25
|
+
return nil
|
26
|
+
end
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def commands
|
30
|
+
@commands ||= []
|
31
|
+
end
|
32
|
+
|
33
|
+
DEF_OPTIONS = {
|
34
|
+
:allow_in_control => false,
|
35
|
+
:allow_in_post_mortem => true,
|
36
|
+
:event => true,
|
37
|
+
:always_run => 0,
|
38
|
+
:unknown => false,
|
39
|
+
:need_context => false,
|
40
|
+
} unless defined?(DEF_OPTIONS)
|
41
|
+
|
42
|
+
def inherited(klass)
|
43
|
+
DEF_OPTIONS.each do |o, v|
|
44
|
+
klass.options[o] = v if klass.options[o].nil?
|
45
|
+
end
|
46
|
+
commands << klass
|
47
|
+
end
|
48
|
+
|
49
|
+
def load_commands
|
50
|
+
Dir[File.join(Debugger.const_get(:RUBY_DEBUG_DIR), 'commands', '*')].each do |file|
|
51
|
+
require file if file =~ /\.rb$/
|
52
|
+
end
|
53
|
+
Debugger.constants.grep(/Functions$/).map { |name| Debugger.const_get(name) }.each do |mod|
|
54
|
+
include mod
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_missing(meth, *args, &block)
|
59
|
+
if meth.to_s =~ /^(.+?)=$/
|
60
|
+
@options[$1.intern] = args.first
|
61
|
+
else
|
62
|
+
if @options.has_key?(meth)
|
63
|
+
@options[meth]
|
64
|
+
else
|
65
|
+
super
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def options
|
71
|
+
@options ||= {}
|
72
|
+
end
|
73
|
+
|
74
|
+
def settings_map
|
75
|
+
@@settings_map ||= {}
|
76
|
+
end
|
77
|
+
private :settings_map
|
78
|
+
|
79
|
+
def settings
|
80
|
+
unless true and defined? @settings and @settings
|
81
|
+
@settings = Object.new
|
82
|
+
map = settings_map
|
83
|
+
c = class << @settings; self end
|
84
|
+
if c.respond_to?(:funcall)
|
85
|
+
c.funcall(:define_method, :[]) do |name|
|
86
|
+
raise "No such setting #{name}" unless map.has_key?(name)
|
87
|
+
map[name][:getter].call
|
88
|
+
end
|
89
|
+
else
|
90
|
+
c.send(:define_method, :[]) do |name|
|
91
|
+
raise "No such setting #{name}" unless map.has_key?(name)
|
92
|
+
map[name][:getter].call
|
93
|
+
end
|
94
|
+
end
|
95
|
+
c = class << @settings; self end
|
96
|
+
if c.respond_to?(:funcall)
|
97
|
+
c.funcall(:define_method, :[]=) do |name, value|
|
98
|
+
raise "No such setting #{name}" unless map.has_key?(name)
|
99
|
+
map[name][:setter].call(value)
|
100
|
+
end
|
101
|
+
else
|
102
|
+
c.send(:define_method, :[]=) do |name, value|
|
103
|
+
raise "No such setting #{name}" unless map.has_key?(name)
|
104
|
+
map[name][:setter].call(value)
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
@settings
|
109
|
+
end
|
110
|
+
|
111
|
+
def register_setting_var(name, default)
|
112
|
+
var_name = "@@#{name}"
|
113
|
+
class_variable_set(var_name, default)
|
114
|
+
register_setting_get(name) { class_variable_get(var_name) }
|
115
|
+
register_setting_set(name) { |value| class_variable_set(var_name, value) }
|
116
|
+
end
|
117
|
+
|
118
|
+
def register_setting_get(name, &block)
|
119
|
+
settings_map[name] ||= {}
|
120
|
+
settings_map[name][:getter] = block
|
121
|
+
end
|
122
|
+
|
123
|
+
def register_setting_set(name, &block)
|
124
|
+
settings_map[name] ||= {}
|
125
|
+
settings_map[name][:setter] = block
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
register_setting_var(:basename, false) # use basename in showing files?
|
130
|
+
register_setting_var(:callstyle, :last)
|
131
|
+
register_setting_var(:debuggertesting, false)
|
132
|
+
register_setting_var(:force_stepping, false)
|
133
|
+
register_setting_var(:full_path, true)
|
134
|
+
register_setting_var(:listsize, 10) # number of lines in list command
|
135
|
+
register_setting_var(:stack_trace_on_error, false)
|
136
|
+
register_setting_var(:tracing_plus, false) # different linetrace lines?
|
137
|
+
|
138
|
+
# width of line output. Use COLUMNS value if it exists and is
|
139
|
+
# not too rediculously large.
|
140
|
+
width = ENV['COLUMNS'].to_i
|
141
|
+
width = 80 unless width > 10
|
142
|
+
register_setting_var(:width, width)
|
143
|
+
|
144
|
+
if not defined? Debugger::ARGV
|
145
|
+
Debugger::ARGV = ARGV.clone
|
146
|
+
end
|
147
|
+
register_setting_var(:argv, Debugger::ARGV)
|
148
|
+
|
149
|
+
def initialize(state)
|
150
|
+
@state = state
|
151
|
+
end
|
152
|
+
|
153
|
+
def match(input)
|
154
|
+
@match = regexp.match(input)
|
155
|
+
end
|
156
|
+
|
157
|
+
protected
|
158
|
+
|
159
|
+
# FIXME: use delegate?
|
160
|
+
def errmsg(*args)
|
161
|
+
@state.errmsg(*args)
|
162
|
+
end
|
163
|
+
|
164
|
+
def print(*args)
|
165
|
+
@state.print(*args)
|
166
|
+
end
|
167
|
+
|
168
|
+
def confirm(msg)
|
169
|
+
@state.confirm(msg) == 'y'
|
170
|
+
end
|
171
|
+
|
172
|
+
def debug_eval(str, b = get_binding)
|
173
|
+
begin
|
174
|
+
val = eval(str, b)
|
175
|
+
rescue StandardError, ScriptError => e
|
176
|
+
if Command.settings[:stack_trace_on_error]
|
177
|
+
at = eval("caller(1)", b)
|
178
|
+
print "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
|
179
|
+
for i in at
|
180
|
+
print "\tfrom %s\n", i
|
181
|
+
end
|
182
|
+
else
|
183
|
+
print "#{e.class} Exception: #{e.message}\n"
|
184
|
+
end
|
185
|
+
throw :debug_error
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
def debug_silent_eval(str)
|
190
|
+
begin
|
191
|
+
eval(str, get_binding)
|
192
|
+
rescue StandardError, ScriptError
|
193
|
+
nil
|
194
|
+
end
|
195
|
+
end
|
196
|
+
|
197
|
+
def get_binding
|
198
|
+
@state.context.frame_binding(@state.frame_pos)
|
199
|
+
end
|
200
|
+
|
201
|
+
def line_at(file, line)
|
202
|
+
Debugger.line_at(file, line)
|
203
|
+
end
|
204
|
+
|
205
|
+
def get_context(thnum)
|
206
|
+
Debugger.contexts.find{|c| c.thnum == thnum}
|
207
|
+
end
|
208
|
+
end
|
209
|
+
|
210
|
+
Command.load_commands
|
211
|
+
|
212
|
+
# Returns setting object.
|
213
|
+
# Use Debugger.settings[] and Debugger.settings[]= methods to query and set
|
214
|
+
# debugger settings. These settings are available:
|
215
|
+
#
|
216
|
+
# - :autolist - automatically calls 'list' command on breakpoint
|
217
|
+
# - :autoeval - evaluates input in the current binding if it's not recognized as a debugger command
|
218
|
+
# - :autoirb - automatically calls 'irb' command on breakpoint
|
219
|
+
# - :stack_trace_on_error - shows full stack trace if eval command results with an exception
|
220
|
+
# - :frame_full_path - displays full paths when showing frame stack
|
221
|
+
# - :frame_class_names - displays method's class name when showing frame stack
|
222
|
+
# - :reload_source_on_change - makes 'list' command to always display up-to-date source code
|
223
|
+
# - :force_stepping - stepping command asways move to the new line
|
224
|
+
#
|
225
|
+
def self.settings
|
226
|
+
Command.settings
|
227
|
+
end
|
228
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
# Implements debugger "break" command.
|
4
|
+
class AddBreakpoint < Command
|
5
|
+
self.allow_in_control = true
|
6
|
+
|
7
|
+
def regexp
|
8
|
+
/ ^\s*
|
9
|
+
b(?:reak)?
|
10
|
+
(?: \s+ #{Position_regexp})? \s*
|
11
|
+
(?: \s+ (.*))? \s*
|
12
|
+
$
|
13
|
+
/x
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
if @match[1]
|
18
|
+
line, _, _, expr = @match.captures
|
19
|
+
else
|
20
|
+
_, file, line, expr = @match.captures
|
21
|
+
end
|
22
|
+
if expr
|
23
|
+
if expr !~ /^\s*if\s+(.+)/
|
24
|
+
if file or line
|
25
|
+
errmsg "Expecting 'if' in breakpoint condition; got: #{expr}.\n"
|
26
|
+
else
|
27
|
+
errmsg "Invalid breakpoint location: #{expr}.\n"
|
28
|
+
end
|
29
|
+
return
|
30
|
+
else
|
31
|
+
expr = $1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
brkpt_filename = nil
|
36
|
+
if file.nil?
|
37
|
+
unless @state.context
|
38
|
+
errmsg "We are not in a state that has an associated file.\n"
|
39
|
+
return
|
40
|
+
end
|
41
|
+
brkpt_filename = @state.file
|
42
|
+
file = File.basename(@state.file)
|
43
|
+
if line.nil?
|
44
|
+
# Set breakpoint at current line
|
45
|
+
line = @state.line.to_s
|
46
|
+
end
|
47
|
+
elsif line !~ /^\d+$/
|
48
|
+
# See if "line" is a method/function name
|
49
|
+
klass = debug_silent_eval(file)
|
50
|
+
if klass && klass.kind_of?(Module)
|
51
|
+
class_name = klass.name if klass
|
52
|
+
else
|
53
|
+
errmsg "Unknown class #{file}.\n"
|
54
|
+
throw :debug_error
|
55
|
+
end
|
56
|
+
else
|
57
|
+
# FIXME: This should be done in LineCache.
|
58
|
+
file = File.expand_path(file) if file.index(File::SEPARATOR) || \
|
59
|
+
File::ALT_SEPARATOR && file.index(File::ALT_SEPARATOR)
|
60
|
+
brkpt_filename = file
|
61
|
+
end
|
62
|
+
|
63
|
+
if line =~ /^\d+$/
|
64
|
+
line = line.to_i
|
65
|
+
if LineCache.cache(brkpt_filename, Command.settings[:reload_source_on_change])
|
66
|
+
last_line = LineCache.size(brkpt_filename)
|
67
|
+
if line > last_line
|
68
|
+
errmsg("There are only %d lines in file \"%s\".\n", last_line, file)
|
69
|
+
return
|
70
|
+
end
|
71
|
+
unless LineCache.trace_line_numbers(brkpt_filename).member?(line)
|
72
|
+
errmsg("Line %d is not a stopping point in file \"%s\".\n", line, file)
|
73
|
+
return
|
74
|
+
end
|
75
|
+
else
|
76
|
+
errmsg("No source file named %s\n" % file)
|
77
|
+
return unless confirm("Set breakpoint anyway? (y/n) ")
|
78
|
+
end
|
79
|
+
|
80
|
+
unless @state.context
|
81
|
+
errmsg "We are not in a state we can add breakpoints.\n"
|
82
|
+
return
|
83
|
+
end
|
84
|
+
b = Debugger.add_breakpoint brkpt_filename, line, expr
|
85
|
+
print "Breakpoint %d file %s, line %s\n", b.id, brkpt_filename, line.to_s
|
86
|
+
unless syntax_valid?(expr)
|
87
|
+
errmsg("Expression \"#{expr}\" syntactically incorrect; breakpoint disabled.\n")
|
88
|
+
b.enabled = false
|
89
|
+
end
|
90
|
+
else
|
91
|
+
method = line.intern.id2name
|
92
|
+
b = Debugger.add_breakpoint class_name, method, expr
|
93
|
+
print "Breakpoint %d at %s::%s\n", b.id, class_name, method.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class << self
|
98
|
+
def help_command
|
99
|
+
'break'
|
100
|
+
end
|
101
|
+
|
102
|
+
def help(cmd)
|
103
|
+
%{
|
104
|
+
b[reak] file:line [if expr]
|
105
|
+
b[reak] class(.|#)method [if expr]
|
106
|
+
\tset breakpoint to some position, (optionally) if expr == true
|
107
|
+
}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Implements debugger "delete" command.
|
113
|
+
class DeleteBreakpointCommand < Command
|
114
|
+
self.allow_in_control = true
|
115
|
+
|
116
|
+
def regexp
|
117
|
+
/^\s *del(?:ete)? (?:\s+(.*))?$/ix
|
118
|
+
end
|
119
|
+
|
120
|
+
def execute
|
121
|
+
unless @state.context
|
122
|
+
errmsg "We are not in a state we can delete breakpoints.\n"
|
123
|
+
return
|
124
|
+
end
|
125
|
+
brkpts = @match[1]
|
126
|
+
unless brkpts
|
127
|
+
if confirm("Delete all breakpoints? (y or n) ")
|
128
|
+
Debugger.breakpoints.clear
|
129
|
+
end
|
130
|
+
else
|
131
|
+
brkpts.split(/[ \t]+/).each do |pos|
|
132
|
+
pos = get_int(pos, "Delete", 1)
|
133
|
+
return unless pos
|
134
|
+
unless Debugger.remove_breakpoint(pos)
|
135
|
+
errmsg "No breakpoint number %d\n", pos
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class << self
|
142
|
+
def help_command
|
143
|
+
'delete'
|
144
|
+
end
|
145
|
+
|
146
|
+
def help(cmd)
|
147
|
+
%{
|
148
|
+
del[ete][ nnn...]\tdelete some or all breakpoints
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module Debugger
|
2
|
+
class CatchCommand < Command # :nodoc:
|
3
|
+
self.allow_in_control = true
|
4
|
+
|
5
|
+
def regexp
|
6
|
+
/^\s* cat(?:ch)?
|
7
|
+
(?:\s+ (\S+))?
|
8
|
+
(?:\s+ (off))? \s* $/ix
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
excn = @match[1]
|
13
|
+
if not excn
|
14
|
+
# No args given.
|
15
|
+
info_catch
|
16
|
+
elsif not @match[2]
|
17
|
+
# One arg given.
|
18
|
+
if 'off' == excn
|
19
|
+
Debugger.catchpoints.clear if
|
20
|
+
confirm("Delete all catchpoints? (y or n) ")
|
21
|
+
else
|
22
|
+
binding = @state.context ? get_binding : TOPLEVEL_BINDING
|
23
|
+
unless debug_eval("#{excn}.is_a?(Class)", binding)
|
24
|
+
print "Warning #{excn} is not known to be a Class\n"
|
25
|
+
end
|
26
|
+
Debugger.add_catchpoint(excn)
|
27
|
+
print "Catch exception %s.\n", excn
|
28
|
+
end
|
29
|
+
elsif @match[2] != 'off'
|
30
|
+
errmsg "Off expected. Got %s\n", @match[2]
|
31
|
+
elsif Debugger.catchpoints.member?(excn)
|
32
|
+
Debugger.catchpoints.delete(excn)
|
33
|
+
print "Catch for exception %s removed.\n", excn
|
34
|
+
else
|
35
|
+
errmsg "Catch for exception %s not found.\n", excn
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class << self
|
40
|
+
def help_command
|
41
|
+
'catch'
|
42
|
+
end
|
43
|
+
|
44
|
+
def help(cmd)
|
45
|
+
%{
|
46
|
+
cat[ch]\t\tsame as "info catch"
|
47
|
+
cat[ch] <exception-name> [on|off]
|
48
|
+
\tIntercept <exception-name> when there would otherwise be no handler.
|
49
|
+
\tWith an "on" or "off", turn handling the exception on or off.
|
50
|
+
cat[ch] off\tdelete all catchpoints
|
51
|
+
}
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
class ConditionCommand < Command # :nodoc:
|
4
|
+
|
5
|
+
def regexp
|
6
|
+
/^\s* cond(?:ition)? (?:\s+(\d+)\s*(.*))?$/ix
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute
|
10
|
+
if not @match[1]
|
11
|
+
errmsg "\"condition\" must be followed a breakpoint number and expression\n"
|
12
|
+
else
|
13
|
+
breakpoints = Debugger.breakpoints.sort_by{|b| b.id }
|
14
|
+
largest = breakpoints.inject(0) do |largest, b|
|
15
|
+
largest = b.id if b.id > largest
|
16
|
+
end
|
17
|
+
if 0 == largest
|
18
|
+
print "No breakpoints have been set.\n"
|
19
|
+
return
|
20
|
+
end
|
21
|
+
pos = get_int(@match[1], "Condition", 1, largest)
|
22
|
+
return unless pos
|
23
|
+
breakpoints.each do |b|
|
24
|
+
if b.id == pos
|
25
|
+
b.expr = @match[2].empty? ? nil : @match[2]
|
26
|
+
break
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class << self
|
34
|
+
def help_command
|
35
|
+
'condition'
|
36
|
+
end
|
37
|
+
|
38
|
+
def help(cmd)
|
39
|
+
%{
|
40
|
+
Condition breakpoint-number expression
|
41
|
+
Specify breakpoint number N to break only if COND is true.
|
42
|
+
N is an integer and COND is an expression to be evaluated whenever
|
43
|
+
breakpoint N is reached. If the empty string is used, the condition is removed.
|
44
|
+
}
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
end # module Debugger
|