trepanning 0.0.4
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
+
|