ruby-debug193 0.0.1
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 +10 -0
- data/LICENSE +23 -0
- data/bin/rdebug +398 -0
- data/cli/ruby-debug.rb +173 -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/jump.rb +66 -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 +144 -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
|