trepanning 0.1.0 → 0.1.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/.gitignore +4 -0
- data/ChangeLog +1279 -235
- data/Makefile +13 -0
- data/NEWS +30 -0
- data/Rakefile +50 -14
- data/app/.gitignore +1 -0
- data/app/breakpoint.rb +7 -2
- data/app/brkptmgr.rb +12 -0
- data/app/cmd_parse.citrus +167 -0
- data/app/cmd_parse.kpeg +221 -0
- data/app/cmd_parse.rb +201 -0
- data/app/cmd_parser.rb +1914 -0
- data/app/complete.rb +79 -0
- data/app/condition.rb +1 -1
- data/app/core.rb +7 -11
- data/app/default.rb +1 -1
- data/app/disassemble.rb +3 -2
- data/app/file.rb +12 -36
- data/app/frame.rb +3 -2
- data/app/irb.rb +9 -5
- data/app/iseq.rb +46 -0
- data/app/options.rb +6 -30
- data/app/run.rb +5 -2
- data/app/util.rb +1 -2
- data/app/yarv.rb +11 -1
- data/bin/.gitignore +1 -0
- data/bin/trepan +6 -6
- data/data/.gitignore +1 -0
- data/interface/.gitignore +1 -0
- data/interface/base_intf.rb +9 -5
- data/interface/comcodes.rb +10 -8
- data/interface/user.rb +76 -17
- data/io/.gitignore +1 -0
- data/io/input.rb +39 -15
- data/io/tcpclient.rb +7 -1
- data/io/tcpfns.rb +5 -3
- data/io/tcpserver.rb +13 -10
- data/lib/.gitignore +1 -0
- data/lib/trepanning.rb +50 -13
- data/processor/.gitignore +1 -0
- data/processor/Makefile +7 -0
- data/processor/breakpoint.rb +7 -2
- data/processor/command/.gitignore +1 -0
- data/processor/command/Makefile +7 -0
- data/processor/command/alias.rb +2 -2
- data/processor/command/backtrace.rb +4 -0
- data/processor/command/base/cmd.rb +45 -2
- data/processor/command/base/subcmd.rb +4 -2
- data/processor/command/base/submgr.rb +23 -19
- data/processor/command/base/subsubcmd.rb +23 -1
- data/processor/command/base/subsubmgr.rb +13 -0
- data/processor/command/break.rb +34 -29
- data/processor/command/complete.rb +37 -0
- data/processor/command/condition.rb +2 -0
- data/processor/command/continue.rb +15 -18
- data/processor/command/disassemble.rb +5 -0
- data/processor/command/down.rb +1 -1
- data/processor/command/eval.rb +70 -0
- data/processor/command/exit.rb +4 -1
- data/processor/command/finish.rb +6 -4
- data/processor/command/frame.rb +6 -3
- data/processor/command/help.rb +97 -54
- data/processor/command/help/.gitignore +1 -0
- data/processor/command/help/README +10 -0
- data/processor/command/help/command.txt +48 -0
- data/processor/command/help/filename.txt +40 -0
- data/processor/command/help/location.txt +37 -0
- data/processor/command/info_subcmd/.gitignore +1 -0
- data/processor/command/info_subcmd/breakpoints.rb +9 -9
- data/processor/command/info_subcmd/{file.rb → files.rb} +92 -27
- data/processor/command/info_subcmd/frame.rb +41 -15
- data/processor/command/info_subcmd/iseq.rb +39 -17
- data/processor/command/info_subcmd/program.rb +2 -8
- data/processor/command/info_subcmd/registers.rb +12 -10
- data/processor/command/info_subcmd/registers_subcmd/.gitignore +1 -0
- data/processor/command/info_subcmd/ruby.rb +60 -0
- data/processor/command/irb.rb +26 -3
- data/processor/command/kill.rb +21 -10
- data/processor/command/list.rb +1 -1
- data/processor/command/macro.rb +37 -23
- data/processor/command/pr.rb +1 -1
- data/processor/command/reload.rb +4 -0
- data/processor/command/reload_subcmd/.gitignore +1 -0
- data/processor/command/restart.rb +9 -9
- data/processor/command/save.rb +29 -36
- data/processor/command/set_subcmd/.gitignore +1 -0
- data/processor/command/set_subcmd/auto_subcmd/.gitignore +1 -0
- data/processor/command/set_subcmd/confirm.rb +23 -0
- data/processor/command/set_subcmd/debug_subcmd/.gitignore +1 -0
- data/processor/command/set_subcmd/different.rb +2 -0
- data/processor/command/set_subcmd/events.rb +2 -0
- data/processor/command/set_subcmd/max.rb +9 -12
- data/processor/command/set_subcmd/max_subcmd/.gitignore +1 -0
- data/processor/command/set_subcmd/substitute_subcmd/.gitignore +1 -0
- data/processor/command/set_subcmd/trace.rb +7 -13
- data/processor/command/set_subcmd/trace_subcmd/.gitignore +1 -0
- data/processor/command/set_subcmd/trace_subcmd/buffer.rb +12 -27
- data/processor/command/set_subcmd/trace_subcmd/print.rb +10 -8
- data/processor/command/set_subcmd/trace_subcmd/var.rb +6 -10
- data/processor/command/show.rb +12 -1
- data/processor/command/show_subcmd/.gitignore +1 -0
- data/processor/command/show_subcmd/alias.rb +11 -15
- data/processor/command/show_subcmd/auto_subcmd/.gitignore +1 -0
- data/processor/command/show_subcmd/basename.rb +1 -9
- data/processor/command/show_subcmd/confirm.rb +25 -0
- data/processor/command/show_subcmd/debug_subcmd/.gitignore +1 -0
- data/processor/command/show_subcmd/macro.rb +32 -14
- data/processor/command/show_subcmd/max_subcmd/.gitignore +1 -0
- data/processor/command/show_subcmd/trace_subcmd/.gitignore +1 -0
- data/processor/command/show_subcmd/trace_subcmd/buffer.rb +11 -31
- data/processor/command/show_subcmd/trace_subcmd/print.rb +4 -20
- data/processor/command/source.rb +7 -1
- data/processor/command/up.rb +7 -4
- data/processor/default.rb +3 -1
- data/processor/eval.rb +13 -0
- data/processor/eventbuf.rb +3 -2
- data/processor/frame.rb +19 -0
- data/processor/help.rb +20 -0
- data/processor/load_cmds.rb +143 -24
- data/processor/location.rb +61 -10
- data/processor/main.rb +30 -11
- data/processor/mock.rb +5 -3
- data/processor/msg.rb +17 -0
- data/processor/running.rb +1 -1
- data/processor/subcmd.rb +3 -2
- data/processor/validate.rb +173 -185
- data/sample/.gitignore +1 -0
- data/sample/list-terminal-colors.rb +139 -0
- data/sample/rocky-dot-trepanrc +14 -0
- data/sample/rocky-trepan-colors.rb +47 -0
- data/test/Makefile +7 -0
- data/test/data/.gitignore +1 -0
- data/test/data/debugger-stop.cmd +3 -0
- data/test/data/debugger-stop.right +5 -0
- data/test/data/fname-with-blank.right +0 -3
- data/test/data/quit.right +0 -1
- data/test/data/quit2.cmd +6 -0
- data/test/data/quit2.right +3 -0
- data/test/data/testing.cmd +1 -0
- data/test/example/.gitignore +1 -0
- data/test/example/debugger-stop.rb +14 -0
- data/test/functional/.gitignore +2 -0
- data/test/functional/fn_helper.rb +7 -9
- data/test/functional/test-break-long.rb +7 -7
- data/test/functional/test-break.rb +7 -7
- data/test/functional/test-condition.rb +4 -4
- data/test/functional/test-delete.rb +6 -5
- data/test/functional/test-eval.rb +115 -0
- data/test/functional/test-raise.rb +1 -1
- data/test/functional/test-return.rb +1 -1
- data/test/integration/.gitignore +2 -0
- data/test/integration/helper.rb +6 -3
- data/test/integration/test-debugger-stop.rb +22 -0
- data/test/integration/test-quit.rb +8 -0
- data/test/unit/.gitignore +1 -0
- data/test/unit/Makefile +7 -0
- data/test/unit/test-app-brkpt.rb +0 -1
- data/test/unit/test-app-cmd_parse.rb +107 -0
- data/test/unit/test-app-cmd_parser.rb +22 -0
- data/test/unit/test-app-complete.rb +38 -0
- data/test/unit/test-app-condition.rb +20 -0
- data/test/unit/test-app-iseq.rb +31 -0
- data/test/unit/test-app-options.rb +9 -1
- data/test/unit/test-app-util.rb +0 -1
- data/test/unit/test-base-cmd.rb +46 -0
- data/test/unit/test-base-subcmd.rb +11 -2
- data/test/unit/test-base-submgr.rb +23 -0
- data/test/unit/test-base-subsubcmd.rb +20 -0
- data/test/unit/test-cmd-break.rb +22 -23
- data/test/unit/test-cmd-help.rb +4 -0
- data/test/unit/test-completion.rb +43 -0
- data/test/unit/test-io-tcpclient.rb +3 -2
- data/test/unit/test-proc-load_cmds.rb +10 -1
- data/test/unit/test-proc-location.rb +39 -0
- data/test/unit/test-proc-main.rb +1 -1
- data/test/unit/test-proc-validate.rb +47 -31
- data/trepanning.gemspec +45 -0
- metadata +247 -179
- data/app/core.rb-consider +0 -198
- data/test/functional/tmp/b3.rb +0 -5
- data/test/functional/tmp/immediate-bug1.rb +0 -9
data/processor/location.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# Copyright (C) 2010, 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
2
2
|
require 'linecache'
|
3
|
+
require 'pathname' # For cleanpath
|
3
4
|
require_relative 'msg'
|
4
5
|
require_relative '../app/frame'
|
5
6
|
class Trepan
|
@@ -19,7 +20,18 @@ class Trepan
|
|
19
20
|
# For now we want resolved filenames
|
20
21
|
@settings[:basename] ? File.basename(filename) :
|
21
22
|
# Cache this?
|
22
|
-
Pathname.new(filename).cleanpath.to_s
|
23
|
+
File.expand_path(Pathname.new(filename).cleanpath.to_s)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Return the text to the current source line.
|
27
|
+
# FIXME: loc_and_text should call this rather than the other
|
28
|
+
# way around.
|
29
|
+
def current_source_text
|
30
|
+
opts = {:reload_on_change => @reload_on_change}
|
31
|
+
junk1, junk2, text, found_line =
|
32
|
+
loc_and_text('', frame, frame.source_location[0],
|
33
|
+
frame.source_container, opts)
|
34
|
+
text
|
23
35
|
end
|
24
36
|
|
25
37
|
def resolve_file_with_dir(path_suffix)
|
@@ -41,11 +53,11 @@ class Trepan
|
|
41
53
|
|
42
54
|
# Get line +line_number+ from file named +filename+. Return ''
|
43
55
|
# if there was a problem. Leading blanks are stripped off.
|
44
|
-
def line_at(filename, line_number
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
56
|
+
def line_at(filename, line_number,
|
57
|
+
opts = {
|
58
|
+
:reload_on_change => @reload_on_change,
|
59
|
+
:output => @settings[:highlight]
|
60
|
+
})
|
49
61
|
line = LineCache::getline(filename, line_number, opts)
|
50
62
|
|
51
63
|
unless line
|
@@ -61,12 +73,16 @@ class Trepan
|
|
61
73
|
line ? line.lstrip.chomp : line
|
62
74
|
end
|
63
75
|
|
64
|
-
def loc_and_text(loc, frame, line_no, source_container
|
76
|
+
def loc_and_text(loc, frame, line_no, source_container,
|
77
|
+
opts = {
|
78
|
+
:reload_on_change => @reload_on_change,
|
79
|
+
:output => @settings[:highlight]
|
80
|
+
})
|
65
81
|
found_line = true
|
66
82
|
## FIXME: condition is too long.
|
67
83
|
if source_container[0] == 'string' && frame.iseq && frame.iseq.eval_source
|
68
84
|
file = LineCache::map_iseq(frame.iseq)
|
69
|
-
text = LineCache::getline(frame.iseq, line_no)
|
85
|
+
text = LineCache::getline(frame.iseq, line_no, opts)
|
70
86
|
loc += " remapped #{canonic_file(file)}:#{line_no}"
|
71
87
|
elsif source_container[0] != 'file'
|
72
88
|
via = loc
|
@@ -78,7 +94,7 @@ class Trepan
|
|
78
94
|
line_no = frame.source_location[0]
|
79
95
|
filename = source_container[1]
|
80
96
|
loc += " via #{canonic_file(filename)}:#{line_no}"
|
81
|
-
text = line_at(filename, line_no)
|
97
|
+
text = line_at(filename, line_no, opts)
|
82
98
|
found_line = false
|
83
99
|
end
|
84
100
|
else
|
@@ -88,7 +104,7 @@ class Trepan
|
|
88
104
|
loc += " remapped #{canonic_file(map_file)}:#{map_line}"
|
89
105
|
end
|
90
106
|
|
91
|
-
text = line_at(container, line_no)
|
107
|
+
text = line_at(container, line_no, opts)
|
92
108
|
end
|
93
109
|
[loc, line_no, text, found_line]
|
94
110
|
end
|
@@ -154,3 +170,38 @@ class Trepan
|
|
154
170
|
|
155
171
|
end
|
156
172
|
end
|
173
|
+
|
174
|
+
if __FILE__ == $0 && caller.size == 0 && ARGV.size > 0
|
175
|
+
# Demo it.
|
176
|
+
require 'thread_frame'
|
177
|
+
require_relative 'frame'
|
178
|
+
require_relative '../app/mock'
|
179
|
+
require_relative 'main' # Have to include before defining CmdProcessor!
|
180
|
+
# FIXME
|
181
|
+
class Trepan::CmdProcessor
|
182
|
+
def errmsg(msg)
|
183
|
+
puts msg
|
184
|
+
end
|
185
|
+
def print_location
|
186
|
+
puts "#{@frame.source_container} #{frame.source_location[0]}"
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
proc = Trepan::CmdProcessor.new(Trepan::MockCore.new())
|
191
|
+
proc.instance_variable_set('@settings', {})
|
192
|
+
proc.frame_initialize
|
193
|
+
proc.frame_setup(RubyVM::ThreadFrame.current)
|
194
|
+
proc.frame_initialize
|
195
|
+
|
196
|
+
proc.location_initialize
|
197
|
+
puts proc.canonic_file(__FILE__)
|
198
|
+
proc.instance_variable_set('@settings', {:basename => true})
|
199
|
+
puts proc.canonic_file(__FILE__)
|
200
|
+
puts proc.current_source_text
|
201
|
+
xx = eval <<-END
|
202
|
+
proc.frame_initialize
|
203
|
+
proc.frame_setup(RubyVM::ThreadFrame.current)
|
204
|
+
proc.location_initialize
|
205
|
+
proc.current_source_text
|
206
|
+
END
|
207
|
+
end
|
data/processor/main.rb
CHANGED
@@ -3,7 +3,6 @@
|
|
3
3
|
# command class and debugger command objects are pulled in from here.
|
4
4
|
|
5
5
|
require 'set'
|
6
|
-
require 'pathname' # For cleanpath
|
7
6
|
|
8
7
|
%w(default breakpoint display eventbuf eval load_cmds location frame hook msg
|
9
8
|
running validate).each do
|
@@ -33,6 +32,8 @@ class Trepan
|
|
33
32
|
# next stop while settings is the default
|
34
33
|
# value to use.
|
35
34
|
attr_accessor :event # Stop event. Same as @core.event
|
35
|
+
attr_reader :intf # Current interface
|
36
|
+
# Trepan::Core instance)
|
36
37
|
attr_accessor :leave_cmd_loop # Commands set this to signal to leave
|
37
38
|
# the command loop (which often continues to
|
38
39
|
# run the debugged program).
|
@@ -63,6 +64,8 @@ class Trepan
|
|
63
64
|
'c-call' => 'C>',
|
64
65
|
'c-return' => '<C',
|
65
66
|
'call' => '->',
|
67
|
+
'send' => '=>',
|
68
|
+
'leave' => '<=',
|
66
69
|
'class' => '::',
|
67
70
|
'coverage' => '[]',
|
68
71
|
'debugger-call' => ':o',
|
@@ -75,6 +78,7 @@ class Trepan
|
|
75
78
|
'unknown' => '?!',
|
76
79
|
'vm' => 'VM',
|
77
80
|
'vm-insn' => '..',
|
81
|
+
'yield' => '<>',
|
78
82
|
}
|
79
83
|
# These events are important enough event that we always want to
|
80
84
|
# stop on them.
|
@@ -138,6 +142,10 @@ class Trepan
|
|
138
142
|
|
139
143
|
end
|
140
144
|
|
145
|
+
def finalize
|
146
|
+
breakpoint_finalize
|
147
|
+
end
|
148
|
+
|
141
149
|
# Check that we meed the criteria that cmd specifies it needs
|
142
150
|
def ok_for_running(cmd, name, nargs)
|
143
151
|
# TODO check execution_set against execution status.
|
@@ -170,13 +178,14 @@ class Trepan
|
|
170
178
|
# Run one debugger command. True is returned if we want to quit.
|
171
179
|
def process_command_and_quit?()
|
172
180
|
intf_size = @dbgr.intf.size
|
173
|
-
intf = @dbgr.intf[-1]
|
174
|
-
return true if intf.input_eof? && intf_size == 1
|
175
|
-
while intf_size > 1 ||
|
181
|
+
@intf = @dbgr.intf[-1]
|
182
|
+
return true if @intf.input_eof? && intf_size == 1
|
183
|
+
while intf_size > 1 || !@intf.input_eof?
|
176
184
|
begin
|
177
185
|
@current_command =
|
178
186
|
if @cmd_queue.empty?
|
179
|
-
|
187
|
+
# Leave trailing blanks on for the "complete" command
|
188
|
+
read_command.chomp
|
180
189
|
else
|
181
190
|
@cmd_queue.shift
|
182
191
|
end
|
@@ -193,7 +202,7 @@ class Trepan
|
|
193
202
|
if intf_size > 1
|
194
203
|
@dbgr.intf.pop
|
195
204
|
intf_size = @dbgr.intf.size
|
196
|
-
intf = @dbgr.intf[-1]
|
205
|
+
@intf = @dbgr.intf[-1]
|
197
206
|
@last_command = nil
|
198
207
|
print_location
|
199
208
|
else
|
@@ -205,6 +214,9 @@ class Trepan
|
|
205
214
|
end
|
206
215
|
end
|
207
216
|
run_command(@current_command)
|
217
|
+
|
218
|
+
# Save it to the history.
|
219
|
+
@intf.history_io.puts @last_command if @last_command && @intf.history_io
|
208
220
|
end
|
209
221
|
|
210
222
|
# This is the main entry point.
|
@@ -240,7 +252,12 @@ class Trepan
|
|
240
252
|
@dbgr.stop
|
241
253
|
raise
|
242
254
|
rescue Exception => exc
|
243
|
-
errmsg
|
255
|
+
# If we are inside the script interface errmsg may fail.
|
256
|
+
begin
|
257
|
+
errmsg("Internal debugger error: #{exc.inspect}")
|
258
|
+
rescue
|
259
|
+
$stderr.puts "Internal debugger error: #{exc.inspect}"
|
260
|
+
end
|
244
261
|
exception_dump(exc, @settings[:debugexcept], $!.backtrace)
|
245
262
|
end
|
246
263
|
end
|
@@ -269,12 +286,13 @@ class Trepan
|
|
269
286
|
macro_cmd_name = args[0]
|
270
287
|
return false if args.size == 0
|
271
288
|
break unless @macros.member?(macro_cmd_name)
|
272
|
-
current_command = @macros[macro_cmd_name].call(*args[1..-1])
|
273
|
-
msg current_command if settings[:debugmacro]
|
274
|
-
# FIXME: should handle nested Array as a new command.
|
289
|
+
current_command = @macros[macro_cmd_name][0].call(*args[1..-1])
|
290
|
+
msg current_command.inspect if settings[:debugmacro]
|
275
291
|
if current_command.is_a?(Array) &&
|
276
292
|
current_command.all? {|val| val.is_a?(String)}
|
277
|
-
args = current_command
|
293
|
+
args = (first=current_command.shift).split
|
294
|
+
@cmd_queue += current_command
|
295
|
+
current_command = first
|
278
296
|
elsif current_command.is_a?(String)
|
279
297
|
args = current_command.split
|
280
298
|
else
|
@@ -337,6 +355,7 @@ if __FILE__ == $0
|
|
337
355
|
cmdproc.errmsg('Whoa!')
|
338
356
|
cmds = cmdproc.commands
|
339
357
|
p cmdproc.aliases
|
358
|
+
p cmdproc.commands.keys.sort
|
340
359
|
cmd_name, cmd_obj = cmds.first
|
341
360
|
puts cmd_obj.class.const_get(:HELP)
|
342
361
|
puts cmd_obj.class.const_get(:SHORT_HELP)
|
data/processor/mock.rb
CHANGED
@@ -70,13 +70,13 @@ module MockDebugger
|
|
70
70
|
def cmd.confirm(prompt, default)
|
71
71
|
true
|
72
72
|
end
|
73
|
-
def cmd.errmsg(message)
|
73
|
+
def cmd.errmsg(message, opts={})
|
74
74
|
puts "Error: #{message}"
|
75
75
|
end
|
76
|
-
def cmd.msg(message)
|
76
|
+
def cmd.msg(message, opts={})
|
77
77
|
puts message
|
78
78
|
end
|
79
|
-
def cmd.msg_nocr(message)
|
79
|
+
def cmd.msg_nocr(message, opts={})
|
80
80
|
print message
|
81
81
|
end
|
82
82
|
def cmd.section(message, opts={})
|
@@ -91,6 +91,7 @@ module MockDebugger
|
|
91
91
|
sub_name = sub_class.const_get('PREFIX')
|
92
92
|
dbgr, cmd = setup(sub_name[0], false)
|
93
93
|
cmd.proc.frame_setup(RubyVM::ThreadFrame::current.prev)
|
94
|
+
cmd.proc.event = 'debugger-call'
|
94
95
|
sub_cmd = sub_class.new(cmd)
|
95
96
|
sub_cmd.summary_help(sub_cmd.name)
|
96
97
|
puts
|
@@ -103,6 +104,7 @@ module MockDebugger
|
|
103
104
|
subsub_name = subsub_class.const_get('PREFIX')
|
104
105
|
dbgr, cmd = setup(subsub_name[0], false)
|
105
106
|
cmd.proc.frame_setup(RubyVM::ThreadFrame::current.prev)
|
107
|
+
cmd.proc.event = 'debugger-call'
|
106
108
|
sub_cmd = sub_class.new(dbgr.core.processor, cmd)
|
107
109
|
subsub_cmd = subsub_class.new(cmd.proc, sub_cmd, subsub_name.join(''))
|
108
110
|
subsub_cmd.summary_help(subsub_cmd.name)
|
data/processor/msg.rb
CHANGED
@@ -3,6 +3,8 @@
|
|
3
3
|
require_relative '../app/util'
|
4
4
|
class Trepan
|
5
5
|
class CmdProcessor
|
6
|
+
attr_accessor :ruby_highlighter
|
7
|
+
|
6
8
|
def errmsg(message, opts={})
|
7
9
|
message = safe_rep(message) unless opts[:unlimited]
|
8
10
|
if @settings[:highlight] && defined?(Term::ANSIColor)
|
@@ -26,6 +28,20 @@ class Trepan
|
|
26
28
|
@dbgr.intf[-1].read_command(@prompt)
|
27
29
|
end
|
28
30
|
|
31
|
+
def ruby_format(text)
|
32
|
+
return text unless settings[:highlight]
|
33
|
+
unless @ruby_highlighter
|
34
|
+
begin
|
35
|
+
require 'coderay'
|
36
|
+
require 'term/ansicolor'
|
37
|
+
@ruby_highlighter = CodeRay::Duo[:ruby, :term]
|
38
|
+
rescue LoadError
|
39
|
+
return text
|
40
|
+
end
|
41
|
+
end
|
42
|
+
return @ruby_highlighter.encode(text)
|
43
|
+
end
|
44
|
+
|
29
45
|
def safe_rep(str)
|
30
46
|
Util::safe_repr(str, @settings[:maxstring])
|
31
47
|
end
|
@@ -38,5 +54,6 @@ class Trepan
|
|
38
54
|
end
|
39
55
|
@dbgr.intf[-1].msg(message)
|
40
56
|
end
|
57
|
+
|
41
58
|
end
|
42
59
|
end
|
data/processor/running.rb
CHANGED
@@ -33,7 +33,7 @@ class Trepan
|
|
33
33
|
step(0, opts)
|
34
34
|
@next_level = @frame.stack_size - level_count
|
35
35
|
@next_thread = Thread.current
|
36
|
-
@stop_events = Set.new(%w(return))
|
36
|
+
@stop_events = Set.new(%w(return leave yield))
|
37
37
|
|
38
38
|
# Try high-speed (run-time-assisted) method
|
39
39
|
@frame.trace_off = true # No more tracing in this frame
|
data/processor/subcmd.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
1
|
+
# Copyright (C) 2010, 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
2
2
|
# gdb-like subcommand processing.
|
3
3
|
|
4
4
|
class Trepan
|
5
5
|
class Subcmd
|
6
6
|
|
7
|
-
attr_reader :subcmds
|
7
|
+
attr_reader :subcmds # Hash of subcommands. Key is the subcommand name.
|
8
|
+
# the value is the subcommand object to run.
|
8
9
|
def initialize(cmd)
|
9
10
|
@cmd = cmd
|
10
11
|
@subcmds = {}
|
data/processor/validate.rb
CHANGED
@@ -1,10 +1,11 @@
|
|
1
|
-
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
1
|
+
# Copyright (C) 2010, 2011 Rocky Bernstein <rockyb@rubyforge.net>
|
2
2
|
|
3
3
|
# Trepan command input validation routines. A String type is
|
4
4
|
# usually passed in as the argument to validation routines.
|
5
5
|
|
6
6
|
require_relative '../app/condition'
|
7
7
|
require_relative '../app/file'
|
8
|
+
require_relative '../app/cmd_parse'
|
8
9
|
require_relative '../app/thread'
|
9
10
|
|
10
11
|
require_relative 'location' # for resolve_file_with_dir
|
@@ -21,7 +22,7 @@ class Trepan
|
|
21
22
|
include Trepan::Condition
|
22
23
|
|
23
24
|
def confirm(msg, default)
|
24
|
-
@dbgr.intf[-1].confirm(msg, default)
|
25
|
+
@settings[:confirm] ? @dbgr.intf[-1].confirm(msg, default) : true
|
25
26
|
end
|
26
27
|
|
27
28
|
# Like cmdfns.get_an_int(), but if there's a stack frame use that
|
@@ -74,10 +75,10 @@ class Trepan
|
|
74
75
|
end
|
75
76
|
|
76
77
|
if val < opts[:min_value]
|
77
|
-
if cmdname
|
78
|
+
if opts[:cmdname]
|
78
79
|
errmsg(("Command '%s' expects an integer at least" +
|
79
80
|
' %d; got: %d.') %
|
80
|
-
[cmdname, opts[:min_value], opts[:default]])
|
81
|
+
[opts[:cmdname], opts[:min_value], opts[:default]])
|
81
82
|
else
|
82
83
|
errmsg(("Expecting a positive integer at least" +
|
83
84
|
' %d; got: %d') %
|
@@ -135,92 +136,101 @@ class Trepan
|
|
135
136
|
# FIXME: do something if there is more than one.
|
136
137
|
if iseqs.size == 1
|
137
138
|
iseqs[-1]
|
138
|
-
elsif
|
139
|
-
|
139
|
+
elsif meth = method?(object_string)
|
140
|
+
meth.iseq
|
140
141
|
else
|
141
|
-
|
142
|
-
string =
|
143
|
-
if parts.size < 2
|
144
|
-
"method(\"#{object_string}\").iseq"
|
145
|
-
else
|
146
|
-
parts[0..-2].join('.')+".method(\"#{parts[-1]}\").iseq"
|
147
|
-
end
|
148
|
-
debug_eval_no_errmsg(string)
|
142
|
+
nil
|
149
143
|
end
|
150
144
|
rescue
|
151
145
|
nil
|
152
146
|
end
|
153
147
|
|
154
|
-
def
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
148
|
+
def position_to_line_and_offset(iseq, filename, position, offset_type)
|
149
|
+
case offset_type
|
150
|
+
when :line
|
151
|
+
if ary = iseq.lineoffsets[position]
|
152
|
+
# Normally the first offset is a trace instruction and doesn't
|
153
|
+
# register as the given line, so we need to take the next instruction
|
154
|
+
# after the first one, when available.
|
155
|
+
vm_offset = ary.size > 1 ? ary[1] : ary[0]
|
156
|
+
line_no = position
|
157
|
+
elsif found_iseq = find_iseqs_with_lineno(filename, position)
|
158
|
+
return position_to_line_and_offset(found_iseq, filename, position,
|
159
|
+
offset_type)
|
160
|
+
elsif found_iseq = find_iseq_with_line_from_iseq(iseq, position)
|
161
|
+
return position_to_line_and_offset(found_iseq, filename, position,
|
162
|
+
offset_type)
|
165
163
|
else
|
166
|
-
|
164
|
+
errmsg("Unable to find offset for line #{position}\n\t" +
|
165
|
+
"in #{iseq.name} of file #{filename}")
|
166
|
+
return [nil, nil]
|
167
167
|
end
|
168
|
-
|
169
|
-
|
168
|
+
when :offset
|
169
|
+
if ary=iseq.offset2lines(position)
|
170
|
+
line_no = ary.first
|
171
|
+
vm_offset = position
|
172
|
+
else
|
173
|
+
errmsg "Unable to find line for offset #{position} in #{iseq}"
|
174
|
+
return [nil, nil]
|
175
|
+
end
|
176
|
+
when nil
|
177
|
+
vm_offset = 0
|
178
|
+
line_no = iseq.offset2lines(vm_offset).first
|
179
|
+
else
|
180
|
+
errmsg "Bad parse offset_type: #{offset_type.inspect}"
|
181
|
+
return [nil, nil]
|
182
|
+
end
|
183
|
+
return [iseq, line_no, vm_offset]
|
170
184
|
end
|
171
185
|
|
172
|
-
# Parse a breakpoint position.
|
173
|
-
#
|
174
|
-
#
|
175
|
-
# -
|
176
|
-
#
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
186
|
+
# Parse a breakpoint position. On success return:
|
187
|
+
# - the instruction sequence to use
|
188
|
+
# - the line number - a Fixnum
|
189
|
+
# - vm_offset - a Fixnum
|
190
|
+
# - the condition (by default 'true') to use for this breakpoint
|
191
|
+
# - true condition should be negated. Used in *condition* if/unless
|
192
|
+
def breakpoint_position(position_str, allow_condition)
|
193
|
+
break_cmd_parse = if allow_condition
|
194
|
+
parse_breakpoint(position_str)
|
195
|
+
else
|
196
|
+
parse_breakpoint_no_condition(position_str)
|
197
|
+
end
|
198
|
+
return [nil] * 5 unless break_cmd_parse
|
199
|
+
tail = [break_cmd_parse.condition, break_cmd_parse.negate]
|
200
|
+
meth_or_frame, file, position, offset_type =
|
201
|
+
parse_position(break_cmd_parse.position)
|
202
|
+
if meth_or_frame
|
203
|
+
if iseq = meth_or_frame.iseq
|
204
|
+
iseq, line_no, vm_offset =
|
205
|
+
position_to_line_and_offset(iseq, file, position, offset_type)
|
206
|
+
if vm_offset && line_no
|
207
|
+
return [iseq, line_no, vm_offset] + tail
|
191
208
|
end
|
192
|
-
end
|
193
|
-
if args.empty? || 'if' == args[0]
|
194
|
-
use_offset = false
|
195
209
|
else
|
196
|
-
|
210
|
+
errmsg("Unable to set breakpoint in #{meth_or_frame}")
|
197
211
|
end
|
198
|
-
|
199
|
-
|
200
|
-
|
212
|
+
elsif file && position
|
213
|
+
if :line == offset_type
|
214
|
+
iseq = find_iseqs_with_lineno(file, position)
|
201
215
|
if iseq
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
# FIXME: *Still* have a bug stopping at offset 0.
|
206
|
-
# So stop at next offset after 0.
|
207
|
-
# 'o0'
|
208
|
-
"o#{@frame.iseq.offsetlines.keys.sort[1]}"
|
209
|
-
else
|
210
|
-
args.shift
|
211
|
-
end
|
216
|
+
junk, line_no, vm_offset =
|
217
|
+
position_to_line_and_offset(iseq, file, position, offset_type)
|
218
|
+
return [@frame.iseq, line_no, vm_offset] + tail
|
212
219
|
else
|
213
|
-
|
214
|
-
|
220
|
+
errmsg("Unable to find instruction sequence for" +
|
221
|
+
" position #{position} in #{file}")
|
215
222
|
end
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
223
|
+
else
|
224
|
+
errmsg "Come back later..."
|
225
|
+
end
|
226
|
+
elsif @frame.iseq.source_container[1] == file
|
227
|
+
line_no, vm_offset = position_to_line_and_offset(@frame.iseq, position,
|
228
|
+
offset_type)
|
229
|
+
return [@frame.iseq, line_no, vm_offset] + tail
|
230
|
+
else
|
231
|
+
errmsg("Unable to parse breakpoint position #{position_str}")
|
222
232
|
end
|
223
|
-
return [
|
233
|
+
return [nil] * 5
|
224
234
|
end
|
225
235
|
|
226
236
|
# Return true if arg is 'on' or 1 and false arg is 'off' or 0.
|
@@ -244,101 +254,76 @@ class Trepan
|
|
244
254
|
raise TypeError
|
245
255
|
end
|
246
256
|
|
247
|
-
|
248
|
-
obj, type, meth =
|
249
|
-
if method_string =~ /(.+)(#|::|\.)(.+)/
|
250
|
-
[$1, $2, $3]
|
251
|
-
else
|
252
|
-
['self', '.', method_string]
|
253
|
-
end
|
254
|
-
ret = debug_eval_no_errmsg("#{obj}.method(#{meth.inspect})")
|
255
|
-
return true if ret
|
256
|
-
return debug_eval_no_errmsg("#{obj}.is_a?(Class)") &&
|
257
|
-
debug_eval_no_errmsg("#{obj}.method_defined?(#{meth.inspect})")
|
258
|
-
end
|
257
|
+
include CmdParser
|
259
258
|
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
lineno_str = arg[colon+1..-1].lstrip
|
270
|
-
mf, container, lineno = parse_position_one_arg(arg1, old_mod, false, allow_offset)
|
271
|
-
return nil, nil, nil unless container
|
272
|
-
filename = canonic_file(arg1)
|
273
|
-
# Next handle part after the colon
|
274
|
-
val = get_an_int(lineno_str)
|
275
|
-
lineno = val if val
|
259
|
+
def get_method(meth)
|
260
|
+
start_binding =
|
261
|
+
begin
|
262
|
+
@frame.binding
|
263
|
+
rescue
|
264
|
+
binding
|
265
|
+
end
|
266
|
+
if meth.kind_of?(String)
|
267
|
+
meth_for_string(meth, start_binding)
|
276
268
|
else
|
277
|
-
|
269
|
+
begin
|
270
|
+
meth_for_parse_struct(meth, start_binding)
|
271
|
+
rescue NameError
|
272
|
+
errmsg("Can't evalute #{meth.name} to get a method")
|
273
|
+
return nil
|
274
|
+
end
|
278
275
|
end
|
279
|
-
|
280
|
-
return mf, container, lineno
|
281
276
|
end
|
282
277
|
|
283
|
-
#
|
284
|
-
#
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
def parse_position_one_arg(arg, old_mod=nil, show_errmsg=true, allow_offset=false)
|
289
|
-
name, filename = nil, nil, nil
|
290
|
-
begin
|
291
|
-
# First see if argument is an integer
|
292
|
-
lineno = Integer(arg)
|
293
|
-
rescue
|
294
|
-
else
|
295
|
-
container = frame_container(@frame, false)
|
296
|
-
filename = container[1] unless old_mod
|
297
|
-
return nil, [container[0], canonic_file(filename)], lineno
|
298
|
-
end
|
278
|
+
# FIXME: this is a ? method but we return
|
279
|
+
# the method value.
|
280
|
+
def method?(meth)
|
281
|
+
get_method(meth)
|
282
|
+
end
|
299
283
|
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
284
|
+
# parse_position(self, arg)->(meth, filename, offset, offset_type)
|
285
|
+
# See app/cmd_parser.kpeg for the syntax of a position which
|
286
|
+
# should include things like:
|
287
|
+
# Parse arg as [filename:]lineno | function | module
|
288
|
+
# Make sure it works for C:\foo\bar.py:12
|
289
|
+
def parse_position(info)
|
290
|
+
info = parse_location(info) if info.kind_of?(String)
|
291
|
+
case info.container_type
|
292
|
+
when :fn
|
293
|
+
if meth = method?(info.container)
|
294
|
+
return [meth, meth.iseq.source_container[1], info.position,
|
295
|
+
info.position_type]
|
304
296
|
else
|
305
|
-
|
297
|
+
return [nil] * 4
|
306
298
|
end
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
313
|
-
|
314
|
-
|
315
|
-
|
316
|
-
|
317
|
-
|
318
|
-
|
319
|
-
|
320
|
-
|
321
|
-
return
|
322
|
-
|
323
|
-
|
324
|
-
|
325
|
-
|
326
|
-
|
327
|
-
|
328
|
-
filename = iseq.source_container[1]
|
329
|
-
line_no = iseq.offsetlines.values.flatten.min
|
330
|
-
return arg, ['file', canonic_file(filename)], line_no
|
331
|
-
end
|
332
|
-
|
333
|
-
if show_errmsg
|
334
|
-
unless (allow_offset && arg.size > 0 && arg[0].downcase == 'o')
|
335
|
-
errmsg("#{arg} is not a line number, read-in filename or method " +
|
336
|
-
"we can get location information about")
|
299
|
+
when :file
|
300
|
+
filename = canonic_file(info.container)
|
301
|
+
# ?? Try to look up method here?
|
302
|
+
container = frame_container(@frame, false)
|
303
|
+
try_filename = container[1]
|
304
|
+
frame = (canonic_file(try_filename) == filename) ? @frame : nil
|
305
|
+
# else
|
306
|
+
# LineCache.compiled_method(filename)
|
307
|
+
# end
|
308
|
+
return frame, filename, info.position, info.position_type
|
309
|
+
when nil
|
310
|
+
if [:line, :offset].member?(info.position_type)
|
311
|
+
container = frame_container(@frame, false)
|
312
|
+
filename = container[1]
|
313
|
+
return @frame, canonic_file(filename), info.position, info.position_type
|
314
|
+
elsif !info.position_type
|
315
|
+
errmsg "Can't parse #{arg} as a position"
|
316
|
+
return [nil] * 4
|
317
|
+
else
|
318
|
+
errmsg "Unknown position type #{info.position_type} for location #{arg}"
|
319
|
+
return [nil] * 4
|
337
320
|
end
|
321
|
+
else
|
322
|
+
errmsg "Unknown container type #{info.container_type} for location #{arg}"
|
323
|
+
return [nil] * 4
|
338
324
|
end
|
339
|
-
return nil, nil, nil
|
340
325
|
end
|
341
|
-
|
326
|
+
|
342
327
|
def validate_initialize
|
343
328
|
## top_srcdir = File.expand_path(File.join(File.dirname(__FILE__), '..'))
|
344
329
|
## @dbgr_script_iseqs, @dbgr_iseqs = filter_scripts(top_srcdir)
|
@@ -359,52 +344,55 @@ if __FILE__ == $0
|
|
359
344
|
require_relative 'main' # Have to include before defining CmdProcessor!
|
360
345
|
# FIXME
|
361
346
|
|
362
|
-
|
363
|
-
|
364
|
-
|
347
|
+
cmdproc = Trepan::CmdProcessor.new(Trepan::MockCore.new())
|
348
|
+
cmdproc.frame_initialize
|
349
|
+
cmdproc.instance_variable_set('@settings',
|
365
350
|
Trepan::CmdProcessor::DEFAULT_SETTINGS)
|
366
|
-
|
351
|
+
cmdproc.frame_setup(RubyVM::ThreadFrame.current)
|
367
352
|
onoff = %w(1 0 on off)
|
368
|
-
onoff.each { |val| puts "onoff(#{val}) = #{
|
353
|
+
onoff.each { |val| puts "onoff(#{val}) = #{cmdproc.get_onoff(val)}" }
|
369
354
|
%w(1 1E bad 1+1 -5).each do |val|
|
370
|
-
puts "get_int_noerr(#{val}) = #{
|
355
|
+
puts "get_int_noerr(#{val}) = #{cmdproc.get_int_noerr(val).inspect}"
|
371
356
|
end
|
372
357
|
def foo; 5 end
|
373
|
-
def
|
358
|
+
def cmdproc.errmsg(msg)
|
374
359
|
puts msg
|
375
360
|
end
|
376
|
-
puts
|
377
|
-
puts
|
361
|
+
# puts cmdproc.object_iseq('food').inspect
|
362
|
+
# puts cmdproc.object_iseq('foo').inspect
|
378
363
|
|
379
|
-
puts
|
380
|
-
puts
|
364
|
+
# puts cmdproc.object_iseq('foo@validate.rb').inspect
|
365
|
+
# puts cmdproc.object_iseq('cmdproc.object_iseq').inspect
|
381
366
|
|
382
|
-
puts
|
367
|
+
puts cmdproc.parse_position(__FILE__).inspect
|
368
|
+
puts cmdproc.parse_position('@8').inspect
|
369
|
+
puts cmdproc.parse_position('8').inspect
|
370
|
+
puts cmdproc.parse_position("#{__FILE__} #{__LINE__}").inspect
|
383
371
|
|
384
372
|
puts '=' * 40
|
385
|
-
['Array
|
386
|
-
'foo', '
|
387
|
-
puts "#{str} should be
|
373
|
+
['Array.map', 'Trepan::CmdProcessor.new',
|
374
|
+
'foo', 'cmdproc.errmsg'].each do |str|
|
375
|
+
puts "#{str} should be method: #{!!cmdproc.method?(str)}"
|
388
376
|
end
|
389
377
|
puts '=' * 40
|
390
|
-
# require_relative '../lib/trepanning'
|
391
|
-
# dbgr = Trepan.new
|
392
|
-
# dbgr.debugger
|
393
378
|
|
394
379
|
# FIXME:
|
395
|
-
|
396
|
-
# Trepan::CmdProcessor.allocate should be false: true
|
380
|
+
puts "Trepan::CmdProcessor.allocate is: #{cmdproc.get_method('Trepan::CmdProcessor.allocate')}"
|
397
381
|
|
398
382
|
['food', '.errmsg'].each do |str|
|
399
|
-
puts "#{str} should be false: #{
|
383
|
+
puts "#{str} should be false: #{cmdproc.method?(str).to_s}"
|
400
384
|
end
|
401
385
|
puts '-' * 20
|
402
|
-
p
|
403
|
-
p
|
404
|
-
p
|
405
|
-
p
|
406
|
-
|
407
|
-
|
408
|
-
p
|
386
|
+
p cmdproc.breakpoint_position('foo', true)
|
387
|
+
p cmdproc.breakpoint_position('@0', true)
|
388
|
+
p cmdproc.breakpoint_position("#{__LINE__}", true)
|
389
|
+
p cmdproc.breakpoint_position("#{__FILE__} @0", false)
|
390
|
+
p cmdproc.breakpoint_position("#{__FILE__}:#{__LINE__}", true)
|
391
|
+
p cmdproc.breakpoint_position("#{__FILE__} #{__LINE__} if 1 == a", true)
|
392
|
+
p cmdproc.breakpoint_position("cmdproc.errmsg", false)
|
393
|
+
p cmdproc.breakpoint_position("cmdproc.errmsg:@0", false)
|
394
|
+
### p cmdproc.breakpoint_position(%w(2 if a > b))
|
395
|
+
p cmdproc.get_int_list(%w(1+0 3-1 3))
|
396
|
+
p cmdproc.get_int_list(%w(a 2 3))
|
409
397
|
end
|
410
398
|
end
|