trepanning 0.0.4
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/ChangeLog +4422 -0
- data/LICENSE +23 -0
- data/NEWS +12 -0
- data/README.textile +56 -0
- data/Rakefile +171 -0
- data/app/Makefile +7 -0
- data/app/breakpoint.rb +157 -0
- data/app/brkptmgr.rb +149 -0
- data/app/condition.rb +22 -0
- data/app/core.rb +203 -0
- data/app/default.rb +54 -0
- data/app/disassemble.rb +61 -0
- data/app/display.rb +148 -0
- data/app/file.rb +135 -0
- data/app/frame.rb +275 -0
- data/app/irb.rb +112 -0
- data/app/mock.rb +22 -0
- data/app/options.rb +122 -0
- data/app/run.rb +95 -0
- data/app/thread.rb +24 -0
- data/app/util.rb +32 -0
- data/bin/trepan +63 -0
- data/data/custom_require.rb +44 -0
- data/data/irbrc +55 -0
- data/data/prelude.rb +38 -0
- data/interface/base_intf.rb +95 -0
- data/interface/script.rb +103 -0
- data/interface/user.rb +90 -0
- data/io/base_io.rb +92 -0
- data/io/input.rb +111 -0
- data/io/string_array.rb +155 -0
- data/lib/Makefile +7 -0
- data/lib/trepanning.rb +277 -0
- data/processor/breakpoint.rb +108 -0
- data/processor/command/alias.rb +55 -0
- data/processor/command/backtrace.rb +95 -0
- data/processor/command/base/cmd.rb +97 -0
- data/processor/command/base/subcmd.rb +207 -0
- data/processor/command/base/submgr.rb +178 -0
- data/processor/command/base/subsubcmd.rb +102 -0
- data/processor/command/base/subsubmgr.rb +182 -0
- data/processor/command/break.rb +85 -0
- data/processor/command/condition.rb +64 -0
- data/processor/command/continue.rb +61 -0
- data/processor/command/debug.rb +85 -0
- data/processor/command/delete.rb +54 -0
- data/processor/command/directory.rb +43 -0
- data/processor/command/disable.rb +65 -0
- data/processor/command/disassemble.rb +103 -0
- data/processor/command/display.rb +81 -0
- data/processor/command/down.rb +56 -0
- data/processor/command/enable.rb +43 -0
- data/processor/command/exit.rb +54 -0
- data/processor/command/finish.rb +81 -0
- data/processor/command/frame.rb +117 -0
- data/processor/command/help.rb +146 -0
- data/processor/command/info.rb +28 -0
- data/processor/command/info_subcmd/args.rb +56 -0
- data/processor/command/info_subcmd/breakpoints.rb +162 -0
- data/processor/command/info_subcmd/file.rb +162 -0
- data/processor/command/info_subcmd/frame.rb +39 -0
- data/processor/command/info_subcmd/iseq.rb +83 -0
- data/processor/command/info_subcmd/locals.rb +88 -0
- data/processor/command/info_subcmd/program.rb +54 -0
- data/processor/command/info_subcmd/registers.rb +72 -0
- data/processor/command/info_subcmd/registers_subcmd/dfp.rb +38 -0
- data/processor/command/info_subcmd/registers_subcmd/helper.rb +40 -0
- data/processor/command/info_subcmd/registers_subcmd/lfp.rb +54 -0
- data/processor/command/info_subcmd/registers_subcmd/pc.rb +44 -0
- data/processor/command/info_subcmd/registers_subcmd/sp.rb +75 -0
- data/processor/command/info_subcmd/return.rb +40 -0
- data/processor/command/info_subcmd/thread.rb +106 -0
- data/processor/command/irb.rb +106 -0
- data/processor/command/kill.rb +58 -0
- data/processor/command/list.rb +327 -0
- data/processor/command/macro.rb +65 -0
- data/processor/command/next.rb +89 -0
- data/processor/command/nocache.rb +33 -0
- data/processor/command/print.rb +37 -0
- data/processor/command/ps.rb +40 -0
- data/processor/command/quit.rb +62 -0
- data/processor/command/raise.rb +47 -0
- data/processor/command/reload.rb +28 -0
- data/processor/command/reload_subcmd/command.rb +34 -0
- data/processor/command/restart.rb +57 -0
- data/processor/command/save.rb +60 -0
- data/processor/command/set.rb +47 -0
- data/processor/command/set_subcmd/auto.rb +27 -0
- data/processor/command/set_subcmd/auto_subcmd/eval.rb +67 -0
- data/processor/command/set_subcmd/auto_subcmd/irb.rb +49 -0
- data/processor/command/set_subcmd/auto_subcmd/list.rb +51 -0
- data/processor/command/set_subcmd/basename.rb +39 -0
- data/processor/command/set_subcmd/debug.rb +27 -0
- data/processor/command/set_subcmd/debug_subcmd/dbgr.rb +49 -0
- data/processor/command/set_subcmd/debug_subcmd/except.rb +35 -0
- data/processor/command/set_subcmd/debug_subcmd/macro.rb +35 -0
- data/processor/command/set_subcmd/debug_subcmd/skip.rb +35 -0
- data/processor/command/set_subcmd/debug_subcmd/stack.rb +45 -0
- data/processor/command/set_subcmd/different.rb +67 -0
- data/processor/command/set_subcmd/events.rb +71 -0
- data/processor/command/set_subcmd/max.rb +35 -0
- data/processor/command/set_subcmd/max_subcmd/list.rb +50 -0
- data/processor/command/set_subcmd/max_subcmd/stack.rb +60 -0
- data/processor/command/set_subcmd/max_subcmd/string.rb +53 -0
- data/processor/command/set_subcmd/max_subcmd/width.rb +50 -0
- data/processor/command/set_subcmd/return.rb +66 -0
- data/processor/command/set_subcmd/sp.rb +62 -0
- data/processor/command/set_subcmd/substitute.rb +25 -0
- data/processor/command/set_subcmd/substitute_subcmd/eval.rb +98 -0
- data/processor/command/set_subcmd/substitute_subcmd/path.rb +55 -0
- data/processor/command/set_subcmd/substitute_subcmd/string.rb +72 -0
- data/processor/command/set_subcmd/timer.rb +68 -0
- data/processor/command/set_subcmd/trace.rb +43 -0
- data/processor/command/set_subcmd/trace_subcmd/buffer.rb +56 -0
- data/processor/command/set_subcmd/trace_subcmd/print.rb +54 -0
- data/processor/command/set_subcmd/trace_subcmd/var.rb +61 -0
- data/processor/command/show.rb +27 -0
- data/processor/command/show_subcmd/alias.rb +50 -0
- data/processor/command/show_subcmd/args.rb +50 -0
- data/processor/command/show_subcmd/auto.rb +27 -0
- data/processor/command/show_subcmd/auto_subcmd/eval.rb +38 -0
- data/processor/command/show_subcmd/auto_subcmd/irb.rb +34 -0
- data/processor/command/show_subcmd/auto_subcmd/list.rb +36 -0
- data/processor/command/show_subcmd/basename.rb +28 -0
- data/processor/command/show_subcmd/debug.rb +27 -0
- data/processor/command/show_subcmd/debug_subcmd/dbgr.rb +31 -0
- data/processor/command/show_subcmd/debug_subcmd/except.rb +33 -0
- data/processor/command/show_subcmd/debug_subcmd/macro.rb +32 -0
- data/processor/command/show_subcmd/debug_subcmd/skip.rb +33 -0
- data/processor/command/show_subcmd/debug_subcmd/stack.rb +32 -0
- data/processor/command/show_subcmd/different.rb +37 -0
- data/processor/command/show_subcmd/events.rb +40 -0
- data/processor/command/show_subcmd/macro.rb +45 -0
- data/processor/command/show_subcmd/max.rb +31 -0
- data/processor/command/show_subcmd/max_subcmd/list.rb +39 -0
- data/processor/command/show_subcmd/max_subcmd/stack.rb +35 -0
- data/processor/command/show_subcmd/max_subcmd/string.rb +41 -0
- data/processor/command/show_subcmd/max_subcmd/width.rb +36 -0
- data/processor/command/show_subcmd/trace.rb +29 -0
- data/processor/command/show_subcmd/trace_subcmd/buffer.rb +84 -0
- data/processor/command/show_subcmd/trace_subcmd/print.rb +38 -0
- data/processor/command/source.rb +74 -0
- data/processor/command/step.rb +139 -0
- data/processor/command/stepi.rb +63 -0
- data/processor/command/unalias.rb +44 -0
- data/processor/command/undisplay.rb +63 -0
- data/processor/command/up.rb +92 -0
- data/processor/default.rb +45 -0
- data/processor/display.rb +17 -0
- data/processor/eval.rb +88 -0
- data/processor/eventbuf.rb +131 -0
- data/processor/frame.rb +230 -0
- data/processor/help.rb +72 -0
- data/processor/hook.rb +128 -0
- data/processor/load_cmds.rb +102 -0
- data/processor/location.rb +126 -0
- data/processor/main.rb +364 -0
- data/processor/mock.rb +100 -0
- data/processor/msg.rb +26 -0
- data/processor/running.rb +170 -0
- data/processor/subcmd.rb +159 -0
- data/processor/validate.rb +395 -0
- data/test/example/fname with blank.rb +1 -0
- data/test/example/gcd-xx.rb +18 -0
- data/test/example/gcd.rb +19 -0
- data/test/example/gcd1.rb +24 -0
- data/test/example/null.rb +1 -0
- data/test/example/thread1.rb +3 -0
- data/test/functional/fn_helper.rb +119 -0
- data/test/functional/test-break.rb +87 -0
- data/test/functional/test-condition.rb +59 -0
- data/test/functional/test-debugger-call-bug.rb +31 -0
- data/test/functional/test-delete.rb +71 -0
- data/test/functional/test-finish.rb +44 -0
- data/test/functional/test-immediate-step-bug.rb +35 -0
- data/test/functional/test-next.rb +77 -0
- data/test/functional/test-raise.rb +73 -0
- data/test/functional/test-return.rb +100 -0
- data/test/functional/test-step.rb +274 -0
- data/test/functional/test-stepbug.rb +40 -0
- data/test/functional/test-trace-var.rb +40 -0
- data/test/functional/tmp/b1.rb +5 -0
- data/test/functional/tmp/s1.rb +9 -0
- data/test/functional/tmp/t2.rb +6 -0
- data/test/integration/file-diff.rb +88 -0
- data/test/integration/helper.rb +52 -0
- data/test/integration/test-fname-with-blank.rb +11 -0
- data/test/integration/test-quit.rb +11 -0
- data/test/integration/try-test-enable.rb +11 -0
- data/test/unit/cmd-helper.rb +44 -0
- data/test/unit/test-app-brkpt.rb +30 -0
- data/test/unit/test-app-brkptmgr.rb +56 -0
- data/test/unit/test-app-disassemble.rb +60 -0
- data/test/unit/test-app-file.rb +46 -0
- data/test/unit/test-app-frame.rb +49 -0
- data/test/unit/test-app-options.rb +60 -0
- data/test/unit/test-app-run.rb +19 -0
- data/test/unit/test-app-thread.rb +25 -0
- data/test/unit/test-app-util.rb +17 -0
- data/test/unit/test-base-subcmd.rb +59 -0
- data/test/unit/test-bin-trepan.rb +48 -0
- data/test/unit/test-cmd-alias.rb +50 -0
- data/test/unit/test-cmd-break.rb +80 -0
- data/test/unit/test-cmd-endisable.rb +59 -0
- data/test/unit/test-cmd-help.rb +100 -0
- data/test/unit/test-cmd-kill.rb +47 -0
- data/test/unit/test-cmd-quit.rb +26 -0
- data/test/unit/test-cmd-step.rb +45 -0
- data/test/unit/test-intf-user.rb +45 -0
- data/test/unit/test-io-input.rb +26 -0
- data/test/unit/test-proc-eval.rb +26 -0
- data/test/unit/test-proc-frame.rb +77 -0
- data/test/unit/test-proc-help.rb +15 -0
- data/test/unit/test-proc-hook.rb +29 -0
- data/test/unit/test-proc-load_cmds.rb +40 -0
- data/test/unit/test-proc-main.rb +99 -0
- data/test/unit/test-proc-validate.rb +90 -0
- data/test/unit/test-subcmd-help.rb +48 -0
- metadata +358 -0
data/bin/trepan
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
# Invoke debugger from the command line.
|
|
4
|
+
require_relative '../app/run'
|
|
5
|
+
require_relative '../app/options'
|
|
6
|
+
require_relative '../lib/trepanning'
|
|
7
|
+
|
|
8
|
+
# FIXME: the figure out how to run via gem installation.
|
|
9
|
+
if File.basename(__FILE__) == File.basename($0)
|
|
10
|
+
include Trepanning
|
|
11
|
+
|
|
12
|
+
# One way to get local variables is to create a block which is run
|
|
13
|
+
# once.
|
|
14
|
+
#
|
|
15
|
+
# Note however that since there are constants below, we can't
|
|
16
|
+
# wrap all of this in a procedure as that would be defining
|
|
17
|
+
# constants dynamically.
|
|
18
|
+
1.times do
|
|
19
|
+
| ; trepan_path, program_to_debug |
|
|
20
|
+
# options = DEFAULT_CMDLINE_SETTINGS.merge({}) seems to change up
|
|
21
|
+
# DEFAULT_CMDLINE_SETTINGS when options[:key] is changed. The
|
|
22
|
+
# below is the simplest thing I can come up with so far.
|
|
23
|
+
options = copy_default_options
|
|
24
|
+
opts = setup_options(options)
|
|
25
|
+
Trepan::ARGV = ARGV.clone
|
|
26
|
+
rest = opts.parse! ARGV
|
|
27
|
+
|
|
28
|
+
trepan_path = File.expand_path($0)
|
|
29
|
+
if RUBY_PLATFORM =~ /mswin/
|
|
30
|
+
trepan_path += '.cmd' unless trepan_path =~ /\.cmd$/i
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# FIXME: do we need to test defined?
|
|
34
|
+
# FIXME: Should (some of) these be instance variables?
|
|
35
|
+
Trepan::RUBY_PATH = ruby_path
|
|
36
|
+
Trepan::TREPAN_PATH = trepan_path
|
|
37
|
+
Trepan::RBDBGR_FILE = __FILE__
|
|
38
|
+
|
|
39
|
+
if ARGV.empty?
|
|
40
|
+
if options[:version] || options[:help]
|
|
41
|
+
exit 100
|
|
42
|
+
else
|
|
43
|
+
STDERR.puts 'Sorry - for now you must specify a Ruby script to debug.'
|
|
44
|
+
exit(1)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
program_to_debug = ARGV.shift
|
|
49
|
+
program_to_debug = whence_file(program_to_debug) unless
|
|
50
|
+
File.exist?(program_to_debug)
|
|
51
|
+
Trepan::PROG_SCRIPT = program_to_debug
|
|
52
|
+
|
|
53
|
+
# Set global so others may use this debugger.
|
|
54
|
+
$trepan = Trepan.new(:restart_argv =>
|
|
55
|
+
explicit_restart_argv(Trepan::ARGV),
|
|
56
|
+
:cmdfiles => options[:cmdfiles],
|
|
57
|
+
:initial_dir => options[:chdir],
|
|
58
|
+
:nx => options[:nx]
|
|
59
|
+
)
|
|
60
|
+
debug_program($trepan, Trepan::RUBY_PATH,
|
|
61
|
+
File.expand_path(program_to_debug))
|
|
62
|
+
end
|
|
63
|
+
end
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright 2006 by Chad Fowler, Rich Kilmer, Jim Weirich and others.
|
|
3
|
+
# All rights reserved.
|
|
4
|
+
# See LICENSE.txt for permissions.
|
|
5
|
+
#++
|
|
6
|
+
|
|
7
|
+
module Kernel
|
|
8
|
+
|
|
9
|
+
##
|
|
10
|
+
# The Kernel#require from before RubyGems was loaded.
|
|
11
|
+
|
|
12
|
+
alias gem_original_require require
|
|
13
|
+
|
|
14
|
+
##
|
|
15
|
+
# When RubyGems is required, Kernel#require is replaced with our own which
|
|
16
|
+
# is capable of loading gems on demand.
|
|
17
|
+
#
|
|
18
|
+
# When you call <tt>require 'x'</tt>, this is what happens:
|
|
19
|
+
# * If the file can be loaded from the existing Ruby loadpath, it
|
|
20
|
+
# is.
|
|
21
|
+
# * Otherwise, installed gems are searched for a file that matches.
|
|
22
|
+
# If it's found in gem 'y', that gem is activated (added to the
|
|
23
|
+
# loadpath).
|
|
24
|
+
#
|
|
25
|
+
# The normal <tt>require</tt> functionality of returning false if
|
|
26
|
+
# that file has already been loaded is preserved.
|
|
27
|
+
|
|
28
|
+
def require(path) # :doc:
|
|
29
|
+
gem_original_require path
|
|
30
|
+
rescue LoadError => load_error
|
|
31
|
+
if load_error.message.end_with?(path)
|
|
32
|
+
if Gem.try_activate(path)
|
|
33
|
+
return gem_original_require(path)
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
raise load_error
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private :require
|
|
41
|
+
private :gem_original_require
|
|
42
|
+
|
|
43
|
+
end unless Kernel.private_method_defined?(:gem_original_require)
|
|
44
|
+
|
data/data/irbrc
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# -*- Ruby -*-
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
# We use this as the default startup file for irb inside trepan
|
|
4
|
+
# Down the line we will have a way for folks to add/override this
|
|
5
|
+
# with their own file.
|
|
6
|
+
IRB.conf[:PROMPT_MODE] = :SIMPLE
|
|
7
|
+
IRB.conf[:PROMPT][:SIMPLE] =
|
|
8
|
+
{:PROMPT_C=>"trepan ?> ",
|
|
9
|
+
:PROMPT_I=>"trepan >> ",
|
|
10
|
+
:PROMPT_N=>"trepan >> ",
|
|
11
|
+
:PROMPT_S=>nil,
|
|
12
|
+
:RETURN=>"=> %s\n"}
|
|
13
|
+
|
|
14
|
+
# Using dbgr to issue a debugger statement inside irb:
|
|
15
|
+
# dbgr %w(info program)
|
|
16
|
+
# dbgr 'info program' # also works
|
|
17
|
+
# But...
|
|
18
|
+
# dbgr info program # wrong!
|
|
19
|
+
#
|
|
20
|
+
puts "You are in a trepan session. You should have access to program scope."
|
|
21
|
+
puts "'dbgr', 'step', 'n', 'q', 'cont' commands have been added."
|
|
22
|
+
|
|
23
|
+
if defined?($trepan) && $trepan
|
|
24
|
+
puts 'You should have access to debugger state via global variable $trepan'
|
|
25
|
+
end
|
|
26
|
+
if defined?($trepan_frame) && $trepan_frame
|
|
27
|
+
puts 'You should have access to the program frame via global variable $trepan_frame'
|
|
28
|
+
end
|
|
29
|
+
if defined?($trepan_cmdproc) && $trepan_cmdproc
|
|
30
|
+
puts 'You should have access to the command processor via global variable $trepan_cmdproc'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# Monkeypatch to save the current IRB statement to be run and make the instruction sequence
|
|
34
|
+
# "filename" unique. Possibly not needed.
|
|
35
|
+
class IRB::Context
|
|
36
|
+
def evaluate(line, line_no)
|
|
37
|
+
$trepan_irb_statements = line
|
|
38
|
+
@line_no = line_no
|
|
39
|
+
@eval_counter ||= 0
|
|
40
|
+
container =
|
|
41
|
+
if irb_path =~ /\((.+)\)/
|
|
42
|
+
# Note we originally had a colon below. This causes IRB to think
|
|
43
|
+
# tracebacks are IRB bugs since the regexp matching it uses here
|
|
44
|
+
# is now messed up. (irb:5): in ... vs (irb): in ...
|
|
45
|
+
"(#{$1}[#{@eval_counter}])"
|
|
46
|
+
else
|
|
47
|
+
irb_path
|
|
48
|
+
end
|
|
49
|
+
set_last_value(@workspace.evaluate(self, line, container, line_no))
|
|
50
|
+
# @workspace.evaluate("_ = IRB.conf[:MAIN_CONTEXT]._")
|
|
51
|
+
# @_ = @workspace.evaluate(line, irb_path, line_no)
|
|
52
|
+
@eval_counter += 1
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
data/data/prelude.rb
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
|
|
2
|
+
# Mutex
|
|
3
|
+
|
|
4
|
+
class Mutex
|
|
5
|
+
def synchronize
|
|
6
|
+
self.lock
|
|
7
|
+
begin
|
|
8
|
+
yield
|
|
9
|
+
ensure
|
|
10
|
+
self.unlock rescue nil
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
# Thread
|
|
16
|
+
|
|
17
|
+
class Thread
|
|
18
|
+
MUTEX_FOR_THREAD_EXCLUSIVE = Mutex.new
|
|
19
|
+
def self.exclusive
|
|
20
|
+
MUTEX_FOR_THREAD_EXCLUSIVE.synchronize{
|
|
21
|
+
yield
|
|
22
|
+
}
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module Kernel
|
|
27
|
+
module_function
|
|
28
|
+
def require_relative(relative_feature)
|
|
29
|
+
c = caller.first
|
|
30
|
+
e = c.rindex(/:\d+:in /)
|
|
31
|
+
file = $`
|
|
32
|
+
if /\A\((.*)\)/ =~ file # eval, etc.
|
|
33
|
+
raise LoadError, "require_relative is called in #{$1}"
|
|
34
|
+
end
|
|
35
|
+
absolute_feature = File.expand_path(File.join(File.dirname(file), relative_feature))
|
|
36
|
+
require absolute_feature
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
|
|
4
|
+
# A base class for a debugger interface.
|
|
5
|
+
|
|
6
|
+
class Trepan
|
|
7
|
+
|
|
8
|
+
unless defined?(NotImplementedMessage)
|
|
9
|
+
NotImplementedMessage = 'This method must be overriden in a subclass'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
# A debugger interface handles the communication or interaction with between
|
|
13
|
+
# the program and the outside portion which could be
|
|
14
|
+
# - a user,
|
|
15
|
+
# - a front-end that talks to a user, or
|
|
16
|
+
# - another interface in another process or computer
|
|
17
|
+
class Interface
|
|
18
|
+
|
|
19
|
+
attr_accessor :interactive, :input, :output
|
|
20
|
+
|
|
21
|
+
unless defined?(YES)
|
|
22
|
+
YES = %w(y yes oui si yep ja)
|
|
23
|
+
NO = %w(n no non nope nein)
|
|
24
|
+
YES_OR_NO = YES + NO
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def initialize(inp=nil, out=nil, opts={})
|
|
28
|
+
@input = inp || STDIN
|
|
29
|
+
@interactive = false
|
|
30
|
+
@opts = opts
|
|
31
|
+
@output = out || STDOUT
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# Closes all input and/or output.
|
|
35
|
+
def close
|
|
36
|
+
@input.close unless @input.closed?
|
|
37
|
+
@output.close unless @output.closed?
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Called when a dangerous action is about to be done to make sure
|
|
41
|
+
# it's okay. `prompt' is printed; user response is returned.
|
|
42
|
+
def confirm(prompt, default=false)
|
|
43
|
+
raise RuntimeError, Trepan::NotImplementedMessage
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Common routine for reporting debugger error messages.
|
|
47
|
+
def errmsg(str, prefix='** ')
|
|
48
|
+
if str.is_a?(Array)
|
|
49
|
+
str.each{|s| errmsg(s)}
|
|
50
|
+
else
|
|
51
|
+
str.split("\n").each do |s|
|
|
52
|
+
msg("%s%s" % [prefix, s])
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def finalize(last_wishes=nil)
|
|
58
|
+
close
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# Return true if interface is interactive.
|
|
62
|
+
def interactive?
|
|
63
|
+
# Default false and making subclasses figure out how to determine
|
|
64
|
+
# interactiveness.
|
|
65
|
+
false
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# used to write to a debugger that is connected to this
|
|
69
|
+
# server; `str' written will have a newline added to it.
|
|
70
|
+
def msg(message)
|
|
71
|
+
if message.is_a?(Array)
|
|
72
|
+
message.each{|s| msg(s)}
|
|
73
|
+
else
|
|
74
|
+
message = message ? message.to_s + "\n" : ''
|
|
75
|
+
@output.write(message)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# used to write to a debugger that is connected to this
|
|
80
|
+
# server; `str' written will not have a newline added to it
|
|
81
|
+
def msg_nocr(msg)
|
|
82
|
+
@output.write(msg)
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
def read_command( prompt)
|
|
86
|
+
raise RuntimeError, Trepan::NotImplementedMessage
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def readline(prompt='')
|
|
90
|
+
@output.flush
|
|
91
|
+
@output.write(prompt) if prompt and prompt.size > 0
|
|
92
|
+
@input.readline
|
|
93
|
+
end
|
|
94
|
+
end
|
|
95
|
+
end
|
data/interface/script.rb
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
|
|
4
|
+
# Module for reading debugger scripts
|
|
5
|
+
|
|
6
|
+
# Our local modules
|
|
7
|
+
require_relative 'base_intf'
|
|
8
|
+
require_relative '../io/input'
|
|
9
|
+
require_relative '../io/string_array'
|
|
10
|
+
|
|
11
|
+
# Interface when reading debugger scripts
|
|
12
|
+
class Trepan::ScriptInterface < Trepan::Interface
|
|
13
|
+
|
|
14
|
+
DEFAULT_OPTS = {
|
|
15
|
+
:abort_on_error => true,
|
|
16
|
+
:confirm_val => false,
|
|
17
|
+
:verbose => false
|
|
18
|
+
} unless defined?(DEFAULT_OPTS)
|
|
19
|
+
|
|
20
|
+
def initialize(script_name, out=nil, opts={})
|
|
21
|
+
|
|
22
|
+
@opts = DEFAULT_OPTS.merge(opts)
|
|
23
|
+
|
|
24
|
+
at_exit { finalize }
|
|
25
|
+
@script_name = script_name
|
|
26
|
+
@input_lineno = 0
|
|
27
|
+
@input = Trepan::UserInput.open(script_name,
|
|
28
|
+
:line_edit => false)
|
|
29
|
+
@buffer_output = []
|
|
30
|
+
unless opts[:verbose] or out
|
|
31
|
+
out = Trepan::StringArrayOutput.open(@buffer_output)
|
|
32
|
+
end
|
|
33
|
+
super(@input, out, @opts)
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
# Closes input only.
|
|
37
|
+
def close
|
|
38
|
+
@input.close
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
# Called when a dangerous action is about to be done, to make
|
|
42
|
+
# sure it's okay.
|
|
43
|
+
#
|
|
44
|
+
# Could look also look for interactive input and
|
|
45
|
+
# use that. For now, though we'll simplify.
|
|
46
|
+
def confirm(prompt, default)
|
|
47
|
+
@opts[:default_confirm]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Common routine for reporting debugger error messages.
|
|
51
|
+
#
|
|
52
|
+
def errmsg(msg, prefix="*** ")
|
|
53
|
+
# self.verbose shows lines so we don't have to duplicate info
|
|
54
|
+
# here. Perhaps there should be a 'terse' mode to never show
|
|
55
|
+
# position info.
|
|
56
|
+
mess = if not @opts[:verbose]
|
|
57
|
+
location = ("%s:%s: Error in source command file" %
|
|
58
|
+
[@script_name, @input_lineno])
|
|
59
|
+
"%s%s:\n%s%s" % [prefix, location, prefix, msg]
|
|
60
|
+
else
|
|
61
|
+
"%s%s" % [prefix, msg]
|
|
62
|
+
end
|
|
63
|
+
msg(mess)
|
|
64
|
+
# FIXME: should we just set a flag and report eof? to be more
|
|
65
|
+
# consistent with File and IO?
|
|
66
|
+
raise IOError if @opts[:abort_on_error]
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def interactive? ; false end
|
|
70
|
+
|
|
71
|
+
# Script interface to read a command. `prompt' is a parameter for
|
|
72
|
+
# compatibilty and is ignored.
|
|
73
|
+
def read_command(prompt='')
|
|
74
|
+
@input_lineno += 1
|
|
75
|
+
line = readline
|
|
76
|
+
if @opts[:verbose]
|
|
77
|
+
location = "%s line %s" % [@script_name, @input_lineno]
|
|
78
|
+
msg('+ %s: %s' % [location, line])
|
|
79
|
+
end
|
|
80
|
+
# Do something with history?
|
|
81
|
+
return line
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Script interface to read a line. `prompt' is a parameter for
|
|
85
|
+
# compatibilty and is ignored.
|
|
86
|
+
#
|
|
87
|
+
# Could decide make this look for interactive input?
|
|
88
|
+
def readline(prompt='')
|
|
89
|
+
begin
|
|
90
|
+
return input.readline().chomp
|
|
91
|
+
rescue EOFError
|
|
92
|
+
@eof = true
|
|
93
|
+
raise EOFError
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Demo
|
|
99
|
+
if __FILE__ == $0
|
|
100
|
+
intf = Trepan::ScriptInterface.new(__FILE__)
|
|
101
|
+
line = intf.readline()
|
|
102
|
+
print "Line read: ", line
|
|
103
|
+
end
|
data/interface/user.rb
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
|
|
4
|
+
# Interface when communicating with the user in the same process as
|
|
5
|
+
# the debugged program.
|
|
6
|
+
|
|
7
|
+
# Our local modules
|
|
8
|
+
|
|
9
|
+
require_relative 'base_intf'
|
|
10
|
+
require_relative '../io/input'
|
|
11
|
+
|
|
12
|
+
# Moutput = import_relative('dbg_output', '..io', 'pydbgr')
|
|
13
|
+
|
|
14
|
+
# Interface when communicating with the user in the same
|
|
15
|
+
# process as the debugged program.
|
|
16
|
+
class Trepan::UserInterface < Trepan::Interface
|
|
17
|
+
|
|
18
|
+
FILE_HISTORY = '.trapan_hist' unless defined?(FILE_HISTORY)
|
|
19
|
+
|
|
20
|
+
def initialize(inp=nil, out=nil, opts={})
|
|
21
|
+
super(inp, out, opts)
|
|
22
|
+
@input = if inp.class.ancestors.member?(Trepan::InputBase)
|
|
23
|
+
inp
|
|
24
|
+
else
|
|
25
|
+
Trepan::UserInput.open(inp)
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Closes both input and output
|
|
30
|
+
|
|
31
|
+
# Called when a dangerous action is about to be done, to make
|
|
32
|
+
# sure it's okay. Expect a yes/no answer to `prompt' which is printed,
|
|
33
|
+
# suffixed with a question mark and the default value. The user
|
|
34
|
+
# response converted to a boolean is returned.
|
|
35
|
+
def confirm(prompt, default)
|
|
36
|
+
default_str = default ? 'Y/n' : 'N/y'
|
|
37
|
+
while true do
|
|
38
|
+
begin
|
|
39
|
+
response = readline('%s (%s) ' % [prompt, default_str])
|
|
40
|
+
rescue EOFError
|
|
41
|
+
return default
|
|
42
|
+
end
|
|
43
|
+
response = response.strip.downcase
|
|
44
|
+
|
|
45
|
+
# We don't catch "Yes, I'm sure" or "NO!", but I leave that
|
|
46
|
+
# as an exercise for the reader.
|
|
47
|
+
break if YES_OR_NO.member?(response)
|
|
48
|
+
msg "Please answer 'yes' or 'no'. Try again."
|
|
49
|
+
end
|
|
50
|
+
return YES.member?(response)
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def finalize(last_wishes=nil)
|
|
54
|
+
# print exit annotation
|
|
55
|
+
# save history
|
|
56
|
+
super
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
def interactive? ; @input.interactive? end
|
|
60
|
+
|
|
61
|
+
def read_command(prompt='')
|
|
62
|
+
line = readline(prompt)
|
|
63
|
+
# FIXME: Do something with history?
|
|
64
|
+
return line
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Demo
|
|
69
|
+
if __FILE__ == $0
|
|
70
|
+
intf = Trepan::UserInterface.new
|
|
71
|
+
intf.errmsg("Houston, we have a problem here!")
|
|
72
|
+
if ARGV.size > 0
|
|
73
|
+
begin
|
|
74
|
+
line = intf.readline("Type something: ")
|
|
75
|
+
rescue EOFError
|
|
76
|
+
puts "No input, got EOF"
|
|
77
|
+
else
|
|
78
|
+
puts "You typed: #{line}"
|
|
79
|
+
end
|
|
80
|
+
puts "EOF is now: %s" % intf.input.eof?.inspect
|
|
81
|
+
unless intf.input.eof?
|
|
82
|
+
line = intf.confirm("Are you sure", false)
|
|
83
|
+
puts "You typed: #{line}"
|
|
84
|
+
puts "EOF is now: %s" % intf.input.eof?.inspect
|
|
85
|
+
line = intf.confirm("Are you not sure", true)
|
|
86
|
+
puts "You typed: #{line}"
|
|
87
|
+
puts "EOF is now: %s" % intf.input.eof?.inspect
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
data/io/base_io.rb
ADDED
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
# classes to support communication to and from the debugger. This
|
|
3
|
+
# communcation might be to/from another process or another computer.
|
|
4
|
+
# And reading may be from a debugger command script.
|
|
5
|
+
#
|
|
6
|
+
# For example, we'd like to support Sockets, and serial lines and file
|
|
7
|
+
# reading, as well a readline-type input. Encryption and Authentication
|
|
8
|
+
# methods might decorate some of the communication channels.
|
|
9
|
+
#
|
|
10
|
+
# Some ideas originiated as part of Matt Fleming's 2006 Google Summer of
|
|
11
|
+
# Code project.
|
|
12
|
+
|
|
13
|
+
class Trepan
|
|
14
|
+
|
|
15
|
+
NotImplementedMessage = 'This method must be overriden in a subclass' unless
|
|
16
|
+
defined?(NotImplementedMessage)
|
|
17
|
+
|
|
18
|
+
class InputBase
|
|
19
|
+
attr_reader :input
|
|
20
|
+
attr_reader :line_edit
|
|
21
|
+
|
|
22
|
+
DEFAULT_OPTS = {
|
|
23
|
+
:line_edit => false,
|
|
24
|
+
} unless defined?(DEFAULT_OPTS)
|
|
25
|
+
|
|
26
|
+
def initialize(inp, opts={})
|
|
27
|
+
@opts = DEFAULT_OPTS.merge(opts)
|
|
28
|
+
@input = inp
|
|
29
|
+
@line_edit = opts[:line_edit]
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def close
|
|
33
|
+
@input.close unless @input.closed?
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
def eof?
|
|
37
|
+
begin
|
|
38
|
+
@input.eof?
|
|
39
|
+
rescue IOError
|
|
40
|
+
true
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Read a line of input. EOFError will be raised on EOF.
|
|
45
|
+
#
|
|
46
|
+
# Note that we don't support prompting first. Instead, arrange
|
|
47
|
+
# to call Trepan::Output.write() first with the prompt. If
|
|
48
|
+
# `use_raw' is set raw_input() will be used in that is supported
|
|
49
|
+
# by the specific input input. If this option is left None as is
|
|
50
|
+
# normally expected the value from the class initialization is
|
|
51
|
+
# used.
|
|
52
|
+
def readline
|
|
53
|
+
@input.readline
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# This is an abstract class that specifies debugger output.
|
|
58
|
+
class OutputBase
|
|
59
|
+
attr_accessor :flush_after_write
|
|
60
|
+
attr_reader :output
|
|
61
|
+
def initialize(out, opts={})
|
|
62
|
+
@output = out
|
|
63
|
+
@flush_after_write = false
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def close
|
|
67
|
+
@output.close if @output
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def eof?
|
|
71
|
+
@input.eof?
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def flush
|
|
75
|
+
@output.flush
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Use this to set where to write to. output can be a
|
|
79
|
+
# file object or a string. This code raises IOError on error.
|
|
80
|
+
def write(*args)
|
|
81
|
+
@output.print(*args)
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# used to write to a debugger that is connected to this
|
|
85
|
+
# `str' written will have a newline added to it
|
|
86
|
+
#
|
|
87
|
+
def writeline( msg)
|
|
88
|
+
@output.write("%s\n" % msg)
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
data/io/input.rb
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
|
|
4
|
+
# Debugger user/command-oriented input possibly attached to IO-style
|
|
5
|
+
# input or GNU Readline.
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
require_relative 'base_io'
|
|
9
|
+
|
|
10
|
+
class Trepan
|
|
11
|
+
|
|
12
|
+
# Debugger user/command-oriented input possibly attached to IO-style
|
|
13
|
+
# input or GNU Readline.
|
|
14
|
+
class UserInput < Trepan::InputBase
|
|
15
|
+
|
|
16
|
+
def initialize(inp, opts={})
|
|
17
|
+
@opts = DEFAULT_OPTS.merge(opts)
|
|
18
|
+
@input = inp || STDIN
|
|
19
|
+
@eof = false
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def closed?; @input.closed? end
|
|
23
|
+
def eof?; @eof end
|
|
24
|
+
|
|
25
|
+
def interactive?
|
|
26
|
+
@input.respond_to?(:isatty) && @input.isatty
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Read a line of input. EOFError will be raised on EOF.
|
|
30
|
+
#
|
|
31
|
+
# Note that we don't support prompting first. Instead, arrange
|
|
32
|
+
# to call Trepan::Output.write() first with the prompt.
|
|
33
|
+
def readline
|
|
34
|
+
# FIXME we don't do command completion.
|
|
35
|
+
raise EOFError if eof?
|
|
36
|
+
begin
|
|
37
|
+
line = @opts[:line_edit] ? Readline.readline : @input.gets
|
|
38
|
+
@eof = !line
|
|
39
|
+
rescue
|
|
40
|
+
@eof = true
|
|
41
|
+
end
|
|
42
|
+
raise EOFError if eof?
|
|
43
|
+
return line
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
class << self
|
|
47
|
+
# Use this to set where to read from.
|
|
48
|
+
#
|
|
49
|
+
# Set opts[:line_edit] if you want this input to interact with
|
|
50
|
+
# GNU-like readline library. By default, we will assume to try
|
|
51
|
+
# using readline.
|
|
52
|
+
def open(inp=nil, opts={})
|
|
53
|
+
inp ||= STDIN
|
|
54
|
+
inp = File.new(inp, 'r') if inp.is_a?(String)
|
|
55
|
+
opts[:line_edit] = false unless
|
|
56
|
+
inp.respond_to?(:isatty) && inp.isatty && Trepan::GNU_readline?
|
|
57
|
+
self.new(inp, opts)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def Trepan::GNU_readline?
|
|
64
|
+
begin
|
|
65
|
+
require 'readline'
|
|
66
|
+
return true
|
|
67
|
+
rescue LoadError
|
|
68
|
+
return false
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# Demo
|
|
73
|
+
if __FILE__ == $0
|
|
74
|
+
puts 'Have GNU is: %s' % Trepan::GNU_readline?
|
|
75
|
+
inp = Trepan::UserInput.open(__FILE__, :line_edit => false)
|
|
76
|
+
line = inp.readline
|
|
77
|
+
puts line
|
|
78
|
+
inp.close
|
|
79
|
+
filename = 'input.py'
|
|
80
|
+
begin
|
|
81
|
+
Trepan::UserInput.open(filename)
|
|
82
|
+
rescue
|
|
83
|
+
puts "Can't open #{filename} for reading: #{$!}"
|
|
84
|
+
end
|
|
85
|
+
inp = Trepan::UserInput.open(__FILE__, :line_edit => false)
|
|
86
|
+
while true
|
|
87
|
+
begin
|
|
88
|
+
inp.readline
|
|
89
|
+
rescue EOFError
|
|
90
|
+
break
|
|
91
|
+
end
|
|
92
|
+
end
|
|
93
|
+
begin
|
|
94
|
+
inp.readline
|
|
95
|
+
rescue EOFError
|
|
96
|
+
puts 'EOF handled correctly'
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
if ARGV.size > 0
|
|
100
|
+
inp = Trepan::UserInput.open
|
|
101
|
+
begin
|
|
102
|
+
print "Type some characters: "
|
|
103
|
+
line = inp.readline()
|
|
104
|
+
puts "You typed: %s" % line
|
|
105
|
+
rescue EOFError
|
|
106
|
+
puts 'Got EOF'
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
|