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
@@ -0,0 +1,176 @@
|
|
1
|
+
module Debugger
|
2
|
+
module EvalFunctions # :nodoc:
|
3
|
+
def run_with_binding
|
4
|
+
binding = @state.context ? get_binding : TOPLEVEL_BINDING
|
5
|
+
$__dbg_interface = @state.interface
|
6
|
+
eval(<<-EOC, binding)
|
7
|
+
__dbg_verbose_save=$VERBOSE; $VERBOSE=false
|
8
|
+
def dbg_print(*args)
|
9
|
+
$__dbg_interface.print(*args)
|
10
|
+
end
|
11
|
+
remove_method :puts if self.respond_to?(:puts) &&
|
12
|
+
defined?(remove_method)
|
13
|
+
def dbg_puts(*args)
|
14
|
+
$__dbg_interface.print(*args)
|
15
|
+
$__dbg_interface.print("\n")
|
16
|
+
end
|
17
|
+
$VERBOSE=__dbg_verbose_save
|
18
|
+
EOC
|
19
|
+
yield binding
|
20
|
+
ensure
|
21
|
+
$__dbg_interface = nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class EvalCommand < Command # :nodoc:
|
26
|
+
self.allow_in_control = true
|
27
|
+
|
28
|
+
register_setting_get(:autoeval) do
|
29
|
+
EvalCommand.unknown
|
30
|
+
end
|
31
|
+
register_setting_set(:autoeval) do |value|
|
32
|
+
EvalCommand.unknown = value
|
33
|
+
end
|
34
|
+
|
35
|
+
def match(input)
|
36
|
+
@input = input
|
37
|
+
super
|
38
|
+
end
|
39
|
+
|
40
|
+
def regexp
|
41
|
+
/^\s*(p|e(?:val)?)\s+/
|
42
|
+
end
|
43
|
+
|
44
|
+
def execute
|
45
|
+
expr = @match ? @match.post_match : @input
|
46
|
+
run_with_binding do |b|
|
47
|
+
print "%s\n", debug_eval(expr, b).inspect
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
class << self
|
52
|
+
def help_command
|
53
|
+
%w|p eval|
|
54
|
+
end
|
55
|
+
|
56
|
+
def help(cmd)
|
57
|
+
if cmd == 'p'
|
58
|
+
%{
|
59
|
+
p expression\tevaluate expression and print its value
|
60
|
+
}
|
61
|
+
else
|
62
|
+
%{
|
63
|
+
e[val] expression\tevaluate expression and print its value,
|
64
|
+
\t\t\talias for p.
|
65
|
+
* NOTE - to turn on autoeval, use 'set autoeval'
|
66
|
+
}
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
class PPCommand < Command # :nodoc:
|
73
|
+
self.allow_in_control = true
|
74
|
+
|
75
|
+
def regexp
|
76
|
+
/^\s*pp\s+/
|
77
|
+
end
|
78
|
+
|
79
|
+
def execute
|
80
|
+
out = StringIO.new
|
81
|
+
run_with_binding do |b|
|
82
|
+
PP.pp(debug_eval(@match.post_match, b), out)
|
83
|
+
end
|
84
|
+
print out.string
|
85
|
+
rescue
|
86
|
+
out.puts $!.message
|
87
|
+
end
|
88
|
+
|
89
|
+
class << self
|
90
|
+
def help_command
|
91
|
+
'pp'
|
92
|
+
end
|
93
|
+
|
94
|
+
def help(cmd)
|
95
|
+
%{
|
96
|
+
pp expression\tevaluate expression and pretty-print its value
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
class PutLCommand < Command # :nodoc:
|
103
|
+
self.allow_in_control = true
|
104
|
+
|
105
|
+
def regexp
|
106
|
+
/^\s*putl\s+/
|
107
|
+
end
|
108
|
+
|
109
|
+
def execute
|
110
|
+
out = StringIO.new
|
111
|
+
run_with_binding do |b|
|
112
|
+
vals = debug_eval(@match.post_match, b)
|
113
|
+
if vals.is_a?(Array)
|
114
|
+
vals = vals.map{|item| item.to_s}
|
115
|
+
print "%s\n", columnize(vals, self.class.settings[:width])
|
116
|
+
else
|
117
|
+
PP.pp(vals, out)
|
118
|
+
print out.string
|
119
|
+
end
|
120
|
+
end
|
121
|
+
rescue
|
122
|
+
out.puts $!.message
|
123
|
+
end
|
124
|
+
|
125
|
+
class << self
|
126
|
+
def help_command
|
127
|
+
'putl'
|
128
|
+
end
|
129
|
+
|
130
|
+
def help(cmd)
|
131
|
+
%{
|
132
|
+
putl expression\t\tevaluate expression, an array, and columnize its value
|
133
|
+
}
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
class PSCommand < Command # :nodoc:
|
139
|
+
self.allow_in_control = true
|
140
|
+
|
141
|
+
include EvalFunctions
|
142
|
+
|
143
|
+
def regexp
|
144
|
+
/^\s*ps\s+/
|
145
|
+
end
|
146
|
+
|
147
|
+
def execute
|
148
|
+
out = StringIO.new
|
149
|
+
run_with_binding do |b|
|
150
|
+
vals = debug_eval(@match.post_match, b)
|
151
|
+
if vals.is_a?(Array)
|
152
|
+
vals = vals.map{|item| item.to_s}
|
153
|
+
print "%s\n", columnize(vals.sort!, self.class.settings[:width])
|
154
|
+
else
|
155
|
+
PP.pp(vals, out)
|
156
|
+
print out.string
|
157
|
+
end
|
158
|
+
end
|
159
|
+
rescue
|
160
|
+
out.puts $!.message
|
161
|
+
end
|
162
|
+
|
163
|
+
class << self
|
164
|
+
def help_command
|
165
|
+
'ps'
|
166
|
+
end
|
167
|
+
|
168
|
+
def help(cmd)
|
169
|
+
%{
|
170
|
+
ps expression\tevaluate expression, an array, sort, and columnize its value
|
171
|
+
}
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Debugger
|
2
|
+
# Implements the debugger 'finish' command.
|
3
|
+
class FinishCommand < Command
|
4
|
+
self.allow_in_post_mortem = false
|
5
|
+
self.need_context = true
|
6
|
+
|
7
|
+
def regexp
|
8
|
+
/^\s*fin(?:ish)? (?:\s+(.*))?$/x
|
9
|
+
end
|
10
|
+
|
11
|
+
def execute
|
12
|
+
max_frame = @state.context.stack_size - @state.frame_pos
|
13
|
+
if !@match[1] or @match[1].empty?
|
14
|
+
frame_pos = @state.frame_pos
|
15
|
+
else
|
16
|
+
frame_pos = get_int(@match[1], "Finish", 0, max_frame-1, 0)
|
17
|
+
return nil unless frame_pos
|
18
|
+
end
|
19
|
+
@state.context.stop_frame = frame_pos
|
20
|
+
@state.frame_pos = 0
|
21
|
+
@state.proceed
|
22
|
+
end
|
23
|
+
|
24
|
+
class << self
|
25
|
+
def help_command
|
26
|
+
'finish'
|
27
|
+
end
|
28
|
+
|
29
|
+
def help(cmd)
|
30
|
+
%{
|
31
|
+
fin[ish] [frame-number]\tExecute until selected stack frame returns.
|
32
|
+
|
33
|
+
If no frame number is given, we run until the currently selected frame
|
34
|
+
returns. The currently selected frame starts out the most-recent
|
35
|
+
frame or 0 if no frame positioning (e.g "up", "down" or "frame") has
|
36
|
+
been performed. If a frame number is given we run until that frame
|
37
|
+
returns.
|
38
|
+
}
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
module Debugger
|
2
|
+
# Mix-in module to assist in command parsing.
|
3
|
+
module FrameFunctions # :nodoc:
|
4
|
+
def adjust_frame(frame_pos, absolute, context=@state.context)
|
5
|
+
@state.frame_pos = 0 if context != @state.context
|
6
|
+
if absolute
|
7
|
+
if frame_pos < 0
|
8
|
+
abs_frame_pos = context.stack_size + frame_pos
|
9
|
+
else
|
10
|
+
abs_frame_pos = frame_pos
|
11
|
+
end
|
12
|
+
else
|
13
|
+
abs_frame_pos = @state.frame_pos + frame_pos
|
14
|
+
end
|
15
|
+
|
16
|
+
if abs_frame_pos >= context.stack_size then
|
17
|
+
errmsg "Adjusting would put us beyond the oldest (initial) frame.\n"
|
18
|
+
return
|
19
|
+
elsif abs_frame_pos < 0 then
|
20
|
+
errmsg "Adjusting would put us beyond the newest (innermost) frame.\n"
|
21
|
+
return
|
22
|
+
end
|
23
|
+
if @state.frame_pos != abs_frame_pos then
|
24
|
+
@state.previous_line = nil
|
25
|
+
@state.frame_pos = abs_frame_pos
|
26
|
+
end
|
27
|
+
|
28
|
+
@state.file = context.frame_file(@state.frame_pos)
|
29
|
+
@state.line = context.frame_line(@state.frame_pos)
|
30
|
+
|
31
|
+
print_frame(@state.frame_pos, true)
|
32
|
+
end
|
33
|
+
|
34
|
+
def get_frame_call(prefix, pos, context)
|
35
|
+
id = context.frame_method(pos)
|
36
|
+
klass = context.frame_class(pos)
|
37
|
+
call_str = ""
|
38
|
+
if id
|
39
|
+
args = context.frame_args(pos)
|
40
|
+
locals = context.frame_locals(pos)
|
41
|
+
if Command.settings[:callstyle] != :short && klass
|
42
|
+
if Command.settings[:callstyle] == :tracked
|
43
|
+
arg_info = context.frame_args_info(pos)
|
44
|
+
end
|
45
|
+
call_str << "#{klass}."
|
46
|
+
end
|
47
|
+
call_str << id.id2name
|
48
|
+
if args.any?
|
49
|
+
call_str << "("
|
50
|
+
args.each_with_index do |name, i|
|
51
|
+
case Command.settings[:callstyle]
|
52
|
+
when :short
|
53
|
+
call_str += "%s, " % [name]
|
54
|
+
when :last
|
55
|
+
klass = locals[name].class
|
56
|
+
if klass.inspect.size > 20+3
|
57
|
+
klass = klass.inspect[0..20]+"..."
|
58
|
+
end
|
59
|
+
call_str += "%s#%s, " % [name, klass]
|
60
|
+
when :tracked
|
61
|
+
if arg_info && arg_info.size > i
|
62
|
+
call_str += "#{name}: #{arg_info[i].inspect}, "
|
63
|
+
else
|
64
|
+
call_str += "%s, " % name
|
65
|
+
end
|
66
|
+
end
|
67
|
+
if call_str.size > self.class.settings[:width] - prefix.size
|
68
|
+
# Strip off trailing ', ' if any but add stuff for later trunc
|
69
|
+
call_str[-2..-1] = ",...XX"
|
70
|
+
break
|
71
|
+
end
|
72
|
+
end
|
73
|
+
call_str[-2..-1] = ")" # Strip off trailing ', ' if any
|
74
|
+
end
|
75
|
+
end
|
76
|
+
return call_str
|
77
|
+
end
|
78
|
+
|
79
|
+
def print_frame(pos, adjust = false, context=@state.context)
|
80
|
+
file = context.frame_file(pos)
|
81
|
+
line = context.frame_line(pos)
|
82
|
+
klass = context.frame_class(pos)
|
83
|
+
|
84
|
+
unless Command.settings[:full_path]
|
85
|
+
path_components = file.split(/[\\\/]/)
|
86
|
+
if path_components.size > 3
|
87
|
+
path_components[0...-3] = '...'
|
88
|
+
file = path_components.join(File::ALT_SEPARATOR || File::SEPARATOR)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
frame_num = "#%d " % pos
|
93
|
+
call_str = get_frame_call(frame_num, pos, context)
|
94
|
+
file_line = "at line %s:%d\n" % [CommandProcessor.canonic_file(file), line]
|
95
|
+
print frame_num
|
96
|
+
unless call_str.empty?
|
97
|
+
print call_str
|
98
|
+
print ' '
|
99
|
+
if call_str.size + frame_num.size + file_line.size > self.class.settings[:width]
|
100
|
+
print "\n "
|
101
|
+
end
|
102
|
+
end
|
103
|
+
print file_line
|
104
|
+
if ENV['EMACS'] && adjust
|
105
|
+
fmt = (Debugger.annotate.to_i > 1 ?
|
106
|
+
"\032\032source %s:%d\n" : "\032\032%s:%d\n")
|
107
|
+
print fmt % [CommandProcessor.canonic_file(file), line]
|
108
|
+
end
|
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
|
+
|
152
|
+
end
|
153
|
+
|
154
|
+
# Implements debugger "where" or "backtrace" command.
|
155
|
+
class WhereCommand < Command
|
156
|
+
def regexp
|
157
|
+
/^\s*(?:w(?:here)?|bt|backtrace)$/
|
158
|
+
end
|
159
|
+
|
160
|
+
def execute
|
161
|
+
(0...@state.context.stack_size).each do |idx|
|
162
|
+
if idx == @state.frame_pos
|
163
|
+
print "--> "
|
164
|
+
else
|
165
|
+
print " "
|
166
|
+
end
|
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"
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
class << self
|
176
|
+
def help_command
|
177
|
+
%w|where backtrace|
|
178
|
+
end
|
179
|
+
|
180
|
+
def help(cmd)
|
181
|
+
s = if cmd == 'where'
|
182
|
+
%{
|
183
|
+
w[here]\tdisplay stack frames
|
184
|
+
}
|
185
|
+
else
|
186
|
+
%{
|
187
|
+
bt|backtrace\t\talias for where - display stack frames
|
188
|
+
}
|
189
|
+
end
|
190
|
+
s += %{
|
191
|
+
Print the entire stack frame. Each frame is numbered, the most recent
|
192
|
+
frame is 0. frame number can be referred to in the "frame" command;
|
193
|
+
"up" and "down" add or subtract respectively to frame numbers shown.
|
194
|
+
The position of the current frame is marked with -->. }
|
195
|
+
end
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
class UpCommand < Command # :nodoc:
|
200
|
+
def regexp
|
201
|
+
/^\s* u(?:p)? (?:\s+(.*))?$/x
|
202
|
+
end
|
203
|
+
|
204
|
+
def execute
|
205
|
+
pos = get_int(@match[1], "Up")
|
206
|
+
return unless pos
|
207
|
+
adjust_frame(pos, false)
|
208
|
+
end
|
209
|
+
|
210
|
+
class << self
|
211
|
+
def help_command
|
212
|
+
'up'
|
213
|
+
end
|
214
|
+
|
215
|
+
def help(cmd)
|
216
|
+
%{
|
217
|
+
up[count]\tmove to higher frame
|
218
|
+
}
|
219
|
+
end
|
220
|
+
end
|
221
|
+
end
|
222
|
+
|
223
|
+
class DownCommand < Command # :nodoc:
|
224
|
+
def regexp
|
225
|
+
/^\s* down (?:\s+(.*))? .*$/x
|
226
|
+
end
|
227
|
+
|
228
|
+
def execute
|
229
|
+
pos = get_int(@match[1], "Down")
|
230
|
+
return unless pos
|
231
|
+
adjust_frame(-pos, false)
|
232
|
+
end
|
233
|
+
|
234
|
+
class << self
|
235
|
+
def help_command
|
236
|
+
'down'
|
237
|
+
end
|
238
|
+
|
239
|
+
def help(cmd)
|
240
|
+
%{
|
241
|
+
down[count]\tmove to lower frame
|
242
|
+
}
|
243
|
+
end
|
244
|
+
end
|
245
|
+
end
|
246
|
+
|
247
|
+
class FrameCommand < Command # :nodoc:
|
248
|
+
def regexp
|
249
|
+
/ ^\s*
|
250
|
+
f(?:rame)?
|
251
|
+
(?: \s+ (\S+))? \s*
|
252
|
+
(?: thread \s+ (.*))? \s*
|
253
|
+
$/x
|
254
|
+
end
|
255
|
+
|
256
|
+
def execute
|
257
|
+
if not @match[1]
|
258
|
+
pos = 0
|
259
|
+
else
|
260
|
+
pos = get_int(@match[1], "Frame")
|
261
|
+
return unless pos
|
262
|
+
end
|
263
|
+
if @match[2]
|
264
|
+
context = parse_thread_num('frame', @match[2])
|
265
|
+
unless context
|
266
|
+
errmsg "Thread #{@match[2]} doesn't exist.\n"
|
267
|
+
return
|
268
|
+
end
|
269
|
+
else
|
270
|
+
context = @state.context
|
271
|
+
end
|
272
|
+
adjust_frame(pos, true, context)
|
273
|
+
end
|
274
|
+
|
275
|
+
class << self
|
276
|
+
def help_command
|
277
|
+
'frame'
|
278
|
+
end
|
279
|
+
|
280
|
+
def help(cmd)
|
281
|
+
%{
|
282
|
+
f[rame] [frame-number [thread thread-number]]
|
283
|
+
Move the current frame to the specified frame number, or the
|
284
|
+
0 if no frame-number has been given.
|
285
|
+
|
286
|
+
A negative number indicates position from the other end. So
|
287
|
+
'frame -1' moves to the oldest frame, and 'frame 0' moves to
|
288
|
+
the newest frame.
|
289
|
+
|
290
|
+
Without an argument, the command prints the current stack
|
291
|
+
frame. Since the current position is redisplayed, it may trigger a
|
292
|
+
resyncronization if there is a front end also watching over
|
293
|
+
things.
|
294
|
+
|
295
|
+
If a thread number is given then we set the context for evaluating
|
296
|
+
expressions to that frame of that thread.
|
297
|
+
}
|
298
|
+
end
|
299
|
+
end
|
300
|
+
end
|
301
|
+
end
|