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/io/string_array.rb
ADDED
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
|
|
4
|
+
# Simulate I/O using lists of strings.
|
|
5
|
+
|
|
6
|
+
require_relative 'base_io'
|
|
7
|
+
|
|
8
|
+
# Simulate I/O using an array of strings. Sort of like StringIO, but
|
|
9
|
+
# even simplier.
|
|
10
|
+
class Trepan::StringArrayInput < Trepan::InputBase
|
|
11
|
+
|
|
12
|
+
def initialize(inp, opts={})
|
|
13
|
+
super
|
|
14
|
+
@closed = false
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
# this close() interface is defined for class compatibility
|
|
18
|
+
def close
|
|
19
|
+
@closed = true
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def closed?
|
|
23
|
+
@closed
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def eof?
|
|
27
|
+
@closed || @input.empty?
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Nothing to do here. Interface is for compatibility
|
|
31
|
+
def flush ; end
|
|
32
|
+
|
|
33
|
+
# Read a line of input. EOFError will be raised on EOF.
|
|
34
|
+
# Note that we don't support prompting
|
|
35
|
+
def readline
|
|
36
|
+
raise EOFError if eof?
|
|
37
|
+
if @input.empty?
|
|
38
|
+
raise EOFError
|
|
39
|
+
end
|
|
40
|
+
line = @input.shift
|
|
41
|
+
return line
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
class << self
|
|
45
|
+
# Use this to set where to read from.
|
|
46
|
+
def open(inp, opts={})
|
|
47
|
+
if inp.is_a?(Array)
|
|
48
|
+
return self.new(inp)
|
|
49
|
+
else
|
|
50
|
+
raise IOError, "Invalid input type (%s) for %s" % [inp.class, inp]
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Simulate I/O using an array of strings. Sort of like StringIO, but
|
|
57
|
+
# even simplier.
|
|
58
|
+
class Trepan::StringArrayOutput < Trepan::OutputBase
|
|
59
|
+
|
|
60
|
+
def initialize(out=[], opts={})
|
|
61
|
+
super
|
|
62
|
+
@closed = false
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Nothing to do here. Interface is for compatibility
|
|
66
|
+
def close
|
|
67
|
+
@closed = true
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def closed?
|
|
71
|
+
@closed
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def eof?
|
|
75
|
+
@closed || @output.empty?
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
# Nothing to do here. Interface is for compatibility
|
|
79
|
+
def flush ; end
|
|
80
|
+
|
|
81
|
+
# This method the debugger uses to write. In contrast to
|
|
82
|
+
# writeline, no newline is added to the end to `str'.
|
|
83
|
+
#
|
|
84
|
+
def write(msg)
|
|
85
|
+
raise ValueError if @closed
|
|
86
|
+
@output << msg
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# used to write to a debugger that is connected to this
|
|
90
|
+
# server; Here, we use the null string '' as an indicator of a
|
|
91
|
+
# newline.
|
|
92
|
+
def writeline(msg)
|
|
93
|
+
write(msg)
|
|
94
|
+
write('')
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
class << self
|
|
98
|
+
# Use this to set where to write to. output can be a
|
|
99
|
+
# file object or a string. This code raises IOError on error.
|
|
100
|
+
#
|
|
101
|
+
# If another file was previously open upon calling this open,
|
|
102
|
+
# that will be stacked and will come back into use after
|
|
103
|
+
# a close_write().
|
|
104
|
+
def open(output=[])
|
|
105
|
+
if output.is_a?(Array)
|
|
106
|
+
return self.new(output)
|
|
107
|
+
else
|
|
108
|
+
raise IOError, ("Invalid output type (%s) for %s" %
|
|
109
|
+
[output.class, output])
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Demo
|
|
116
|
+
if __FILE__ == $0
|
|
117
|
+
inp = Trepan::StringArrayInput.open(['Now is the time', 'for all good men'])
|
|
118
|
+
line = inp.readline
|
|
119
|
+
p line
|
|
120
|
+
line = inp.readline
|
|
121
|
+
p line
|
|
122
|
+
begin
|
|
123
|
+
line = inp.readline
|
|
124
|
+
rescue EOFError
|
|
125
|
+
puts 'EOF hit on read'
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
out = Trepan::StringArrayOutput.open
|
|
129
|
+
p out.output
|
|
130
|
+
# line = io.readline('Type some more characters: ')
|
|
131
|
+
out.writeline('Hello, world!')
|
|
132
|
+
p out.output
|
|
133
|
+
out.write('Hello');
|
|
134
|
+
p out.output
|
|
135
|
+
out.writeline(', again.');
|
|
136
|
+
p out.output
|
|
137
|
+
# io.open_write(sys.stdout)
|
|
138
|
+
out.flush_after_write = true
|
|
139
|
+
out.write('Last hello')
|
|
140
|
+
puts "Output is closed? #{out.closed?}"
|
|
141
|
+
out.close
|
|
142
|
+
p out.output
|
|
143
|
+
begin
|
|
144
|
+
out.writeline("You won't see me")
|
|
145
|
+
rescue
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
# Closing after already closed is okay
|
|
149
|
+
out.close
|
|
150
|
+
puts "Output is closed? #{out.closed?}"
|
|
151
|
+
puts "Input is closed? #{inp.closed?}"
|
|
152
|
+
inp.close
|
|
153
|
+
puts "Input is closed? #{inp.closed?}"
|
|
154
|
+
end
|
|
155
|
+
|
data/lib/Makefile
ADDED
data/lib/trepanning.rb
ADDED
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
require 'trace' # Trace filtering
|
|
4
|
+
require 'thread_frame'
|
|
5
|
+
require_relative '../app/core' # core event-handling mechanism
|
|
6
|
+
require_relative '../app/default' # default debugger settings
|
|
7
|
+
require_relative '../interface/user' # user interface (includes I/O)
|
|
8
|
+
require_relative '../interface/script' # --command interface (includes I/O)
|
|
9
|
+
|
|
10
|
+
# SCRIPT_ISEQS__ is like SCRIPT_LINES__ in a patched Ruby 1.9. Setting
|
|
11
|
+
# this variable to a hash causes instruction sequences to be added in
|
|
12
|
+
# this has under their "filename" as a key. More accurately though,
|
|
13
|
+
# the "filename" is instruction sequence name that was given as in the
|
|
14
|
+
# "filename" parameter when the instruction sequence was
|
|
15
|
+
# generated. Each value is an array of instruction sequences that
|
|
16
|
+
# share that name.
|
|
17
|
+
SCRIPT_ISEQS__ = {} unless
|
|
18
|
+
defined?(SCRIPT_ISEQS__) && SCRIPT_ISEQS__.is_a?(Hash)
|
|
19
|
+
ISEQS__ = {} unless
|
|
20
|
+
defined?(ISEQS__) && ISEQS__.is_a?(Hash)
|
|
21
|
+
|
|
22
|
+
class Trepan
|
|
23
|
+
VERSION = '0.0.4'
|
|
24
|
+
|
|
25
|
+
attr_accessor :core # access to Trepan::Core instance
|
|
26
|
+
attr_accessor :intf # Array. The way the outside world
|
|
27
|
+
# interfaces with us. An array, so that
|
|
28
|
+
# interfaces can be stacked.
|
|
29
|
+
attr_reader :initial_dir # String. Current directory when program
|
|
30
|
+
# started. Used in restart program.
|
|
31
|
+
attr_accessor :restart_argv # How to restart us, empty or nil.
|
|
32
|
+
# Note restart[0] is typically $0.
|
|
33
|
+
attr_reader :settings # Hash[:symbol] of things you can configure
|
|
34
|
+
attr_accessor :trace_filter # Procs/Methods we ignore.
|
|
35
|
+
|
|
36
|
+
def initialize(settings={})
|
|
37
|
+
|
|
38
|
+
# FIXME: Tracing through intialization code is slow. Need to figure
|
|
39
|
+
# out better ways to do this.
|
|
40
|
+
th = Thread.current
|
|
41
|
+
th.exec_event_tracing = true
|
|
42
|
+
|
|
43
|
+
@settings = Trepanning::DEFAULT_SETTINGS.merge(settings)
|
|
44
|
+
@input ||= @settings[:input]
|
|
45
|
+
@output ||= @settings[:output]
|
|
46
|
+
|
|
47
|
+
@intf = [Trepan::UserInterface.new(@input, @output)]
|
|
48
|
+
@settings[:cmdfiles].each do |cmdfile|
|
|
49
|
+
add_command_file(cmdfile)
|
|
50
|
+
end if @settings.member?(:cmdfiles)
|
|
51
|
+
@core = Core.new(self, @settings[:core_opts])
|
|
52
|
+
if @settings[:initial_dir]
|
|
53
|
+
Dir.chdir(@settings[:initial_dir])
|
|
54
|
+
else
|
|
55
|
+
@settings[:initial_dir] = Dir.pwd
|
|
56
|
+
end
|
|
57
|
+
@initial_dir = @settings[:initial_dir]
|
|
58
|
+
@restart_argv =
|
|
59
|
+
if @settings[:set_restart]
|
|
60
|
+
[File.expand_path($0)] + ARGV
|
|
61
|
+
elsif @settings[:restart_argv]
|
|
62
|
+
@settings[:restart_argv]
|
|
63
|
+
else
|
|
64
|
+
nil
|
|
65
|
+
end
|
|
66
|
+
@trace_filter = Trace::Filter.new
|
|
67
|
+
%w(debugger start stop).each do |m|
|
|
68
|
+
@trace_filter << self.method(m.to_sym)
|
|
69
|
+
end
|
|
70
|
+
%w(debugger event_processor trace_var_processor).each do
|
|
71
|
+
|m|
|
|
72
|
+
@trace_filter << @core.method(m)
|
|
73
|
+
end
|
|
74
|
+
@trace_filter << @trace_filter.method(:add_trace_func)
|
|
75
|
+
@trace_filter << @trace_filter.method(:remove_trace_func)
|
|
76
|
+
@trace_filter << Kernel.method(:add_trace_func)
|
|
77
|
+
|
|
78
|
+
# Run user debugger command startup files.
|
|
79
|
+
add_startup_files unless @settings[:nx]
|
|
80
|
+
add_command_file(@settings[:restore_profile]) if
|
|
81
|
+
@settings[:restore_profile] && File.readable?(@settings[:restore_profile])
|
|
82
|
+
|
|
83
|
+
at_exit do
|
|
84
|
+
clear_trace_func
|
|
85
|
+
@intf[-1].close
|
|
86
|
+
end
|
|
87
|
+
th.exec_event_tracing = false
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# To call from inside a Ruby program, there is one-time setup that
|
|
91
|
+
# needs to be done first:
|
|
92
|
+
# require 'trepanning'
|
|
93
|
+
# mydbg = Trepan.new()
|
|
94
|
+
# or if you haven't mucked around with $0 and ARGV, you might try:
|
|
95
|
+
# mydbg = Trepan.new(:set_restart=>true))
|
|
96
|
+
# which will tell the debugger how to "restart" the program.
|
|
97
|
+
#
|
|
98
|
+
# If you want a synchronous stop in your program call to the
|
|
99
|
+
# debugger at the point of the call, set opts[:immediate]
|
|
100
|
+
# true. Example:
|
|
101
|
+
#
|
|
102
|
+
# ... work, work, work
|
|
103
|
+
# mydbg.debugger(:immediate=>true) # enter debugger here
|
|
104
|
+
# ... work, work, work
|
|
105
|
+
#
|
|
106
|
+
# However to enter the debugger on the next event after the
|
|
107
|
+
# debugger() call:
|
|
108
|
+
#
|
|
109
|
+
# ... work, work, work
|
|
110
|
+
# mydbg.debugger # Don't stop here...
|
|
111
|
+
# work # but stop here.
|
|
112
|
+
#
|
|
113
|
+
# And finally, if you want to debug just a block:
|
|
114
|
+
# mydbg.debugger {
|
|
115
|
+
# ... code you want to debug.
|
|
116
|
+
# }
|
|
117
|
+
#
|
|
118
|
+
# Some options
|
|
119
|
+
|
|
120
|
+
# :immediate - boolean. If true, immediate stop rather than wait
|
|
121
|
+
# for an event
|
|
122
|
+
#
|
|
123
|
+
# :hide_stack - boolean. If true, omit stack frames before the
|
|
124
|
+
# debugger call
|
|
125
|
+
#
|
|
126
|
+
# :debugme - boolean. Allow tracing into this routine. You
|
|
127
|
+
# generally won't want this. It slows things
|
|
128
|
+
# down horribly.
|
|
129
|
+
|
|
130
|
+
def debugger(opts={}, &block)
|
|
131
|
+
# FIXME: one option we may want to pass is the initial trace filter.
|
|
132
|
+
if opts[:hide_stack]
|
|
133
|
+
@core.processor.hidelevels[Thread.current] =
|
|
134
|
+
RubyVM::ThreadFrame.current.stack_size
|
|
135
|
+
end
|
|
136
|
+
th = Thread.current
|
|
137
|
+
if block
|
|
138
|
+
start
|
|
139
|
+
# I don't think yield or block.call is quite right.
|
|
140
|
+
ret = yield # Not: block.call(self) ?
|
|
141
|
+
stop
|
|
142
|
+
return ret
|
|
143
|
+
elsif opts[:immediate]
|
|
144
|
+
# Stop immediately after this method returns. But if opts[:debugme]
|
|
145
|
+
# is set, we can stop in this method.
|
|
146
|
+
RubyVM::ThreadFrame::current.trace_off = true unless opts[:debugme]
|
|
147
|
+
@trace_filter.set_trace_func(@core.event_proc)
|
|
148
|
+
Trace.event_masks[0] |= @core.step_events
|
|
149
|
+
@core.debugger(1)
|
|
150
|
+
else
|
|
151
|
+
RubyVM::ThreadFrame::current.trace_off = true unless opts[:debugme]
|
|
152
|
+
|
|
153
|
+
@trace_filter.set_trace_func(@core.event_proc)
|
|
154
|
+
Trace.event_masks[0] |= @core.step_events
|
|
155
|
+
|
|
156
|
+
# Set to stop on the next event after this returns.
|
|
157
|
+
@core.step_count = 0
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
# Set core's trace-event processor to run
|
|
162
|
+
def start
|
|
163
|
+
@trace_filter.add_trace_func(@core.event_proc)
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
# Remove all of our trace events
|
|
167
|
+
def stop(opts={})
|
|
168
|
+
# FIXME: should do something in the middle when
|
|
169
|
+
# we have the ability to remove *our* specific hook
|
|
170
|
+
# @trace_filter.set_trace_func(nil)
|
|
171
|
+
# @trace_filter.remove_trace_func
|
|
172
|
+
clear_trace_func
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
def add_command_file(cmdfile, stderr=$stderr)
|
|
176
|
+
unless File.readable?(cmdfile)
|
|
177
|
+
if File.exists?(cmdfile)
|
|
178
|
+
stderr.puts "Command file '#{cmdfile}' is not readable."
|
|
179
|
+
return
|
|
180
|
+
else
|
|
181
|
+
stderr.puts "Command file '#{cmdfile}' does not exist."
|
|
182
|
+
stderr.puts caller
|
|
183
|
+
return
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
@intf << Trepan::ScriptInterface.new(cmdfile, @output)
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
def add_startup_files()
|
|
190
|
+
seen = {}
|
|
191
|
+
cwd_initfile = File.join('.', Trepanning::CMD_INITFILE_BASE)
|
|
192
|
+
[cwd_initfile, Trepanning::CMD_INITFILE].each do |initfile|
|
|
193
|
+
full_initfile_path = File.expand_path(initfile)
|
|
194
|
+
next if seen[full_initfile_path]
|
|
195
|
+
add_command_file(full_initfile_path) if File.readable?(full_initfile_path)
|
|
196
|
+
seen[full_initfile_path] = true
|
|
197
|
+
end
|
|
198
|
+
end
|
|
199
|
+
|
|
200
|
+
# As a simplification for creating a debugger object, and then
|
|
201
|
+
# calling using the object to invoke the debugger, we allow this
|
|
202
|
+
# two-step process in one step. That is, instead of
|
|
203
|
+
#
|
|
204
|
+
# require 'trepanning'
|
|
205
|
+
# mydbg = Trepan.new()
|
|
206
|
+
# ...
|
|
207
|
+
# mydbg.debugger
|
|
208
|
+
|
|
209
|
+
# You can run:
|
|
210
|
+
# require 'trepanning'
|
|
211
|
+
# ...
|
|
212
|
+
# Trepan.debug
|
|
213
|
+
#
|
|
214
|
+
# See debugger for options that can be passed. By default :hide_stack is
|
|
215
|
+
# set.
|
|
216
|
+
#
|
|
217
|
+
# Likewise for mydbg.debugger{ ... }
|
|
218
|
+
|
|
219
|
+
def self.debug(opts={}, &block)
|
|
220
|
+
opts = {:hide_stack => true}.merge(opts)
|
|
221
|
+
unless defined?($trepanning) && $trepanning.is_a?(Trepan)
|
|
222
|
+
$trepanning = Trepan.new(opts)
|
|
223
|
+
$trepanning.trace_filter << self.method(:debug)
|
|
224
|
+
end
|
|
225
|
+
$trepanning.debugger(opts, &block)
|
|
226
|
+
end
|
|
227
|
+
|
|
228
|
+
def self.debug_str(string, opts = DEFAULT_DEBUG_STR_SETTINGS)
|
|
229
|
+
$trepanning = Trepan.new(opts) unless
|
|
230
|
+
$trepanning && $trepanning.is_a?(Trepan)
|
|
231
|
+
$trepanning.core.processor.settings[:different] = false
|
|
232
|
+
# Perhaps we should do a remap file to string right here?
|
|
233
|
+
$trepanning.debugger(opts) { eval(string) }
|
|
234
|
+
end
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
module Kernel
|
|
238
|
+
# Same as Trepan.debug.
|
|
239
|
+
# FIXME figure out a way to remove duplication.
|
|
240
|
+
def trepan(opts={}, &block)
|
|
241
|
+
opts = {:hide_stack => true}.merge(opts)
|
|
242
|
+
unless defined?($trepanning) && $trepanning.is_a?(Trepan)
|
|
243
|
+
$trepanning = Trepan.new
|
|
244
|
+
$trepanning.trace_filter << self.method(:trepan)
|
|
245
|
+
end
|
|
246
|
+
$trepanning.debugger(opts, &block)
|
|
247
|
+
end
|
|
248
|
+
end
|
|
249
|
+
|
|
250
|
+
if __FILE__ == $0
|
|
251
|
+
def square(x)
|
|
252
|
+
x * x
|
|
253
|
+
end
|
|
254
|
+
puts 'block debugging...'
|
|
255
|
+
# It is imagined that there are all sorts of command-line options here.
|
|
256
|
+
# (I have a good imagination.)
|
|
257
|
+
Trepan.debug(:set_restart=>true) {
|
|
258
|
+
a = 2
|
|
259
|
+
b = square(a)
|
|
260
|
+
p "square of #{a} is #{b}"
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
puts 'immediate debugging...'
|
|
264
|
+
$trepanning.debugger(:immediate => true)
|
|
265
|
+
puts 'line after immediate'
|
|
266
|
+
a = 3
|
|
267
|
+
square(a)
|
|
268
|
+
|
|
269
|
+
class MyClass
|
|
270
|
+
def initialize(x)
|
|
271
|
+
@x = x
|
|
272
|
+
end
|
|
273
|
+
end
|
|
274
|
+
$trepanning.debugger
|
|
275
|
+
m = MyClass.new(5)
|
|
276
|
+
raise RuntimeError # To see how we handle post-mortem debugging.
|
|
277
|
+
end
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
require_relative '../app/core'
|
|
3
|
+
class Trepan
|
|
4
|
+
|
|
5
|
+
class CmdProcessor
|
|
6
|
+
|
|
7
|
+
attr_reader :brkpts # BreakpointManager.
|
|
8
|
+
|
|
9
|
+
attr_reader :brkpt # Breakpoint. If we are stopped at a
|
|
10
|
+
# breakpoint this is the one we
|
|
11
|
+
# found. (There may be other
|
|
12
|
+
# breakpoints that would have caused a stop
|
|
13
|
+
# as well; this is just one of them).
|
|
14
|
+
# If no breakpoint stop this is nil.
|
|
15
|
+
|
|
16
|
+
def breakpoint_initialize
|
|
17
|
+
@brkpts = BreakpointMgr.new
|
|
18
|
+
@brkpt = nil
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def breakpoint?
|
|
22
|
+
@brkpt = @brkpts.find(@frame.iseq, @frame.pc_offset, @frame.binding)
|
|
23
|
+
@brkpts.delete_by_brkpt(@brkpt) if @brkpt && @brkpt.temp?
|
|
24
|
+
return !!@brkpt
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def breakpoint_find(bpnum, show_errmsg = true)
|
|
28
|
+
if 0 == @brkpts.size
|
|
29
|
+
errmsg('No breakpoints set.') if show_errmsg
|
|
30
|
+
return nil
|
|
31
|
+
elsif bpnum > @brkpts.max || bpnum < 1
|
|
32
|
+
errmsg('Breakpoint number %d is out of range 1..%d' %
|
|
33
|
+
[bpnum, @brkpts.max]) if show_errmsg
|
|
34
|
+
return nil
|
|
35
|
+
end
|
|
36
|
+
bp = @brkpts[bpnum]
|
|
37
|
+
if bp
|
|
38
|
+
return bp
|
|
39
|
+
else
|
|
40
|
+
errmsg('Breakpoint number %d previously deleted.' %
|
|
41
|
+
bpnum) if show_errmsg
|
|
42
|
+
return nil
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
# Does whatever needs to be done to set a breakpoint
|
|
47
|
+
def breakpoint_line(line_number, iseq, temp=false)
|
|
48
|
+
# FIXME: handle breakpoint conditions.
|
|
49
|
+
found_iseq = iseq.child_iseqs.detect do |iseq|
|
|
50
|
+
iseq.lineoffsets.keys.member?(line_number)
|
|
51
|
+
end
|
|
52
|
+
unless found_iseq
|
|
53
|
+
found_iseq = iseq.parent
|
|
54
|
+
while found_iseq do
|
|
55
|
+
break if found_iseq.lineoffsets.keys.member?(line_number)
|
|
56
|
+
found_iseq = found_iseq.parent
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
offset =
|
|
60
|
+
if found_iseq
|
|
61
|
+
# FIXME
|
|
62
|
+
found_iseq.line2offsets(line_number)[1] ||
|
|
63
|
+
found_iseq.line2offsets(line_number)[0]
|
|
64
|
+
else
|
|
65
|
+
nil
|
|
66
|
+
end
|
|
67
|
+
unless offset
|
|
68
|
+
place = "in #{iseq.source_container.join(' ')} " if found_iseq
|
|
69
|
+
errmsg("No line #{line_number} found #{place}for breakpoint.")
|
|
70
|
+
return nil
|
|
71
|
+
end
|
|
72
|
+
@brkpts.add(found_iseq, offset, :temp => temp)
|
|
73
|
+
end
|
|
74
|
+
|
|
75
|
+
def breakpoint_offset(offset, iseq, temp=false)
|
|
76
|
+
# FIXME: handle breakpoint conditions.
|
|
77
|
+
unless iseq.offsetlines.keys.member?(offset)
|
|
78
|
+
errmsg("Offset #{offset} not found in #{iseq.name} for breakpoint.")
|
|
79
|
+
return nil
|
|
80
|
+
end
|
|
81
|
+
@brkpts.add(iseq, offset, :temp => temp, :type => 'offset')
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
# Delete a breakpoint given its breakpoint number.
|
|
85
|
+
def delete_breakpoint_by_number(bpnum, do_enable=true)
|
|
86
|
+
bp = breakpoint_find(bpnum)
|
|
87
|
+
return false unless bp
|
|
88
|
+
|
|
89
|
+
@brkpts.delete_by_brkpt(bp)
|
|
90
|
+
return true
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Enable or disable a breakpoint given its breakpoint number.
|
|
94
|
+
def en_disable_breakpoint_by_number(bpnum, do_enable=true)
|
|
95
|
+
bp = breakpoint_find(bpnum)
|
|
96
|
+
return false unless bp
|
|
97
|
+
|
|
98
|
+
enable_disable = do_enable ? 'en' : 'dis'
|
|
99
|
+
if bp.enabled? == do_enable
|
|
100
|
+
errmsg('Breakpoint %d previously %sabled.' %
|
|
101
|
+
[bpnum, enable_disable])
|
|
102
|
+
return false
|
|
103
|
+
end
|
|
104
|
+
bp.enabled = do_enable
|
|
105
|
+
return true
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
require_relative 'base/cmd'
|
|
3
|
+
|
|
4
|
+
class Trepan::Command::AliasCommand < Trepan::Command
|
|
5
|
+
|
|
6
|
+
unless defined?(HELP)
|
|
7
|
+
HELP =
|
|
8
|
+
"alias ALIAS COMMAND
|
|
9
|
+
|
|
10
|
+
Add an alias for a COMMAND
|
|
11
|
+
|
|
12
|
+
See also 'unalias'.
|
|
13
|
+
"
|
|
14
|
+
|
|
15
|
+
CATEGORY = 'support'
|
|
16
|
+
MAX_ARGS = 2 # Need at most this many
|
|
17
|
+
NAME = File.basename(__FILE__, '.rb')
|
|
18
|
+
NEED_STACK = true
|
|
19
|
+
SHORT_HELP = 'Add an alias for a debugger command'
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
# Run command.
|
|
23
|
+
def run(args)
|
|
24
|
+
if args.size == 1
|
|
25
|
+
@proc.commands['show'].run(%w(show alias))
|
|
26
|
+
elsif args.size == 2
|
|
27
|
+
@proc.commands['show'].run(%W(show alias #{args[1]}))
|
|
28
|
+
else
|
|
29
|
+
junk, al, command = args
|
|
30
|
+
old_command = @proc.aliases[al]
|
|
31
|
+
if @proc.commands.member?(command)
|
|
32
|
+
@proc.aliases[al] = command
|
|
33
|
+
if old_command
|
|
34
|
+
msg("Alias '#{al}' for command '#{command}' replaced old " +
|
|
35
|
+
"alias for '#{old_command}'.")
|
|
36
|
+
else
|
|
37
|
+
msg "New alias '#{al}' for command '#{command}' created."
|
|
38
|
+
end
|
|
39
|
+
else
|
|
40
|
+
errmsg "You must alias to a command name, and '#{command}' isn't one."
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
if __FILE__ == $0
|
|
47
|
+
# Demo it.
|
|
48
|
+
require_relative '../mock'
|
|
49
|
+
name = File.basename(__FILE__, '.rb')
|
|
50
|
+
dbgr, cmd = MockDebugger::setup(name)
|
|
51
|
+
cmd.run %w(alias yy foo)
|
|
52
|
+
cmd.run %w(alias yy step)
|
|
53
|
+
cmd.run %w(alias)
|
|
54
|
+
cmd.run %w(alias yy next)
|
|
55
|
+
end
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
require_relative 'base/cmd'
|
|
4
|
+
class Trepan::Command::BacktraceCommand < Trepan::Command
|
|
5
|
+
|
|
6
|
+
unless defined?(HELP)
|
|
7
|
+
NAME = File.basename(__FILE__, '.rb')
|
|
8
|
+
HELP = <<-HELP
|
|
9
|
+
#{NAME} [count]
|
|
10
|
+
|
|
11
|
+
Print a stack trace, with the most recent frame at the top. With a
|
|
12
|
+
positive number, print at most many entries. With a negative number
|
|
13
|
+
print the top entries minus that number.
|
|
14
|
+
|
|
15
|
+
An arrow indicates the 'current frame'. The current frame determines
|
|
16
|
+
the context used for many debugger commands such as expression
|
|
17
|
+
evaluation or source-line listing.
|
|
18
|
+
|
|
19
|
+
Examples:
|
|
20
|
+
#{NAME} # Print a full stack trace
|
|
21
|
+
#{NAME} 2 # Print only the top two entries
|
|
22
|
+
#{NAME} -1 # Print a stack trace except the initial (least recent) call."
|
|
23
|
+
HELP
|
|
24
|
+
|
|
25
|
+
ALIASES = %w(bt where)
|
|
26
|
+
CATEGORY = 'stack'
|
|
27
|
+
MAX_ARGS = 1 # Need at most this many
|
|
28
|
+
NEED_STACK = true
|
|
29
|
+
SHORT_HELP = 'Print backtrace of stack frames'
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
require_relative '../../app/frame'
|
|
33
|
+
include Trepan::Frame
|
|
34
|
+
|
|
35
|
+
# This method runs the command
|
|
36
|
+
def run(args) # :nodoc
|
|
37
|
+
unless @proc.frame
|
|
38
|
+
errmsg 'No frame.'
|
|
39
|
+
return false
|
|
40
|
+
end
|
|
41
|
+
hide_level =
|
|
42
|
+
if !settings[:debugstack] && @proc.hidelevels[Thread.current]
|
|
43
|
+
@proc.hidelevels[Thread.current]
|
|
44
|
+
else 0
|
|
45
|
+
end
|
|
46
|
+
stack_size = @proc.top_frame.stack_size - hide_level
|
|
47
|
+
opts = {
|
|
48
|
+
:basename => @proc.settings[:basename],
|
|
49
|
+
:current_pos => @proc.frame_index,
|
|
50
|
+
:maxstack => @proc.settings[:maxstack],
|
|
51
|
+
:maxwidth => @proc.settings[:maxwidth],
|
|
52
|
+
:show_pc => @proc.settings[:show_pc]
|
|
53
|
+
}
|
|
54
|
+
opts[:count] =
|
|
55
|
+
if args.size > 1
|
|
56
|
+
opts[:maxstack] = @proc.get_int(args[1],
|
|
57
|
+
:cmdname => 'where',
|
|
58
|
+
:max_value => stack_size)
|
|
59
|
+
else
|
|
60
|
+
stack_size
|
|
61
|
+
end
|
|
62
|
+
return false unless opts[:count]
|
|
63
|
+
# FIXME: Fix Ruby so we don't need this workaround?
|
|
64
|
+
# See also location.rb
|
|
65
|
+
opts[:class] = @proc.core.hook_arg if
|
|
66
|
+
'CFUNC' == @proc.frame.type &&
|
|
67
|
+
@proc.core.hook_arg && @proc.event != 'raise'
|
|
68
|
+
print_stack_trace(@proc.top_frame, opts)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
if __FILE__ == $0
|
|
73
|
+
# Demo it.
|
|
74
|
+
require 'thread_frame'
|
|
75
|
+
require_relative '../mock'
|
|
76
|
+
name = File.basename(__FILE__, '.rb')
|
|
77
|
+
dbgr, cmd = MockDebugger::setup(name)
|
|
78
|
+
|
|
79
|
+
def run_cmd(cmd, args)
|
|
80
|
+
cmd.run(args)
|
|
81
|
+
puts '=' * 40
|
|
82
|
+
end
|
|
83
|
+
|
|
84
|
+
run_cmd(cmd, [name])
|
|
85
|
+
|
|
86
|
+
%w(1 100).each {|count| run_cmd(cmd, [name, count])}
|
|
87
|
+
cmd.settings[:basename] = true
|
|
88
|
+
def foo(cmd, name)
|
|
89
|
+
cmd.proc.frame_setup(RubyVM::ThreadFrame::current)
|
|
90
|
+
run_cmd(cmd, [name])
|
|
91
|
+
end
|
|
92
|
+
foo(cmd, name)
|
|
93
|
+
cmd.settings[:show_pc] = true
|
|
94
|
+
1.times {run_cmd(cmd, [name])}
|
|
95
|
+
end
|