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/app/frame.rb
ADDED
|
@@ -0,0 +1,275 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
require_relative 'util'
|
|
3
|
+
|
|
4
|
+
class Trepan
|
|
5
|
+
|
|
6
|
+
module Frame
|
|
7
|
+
|
|
8
|
+
DEFAULT_STACK_TRACE_SETTINGS = {
|
|
9
|
+
:basename => false, # Basename only for files?
|
|
10
|
+
:maxstack => 16, # How many entries to show? nil means all
|
|
11
|
+
:count => nil, # How many entries to show? nil means all
|
|
12
|
+
:current_pos => 0, # Where are we in the stack?
|
|
13
|
+
:show_pc => false, # Show PC offset?
|
|
14
|
+
} unless defined?(DEFAULT_STACK_TRACE_SETTINGS)
|
|
15
|
+
|
|
16
|
+
include Trepan::Util
|
|
17
|
+
|
|
18
|
+
def all_param_names(iseq, delineate=true)
|
|
19
|
+
return '' unless iseq
|
|
20
|
+
params = param_names(iseq, 0, iseq.argc-1, '')
|
|
21
|
+
if iseq.arg_opts > 0
|
|
22
|
+
opt_params = param_names(iseq, iseq.argc,
|
|
23
|
+
iseq.argc + iseq.arg_opts-2, '')
|
|
24
|
+
opt_params[0] = "optional: #{opt_params[0]}" if delineate
|
|
25
|
+
params += opt_params
|
|
26
|
+
end
|
|
27
|
+
params += param_names(iseq, iseq.arg_rest, iseq.arg_rest, '*') if
|
|
28
|
+
iseq.arg_rest != -1
|
|
29
|
+
if iseq.arg_post_len > 0
|
|
30
|
+
# Manditory arguments after optional ones - new in Ruby 1.9
|
|
31
|
+
post_params = param_names(iseq, iseq.arg_post_start,
|
|
32
|
+
iseq.post_start + iseq.arg_post_len, '')
|
|
33
|
+
post_params[0] = "post: #{post_params[0]}" if delineate
|
|
34
|
+
params += post_params
|
|
35
|
+
end
|
|
36
|
+
params += param_names(iseq, iseq.arg_block, iseq.arg_block, '&') if
|
|
37
|
+
iseq.arg_block != -1
|
|
38
|
+
|
|
39
|
+
return params
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def c_params(frame, maxstring=20)
|
|
43
|
+
argc = frame.argc
|
|
44
|
+
# FIXME should figure out why exception is raised.
|
|
45
|
+
begin
|
|
46
|
+
args =
|
|
47
|
+
if 0 == argc
|
|
48
|
+
''
|
|
49
|
+
elsif frame
|
|
50
|
+
1.upto(argc).map do
|
|
51
|
+
|i|
|
|
52
|
+
safe_repr(frame.sp(argc-i+3).inspect, 10)
|
|
53
|
+
end.join(', ')
|
|
54
|
+
else
|
|
55
|
+
'??'
|
|
56
|
+
end
|
|
57
|
+
safe_repr(args, maxstring)
|
|
58
|
+
rescue NotImplementedError
|
|
59
|
+
'??'
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
# Return the eval string. We get this as the
|
|
64
|
+
# parameter to the eval C call. A bit of checking is done
|
|
65
|
+
# to make sure everything is okay:
|
|
66
|
+
# - we have to be in an EVAL type frame which has an iseq
|
|
67
|
+
# - we have to have a prev frame which is a CFUNC
|
|
68
|
+
# - the prev method name has to be 'eval'
|
|
69
|
+
def eval_string(frame)
|
|
70
|
+
return nil unless 'EVAL' == frame.type && frame.iseq
|
|
71
|
+
prev = frame.prev
|
|
72
|
+
return nil unless prev && 'CFUNC' == prev.type && 'eval' == prev.method
|
|
73
|
+
retval = prev.sp 3
|
|
74
|
+
retval = $1 if retval =~ /^\(eval "(.+)"\)/
|
|
75
|
+
retval
|
|
76
|
+
end
|
|
77
|
+
module_function :eval_string
|
|
78
|
+
|
|
79
|
+
def format_stack_call(frame, opts)
|
|
80
|
+
# FIXME: prettify
|
|
81
|
+
s = "#{frame.type}"
|
|
82
|
+
s += if opts[:class]
|
|
83
|
+
" #{opts[:class]}#"
|
|
84
|
+
else
|
|
85
|
+
begin
|
|
86
|
+
obj = eval('self', frame.binding)
|
|
87
|
+
rescue
|
|
88
|
+
''
|
|
89
|
+
else
|
|
90
|
+
if obj
|
|
91
|
+
" #{obj.class}#"
|
|
92
|
+
else
|
|
93
|
+
''
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
meth = frame.method
|
|
98
|
+
if meth and frame.type != 'IFUNC'
|
|
99
|
+
iseq = frame.iseq
|
|
100
|
+
args = if 'CFUNC' == frame.type
|
|
101
|
+
c_params(frame)
|
|
102
|
+
elsif iseq
|
|
103
|
+
all_param_names(iseq).join(', ')
|
|
104
|
+
end
|
|
105
|
+
s += meth
|
|
106
|
+
if %w(CFUNC METHOD).member?(frame.type)
|
|
107
|
+
s += "(#{args})"
|
|
108
|
+
elsif %w(BLOCK LAMBDA TOP EVAL).member?(frame.type)
|
|
109
|
+
s += " |#{args}|" unless args.nil? || args.empty?
|
|
110
|
+
else
|
|
111
|
+
s += "(#{all_param_names(iseq)})"
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
s
|
|
115
|
+
rescue ThreadFrameError
|
|
116
|
+
'invalid frame'
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def format_stack_entry(frame, opts={})
|
|
120
|
+
return 'invalid frame' if frame.invalid?
|
|
121
|
+
s = format_stack_call(frame, opts)
|
|
122
|
+
s += " in #{frame.source_container[0]} "
|
|
123
|
+
s +=
|
|
124
|
+
if (eval_str = eval_string(frame))
|
|
125
|
+
safe_repr(eval_str.inspect, 15)
|
|
126
|
+
else
|
|
127
|
+
if 'file' == frame.source_container[0] &&
|
|
128
|
+
opts[:basename]
|
|
129
|
+
File.basename(frame.source_container[1])
|
|
130
|
+
else
|
|
131
|
+
frame.source_container[1]
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
if frame.source_location
|
|
135
|
+
s +=
|
|
136
|
+
if opts[:maxwidth] && s.size > opts[:maxwidth]
|
|
137
|
+
"\n\t"
|
|
138
|
+
else
|
|
139
|
+
' '
|
|
140
|
+
end
|
|
141
|
+
if frame.source_location.size == 1
|
|
142
|
+
s += "at line #{frame.source_location[0]}" if
|
|
143
|
+
frame.source_location[0] != 0
|
|
144
|
+
else
|
|
145
|
+
s += " at lines #{frame.source_location}"
|
|
146
|
+
end
|
|
147
|
+
end
|
|
148
|
+
s += ", pc: #{frame.pc_offset}" if
|
|
149
|
+
frame.pc_offset > 0 && opts[:show_pc]
|
|
150
|
+
return s
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
def offset_for_return(event)
|
|
154
|
+
raise RuntimeError unless %w(return c-return).member?(event)
|
|
155
|
+
'return' == event ? 1 : 2
|
|
156
|
+
end
|
|
157
|
+
module_function :offset_for_return
|
|
158
|
+
|
|
159
|
+
def param_names(iseq, start, stop, prefix='')
|
|
160
|
+
start.upto(stop).map do |i|
|
|
161
|
+
begin
|
|
162
|
+
prefix + iseq.local_name(i)
|
|
163
|
+
rescue
|
|
164
|
+
nil
|
|
165
|
+
end
|
|
166
|
+
end.compact
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def print_stack_entry(frame, i, prefix=' ', opts={})
|
|
170
|
+
opts[:class] = nil unless i == 0
|
|
171
|
+
msg "%s%s" % [prefix, format_stack_entry(frame, opts)]
|
|
172
|
+
end
|
|
173
|
+
|
|
174
|
+
def print_stack_trace_from_to(from, to, frame, opts)
|
|
175
|
+
from.upto(to) do |i|
|
|
176
|
+
prefix = (i == opts[:current_pos]) ? '-->' : ' '
|
|
177
|
+
prefix += ' #%d ' % [i]
|
|
178
|
+
print_stack_entry(frame, i, prefix, opts)
|
|
179
|
+
frame = frame.prev
|
|
180
|
+
end
|
|
181
|
+
end
|
|
182
|
+
|
|
183
|
+
# Print `count' frame entries
|
|
184
|
+
def print_stack_trace(frame, opts={})
|
|
185
|
+
opts = DEFAULT_STACK_TRACE_SETTINGS.merge(opts)
|
|
186
|
+
halfstack = opts[:maxstack] / 2
|
|
187
|
+
n = frame.stack_size
|
|
188
|
+
n = [n, opts[:count]].min if opts[:count]
|
|
189
|
+
if n > (halfstack * 2)
|
|
190
|
+
print_stack_trace_from_to(0, halfstack-1, frame, opts)
|
|
191
|
+
msg "... %d levels ..." % (n - halfstack*2)
|
|
192
|
+
print_stack_trace_from_to(n - halfstack, n-1, frame, opts)
|
|
193
|
+
else
|
|
194
|
+
print_stack_trace_from_to(0, n-1, frame, opts)
|
|
195
|
+
end
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
def set_return_value(frame, event, value)
|
|
199
|
+
offset = offset_for_return(event)
|
|
200
|
+
frame.sp_set(offset, value)
|
|
201
|
+
end
|
|
202
|
+
module_function :set_return_value
|
|
203
|
+
|
|
204
|
+
def value_returned(frame, event)
|
|
205
|
+
frame.sp(offset_for_return(event))
|
|
206
|
+
end
|
|
207
|
+
module_function :value_returned
|
|
208
|
+
end
|
|
209
|
+
end
|
|
210
|
+
|
|
211
|
+
if __FILE__ == $0
|
|
212
|
+
# Demo it.
|
|
213
|
+
require 'thread_frame'
|
|
214
|
+
include Trepan::Frame
|
|
215
|
+
def msg(msg)
|
|
216
|
+
puts msg
|
|
217
|
+
end
|
|
218
|
+
print_stack_trace(RubyVM::ThreadFrame.current, :basename => true)
|
|
219
|
+
def foo
|
|
220
|
+
puts '=' * 10
|
|
221
|
+
print_stack_trace(RubyVM::ThreadFrame.current, :show_pc => true)
|
|
222
|
+
end
|
|
223
|
+
foo
|
|
224
|
+
|
|
225
|
+
def bar(a, b, c)
|
|
226
|
+
puts '=' * 10
|
|
227
|
+
print_stack_trace(RubyVM::ThreadFrame.current,
|
|
228
|
+
)
|
|
229
|
+
end
|
|
230
|
+
bar(1, 2, 3)
|
|
231
|
+
|
|
232
|
+
def baz(a, b, c=5)
|
|
233
|
+
puts '=' * 10
|
|
234
|
+
print_stack_trace(RubyVM::ThreadFrame.current)
|
|
235
|
+
end
|
|
236
|
+
baz(1, 2)
|
|
237
|
+
|
|
238
|
+
def bat(a, b, &block)
|
|
239
|
+
puts '=' * 10
|
|
240
|
+
print_stack_trace(RubyVM::ThreadFrame.current)
|
|
241
|
+
end
|
|
242
|
+
bat(1, 2)
|
|
243
|
+
|
|
244
|
+
def babe(a, b, *rest)
|
|
245
|
+
puts '=' * 10
|
|
246
|
+
print_stack_trace(RubyVM::ThreadFrame.current)
|
|
247
|
+
end
|
|
248
|
+
babe(1, 2)
|
|
249
|
+
|
|
250
|
+
puts '=' * 10
|
|
251
|
+
x = lambda { |a,b| print_stack_trace(RubyVM::ThreadFrame::current) }
|
|
252
|
+
x.call(1,2)
|
|
253
|
+
puts '=' * 10
|
|
254
|
+
x = Proc.new do |a|
|
|
255
|
+
print_stack_trace(RubyVM::ThreadFrame::current)
|
|
256
|
+
end
|
|
257
|
+
x.call(1,2)
|
|
258
|
+
class C
|
|
259
|
+
def initialize(a)
|
|
260
|
+
print_stack_trace(RubyVM::ThreadFrame::current)
|
|
261
|
+
end
|
|
262
|
+
end
|
|
263
|
+
puts '=' * 30
|
|
264
|
+
C.new('Hi')
|
|
265
|
+
puts '=' * 30
|
|
266
|
+
eval("print_stack_trace(RubyVM::ThreadFrame.current)")
|
|
267
|
+
puts '=' * 30
|
|
268
|
+
eval("eval('print_stack_trace(RubyVM::ThreadFrame.current)')")
|
|
269
|
+
puts '=' * 30
|
|
270
|
+
eval("eval('print_stack_trace(RubyVM::ThreadFrame.current, :maxstack => 2)')")
|
|
271
|
+
puts '=' * 30
|
|
272
|
+
1.times do |a; b|
|
|
273
|
+
print_stack_trace(RubyVM::ThreadFrame::current)
|
|
274
|
+
end
|
|
275
|
+
end
|
data/app/irb.rb
ADDED
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
# This code comes more or less from ruby-debug.
|
|
3
|
+
require 'irb'
|
|
4
|
+
module IRB # :nodoc:
|
|
5
|
+
module ExtendCommand # :nodoc:
|
|
6
|
+
# FIXME: should we read these out of a directory to
|
|
7
|
+
# make this more user-customizable?
|
|
8
|
+
|
|
9
|
+
# A base command class that resume execution
|
|
10
|
+
class DebuggerResumeCommand
|
|
11
|
+
def self.execute(conf, *opts)
|
|
12
|
+
name =
|
|
13
|
+
if self.name =~ /IRB::ExtendCommand::(\S+)/
|
|
14
|
+
$1.downcase
|
|
15
|
+
else
|
|
16
|
+
'unknown'
|
|
17
|
+
end
|
|
18
|
+
$trepan_args = opts
|
|
19
|
+
$trepan_command =
|
|
20
|
+
if $trepan_irb_statements
|
|
21
|
+
$trepan_irb_statements
|
|
22
|
+
else
|
|
23
|
+
([name] + opts).join(' ')
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
throw :IRB_EXIT, name.to_sym
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
class Continue < DebuggerResumeCommand ; end
|
|
31
|
+
class Finish < DebuggerResumeCommand ; end
|
|
32
|
+
class Next < DebuggerResumeCommand ; end
|
|
33
|
+
class Quit < DebuggerResumeCommand ; end
|
|
34
|
+
class Step < DebuggerResumeCommand ; end
|
|
35
|
+
|
|
36
|
+
# Issues a comamnd to the debugger without continuing
|
|
37
|
+
# execution.
|
|
38
|
+
class Dbgr
|
|
39
|
+
def self.execute(conf, *opts)
|
|
40
|
+
$trepan_command =
|
|
41
|
+
if opts.size == 1 && opts[0].is_a?(String)
|
|
42
|
+
$trepan_args = opts[0]
|
|
43
|
+
else
|
|
44
|
+
opts.join(' ')
|
|
45
|
+
end
|
|
46
|
+
dbg_cmdproc = conf.workspace.instance_variable_get('@dbg_cmdproc')
|
|
47
|
+
dbg_cmdproc.run_command($trepan_command)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
end
|
|
52
|
+
if defined?(ExtendCommandBundle)
|
|
53
|
+
# New irb Commands which are the same name as their debugger
|
|
54
|
+
# counterpart
|
|
55
|
+
%w(Dbgr Finish Step).each do |name|
|
|
56
|
+
command = name.downcase
|
|
57
|
+
sym = name.to_sym
|
|
58
|
+
ExtendCommandBundle.def_extend_command command, sym
|
|
59
|
+
end
|
|
60
|
+
# New irb Commands which are the slightly different from their
|
|
61
|
+
# debugger counterpart
|
|
62
|
+
[['cont', :Continue],
|
|
63
|
+
['n', :Next],
|
|
64
|
+
['q', :Quit]].each do |command, sym|
|
|
65
|
+
ExtendCommandBundle.def_extend_command command, sym
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def self.start_session(binding, dbg_cmdproc, conf={})
|
|
70
|
+
unless @__initialized
|
|
71
|
+
|
|
72
|
+
# Set to run the standard rbdbgr IRB profile
|
|
73
|
+
irbrc = File.expand_path(File.join(File.dirname(__FILE__),
|
|
74
|
+
%w(.. data irbrc)))
|
|
75
|
+
ENV['IRBRC'] = irbrc
|
|
76
|
+
|
|
77
|
+
args = ARGV.dup
|
|
78
|
+
ARGV.replace([])
|
|
79
|
+
IRB.setup(nil)
|
|
80
|
+
ARGV.replace(args)
|
|
81
|
+
|
|
82
|
+
# If the user has a IRB profile, run that now.
|
|
83
|
+
if ENV['RBDBGR_IRB']
|
|
84
|
+
ENV['IRBRC'] = ENV['RBDBGR_IRB']
|
|
85
|
+
@CONF[:RC_NAME_GENERATOR]=nil
|
|
86
|
+
IRB.run_config
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
@__initialized = true
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
workspace = WorkSpace.new(binding)
|
|
93
|
+
workspace.instance_variable_set('@dbg_cmdproc', dbg_cmdproc)
|
|
94
|
+
|
|
95
|
+
irb = Irb.new(workspace)
|
|
96
|
+
|
|
97
|
+
@CONF[:IRB_RC].call(irb.context) if @CONF[:IRB_RC]
|
|
98
|
+
@CONF[:MAIN_CONTEXT] = irb.context
|
|
99
|
+
conf.each {|k, v| @CONF[k] = v}
|
|
100
|
+
# A copy of this back_trace_limit is already active. How?
|
|
101
|
+
IRB.CurrentContext.back_trace_limit = @CONF[:BACK_TRACE_LIMIT]
|
|
102
|
+
|
|
103
|
+
catch(:IRB_EXIT) do
|
|
104
|
+
irb.eval_input
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
if __FILE__ == $0
|
|
110
|
+
# Demo it.
|
|
111
|
+
IRB.start_session(binding, nil) if ARGV.size > 0
|
|
112
|
+
end
|
data/app/mock.rb
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
class Trepan
|
|
3
|
+
attr_accessor :trace_filter # Procs/Methods we ignore.
|
|
4
|
+
def initialize(opts={})
|
|
5
|
+
@trace_filter = []
|
|
6
|
+
end
|
|
7
|
+
class MockDebugger
|
|
8
|
+
attr_reader :initial_dir
|
|
9
|
+
def initialize(settings={})
|
|
10
|
+
@initial_dir = '.'
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
class MockCore
|
|
14
|
+
attr_accessor :dbgr
|
|
15
|
+
attr_reader :core
|
|
16
|
+
def initialize
|
|
17
|
+
@dbgr = MockDebugger.new
|
|
18
|
+
end
|
|
19
|
+
def event; 'line' end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
|
data/app/options.rb
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
#!/usr/bin/env ruby
|
|
2
|
+
# -*- coding: utf-8 -*-
|
|
3
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
4
|
+
#=== Summary
|
|
5
|
+
# Parses command-line options.
|
|
6
|
+
#=== Options
|
|
7
|
+
#
|
|
8
|
+
#<tt>--cd=DIR</tt>
|
|
9
|
+
# Change current directory to DIR.
|
|
10
|
+
#
|
|
11
|
+
#<tt>--command</tt> <i>file</i>::
|
|
12
|
+
# Run debugger command file <i>file</i>
|
|
13
|
+
#
|
|
14
|
+
#<tt>--nx</tt>::
|
|
15
|
+
# Don’t execute commands found in any initialization
|
|
16
|
+
# files, e.g. <tt>.rdebugrc</tt>.
|
|
17
|
+
#
|
|
18
|
+
#<tt>--restore=PROFILE</tt>::
|
|
19
|
+
# Debugger command file which restores debugger settings,
|
|
20
|
+
# resumably saved before a 'restart' debugger command.
|
|
21
|
+
#
|
|
22
|
+
#<tt>--version</tt>::
|
|
23
|
+
# Show the version number.
|
|
24
|
+
#
|
|
25
|
+
#<tt>--help</tt>::
|
|
26
|
+
# Show invocation help and exit.
|
|
27
|
+
|
|
28
|
+
require 'optparse'
|
|
29
|
+
module Trepanning
|
|
30
|
+
require_relative 'default'
|
|
31
|
+
|
|
32
|
+
Trepanning::VERSION = '0.0.4.git' unless defined?(Trepanning::VERSION)
|
|
33
|
+
Trepanning::PROGRAM = 'trepan' unless defined?(Rbdbgr::PROGRAM)
|
|
34
|
+
|
|
35
|
+
def show_version
|
|
36
|
+
"#{PROGRAM} version #{VERSION}"
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def copy_default_options
|
|
40
|
+
options = {}
|
|
41
|
+
DEFAULT_CMDLINE_SETTINGS.each do |key, value|
|
|
42
|
+
begin
|
|
43
|
+
options[key] = value.clone
|
|
44
|
+
rescue TypeError
|
|
45
|
+
options[key] = value
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
options
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def setup_options(options, stdout=$stdout, stderr=$stderr)
|
|
52
|
+
OptionParser.new do |opts|
|
|
53
|
+
opts.banner = <<EOB
|
|
54
|
+
#{show_version}
|
|
55
|
+
Usage: #{PROGRAM} [options] <script.rb> -- <script.rb parameters>
|
|
56
|
+
EOB
|
|
57
|
+
opts.on('--command FILE', String,
|
|
58
|
+
"Execute debugger commnds from FILE") do |cmdfile|
|
|
59
|
+
if File.readable?(cmdfile)
|
|
60
|
+
options[:cmdfiles] << cmdfile
|
|
61
|
+
elsif File.exists?(cmdfile)
|
|
62
|
+
stderr.puts "Command file '#{cmdfile}' is not readable."
|
|
63
|
+
else
|
|
64
|
+
stderr.puts "Command file '#{cmdfile}' does not exist."
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
opts.on('--nx',
|
|
68
|
+
"Do not run debugger initialization files (e.g. #{CMD_INITFILE}") do
|
|
69
|
+
options[:nx] = true
|
|
70
|
+
end
|
|
71
|
+
# opts.on('--output FILE', String, "Name of file to record output") do |outfile|
|
|
72
|
+
# options[:outfile] = outfile
|
|
73
|
+
# end
|
|
74
|
+
opts.on("--cd DIR", String, "Change current directory to DIR") do |dir|
|
|
75
|
+
if File.directory?(dir)
|
|
76
|
+
if File.executable?(dir)
|
|
77
|
+
options[:chdir] = dir
|
|
78
|
+
else
|
|
79
|
+
stderr.puts "Can't cd to #{dir}. Option --cd ignored."
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
stderr.puts "\"#{dir}\" is not a directory. Option --cd ignored."
|
|
83
|
+
end
|
|
84
|
+
end
|
|
85
|
+
opts.on("--restore PROFILE", String,
|
|
86
|
+
"Restore debugger state using PROFILE") do |profile|
|
|
87
|
+
if File.readable?(profile)
|
|
88
|
+
options[:restore_profile] = profile
|
|
89
|
+
stderr.puts "Debugger command file #{profile} is not readable. --restore option ignored."
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
opts.on_tail("--help", "Show this message") do
|
|
93
|
+
options[:help] = true
|
|
94
|
+
stdout.puts opts
|
|
95
|
+
end
|
|
96
|
+
opts.on_tail("--version",
|
|
97
|
+
"print the version") do
|
|
98
|
+
options[:version] = true
|
|
99
|
+
stdout.puts show_version
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
if __FILE__ == $0
|
|
106
|
+
include Rbdbgr
|
|
107
|
+
opts = {}
|
|
108
|
+
options ={}
|
|
109
|
+
[%w(--help), %w(--version)].each do |o|
|
|
110
|
+
options = copy_default_options
|
|
111
|
+
opts = setup_options(options)
|
|
112
|
+
rest = opts.parse o
|
|
113
|
+
puts options
|
|
114
|
+
puts '=' * 10
|
|
115
|
+
end
|
|
116
|
+
rest = opts.parse! ARGV
|
|
117
|
+
puts opts.to_s
|
|
118
|
+
puts '=' * 10
|
|
119
|
+
puts options
|
|
120
|
+
puts '=' * 10
|
|
121
|
+
puts Rbdbgr::DEFAULT_CMDLINE_SETTINGS
|
|
122
|
+
end
|
data/app/run.rb
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
3
|
+
require 'rbconfig'
|
|
4
|
+
module Trepanning
|
|
5
|
+
|
|
6
|
+
:module_function # All functions below are easily publically accessible
|
|
7
|
+
|
|
8
|
+
# Given a Ruby interpreter and program we are to debug, debug it.
|
|
9
|
+
# The caller must ensure that ARGV is set up to remove any debugger
|
|
10
|
+
# arguments or things that the debugged program isn't supposed to
|
|
11
|
+
# see. FIXME: Should we make ARGV an explicit parameter?
|
|
12
|
+
def debug_program(dbgr, ruby_path, program_to_debug)
|
|
13
|
+
|
|
14
|
+
# Make sure Ruby script syntax checks okay.
|
|
15
|
+
# Otherwise we get a load message that looks like trepanning has
|
|
16
|
+
# a problem.
|
|
17
|
+
output = `#{ruby_path} -c #{program_to_debug.inspect} 2>&1`
|
|
18
|
+
if $?.exitstatus != 0 and RUBY_PLATFORM !~ /mswin/
|
|
19
|
+
puts output
|
|
20
|
+
exit $?.exitstatus
|
|
21
|
+
end
|
|
22
|
+
# print "\032\032starting\n" if Trepan.annotate and Trepan.annotate > 2
|
|
23
|
+
|
|
24
|
+
dbgr.trace_filter << self.method(:debug_program)
|
|
25
|
+
dbgr.trace_filter << Kernel.method(:load)
|
|
26
|
+
|
|
27
|
+
old_dollar_0 = $0
|
|
28
|
+
|
|
29
|
+
# Without the dance below to set $0, setting it to a signifcantly
|
|
30
|
+
# longer value will truncate it in some OS's. See
|
|
31
|
+
# http://www.ruby-forum.com/topic/187083
|
|
32
|
+
$progname = program_to_debug
|
|
33
|
+
alias $0 $progname
|
|
34
|
+
dollar_0_tracker = lambda {|val| $program_name = val}
|
|
35
|
+
trace_var(:$0, dollar_0_tracker)
|
|
36
|
+
|
|
37
|
+
dbgr.debugger(:hide_stack=>true) do
|
|
38
|
+
dbgr.core.processor.hidelevels[Thread.current] =
|
|
39
|
+
RubyVM::ThreadFrame.current.stack_size + 1
|
|
40
|
+
Kernel::load program_to_debug
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# The dance we have to undo to restore $0 and undo the mess created
|
|
44
|
+
# above.
|
|
45
|
+
$0 = old_dollar_0
|
|
46
|
+
untrace_var(:$0, dollar_0_tracker)
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Return an ARRAY which makes explicit what array is needed to pass
|
|
50
|
+
# to system() to reinvoke this debugger using the same Ruby
|
|
51
|
+
# interpreter. Therefore, We want to the full path of both the Ruby
|
|
52
|
+
# interpreter and the debugger file ($0). We do this so as not to
|
|
53
|
+
# rely on an OS search lookup and/or the current working directory
|
|
54
|
+
# not getting changed from the initial invocation.
|
|
55
|
+
#
|
|
56
|
+
# Hmmm, perhaps it is better to also save the initial working
|
|
57
|
+
# directory? Yes, but on the other hand we can't assume that it
|
|
58
|
+
# still exists so we might still need to have the full paths.
|
|
59
|
+
#
|
|
60
|
+
# It is the caller's responsibility to ensure that argv is the same
|
|
61
|
+
# as ARGV when the debugger was invokes.
|
|
62
|
+
def explicit_restart_argv(argv)
|
|
63
|
+
[ruby_path, File.expand_path($0)] + argv
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Path name of Ruby interpreter we were invoked with.
|
|
67
|
+
def ruby_path
|
|
68
|
+
File.join(%w(bindir RUBY_INSTALL_NAME).map{|k| RbConfig::CONFIG[k]})
|
|
69
|
+
end
|
|
70
|
+
module_function :ruby_path
|
|
71
|
+
|
|
72
|
+
# Do a shell-like path lookup for prog_script and return the results.
|
|
73
|
+
# If we can't find anything return prog_script.
|
|
74
|
+
def whence_file(prog_script)
|
|
75
|
+
if prog_script.index(File::SEPARATOR)
|
|
76
|
+
# Don't search since this name has path separator components
|
|
77
|
+
return prog_script
|
|
78
|
+
end
|
|
79
|
+
for dirname in ENV['PATH'].split(File::PATH_SEPARATOR) do
|
|
80
|
+
prog_script_try = File.join(dirname, prog_script)
|
|
81
|
+
return prog_script_try if File.readable?(prog_script_try)
|
|
82
|
+
end
|
|
83
|
+
# Failure
|
|
84
|
+
return prog_script
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
if __FILE__ == $0
|
|
89
|
+
# Demo it.
|
|
90
|
+
include Trepanning
|
|
91
|
+
puts whence_file('irb')
|
|
92
|
+
puts whence_file('probably-does-not-exist')
|
|
93
|
+
puts ruby_path
|
|
94
|
+
p explicit_restart_argv(ARGV)
|
|
95
|
+
end
|
data/app/thread.rb
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
class Trepan
|
|
3
|
+
module ThreadHelper
|
|
4
|
+
# Return the thread at position num or object_id num.
|
|
5
|
+
def get_thread(num)
|
|
6
|
+
Thread.list.at(num) ||
|
|
7
|
+
Thread.list.detect {|t| t.object_id == num}
|
|
8
|
+
end
|
|
9
|
+
module_function :get_thread
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Demo it.
|
|
14
|
+
if __FILE__ == $0
|
|
15
|
+
include Trepan::ThreadHelper
|
|
16
|
+
Object::Thread.new do
|
|
17
|
+
[2, -2, 0, 1, -1,
|
|
18
|
+
Thread.main.object_id,
|
|
19
|
+
Thread.current.object_id].each do |th_num|
|
|
20
|
+
puts "get_thread(#{th_num}) = #{get_thread(th_num).inspect}"
|
|
21
|
+
end
|
|
22
|
+
end.join
|
|
23
|
+
|
|
24
|
+
end
|
data/app/util.rb
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
class Trepan
|
|
3
|
+
module Util
|
|
4
|
+
|
|
5
|
+
def safe_repr(str, max, suffix='...')
|
|
6
|
+
if str.is_a?(String) && str.size > max && !str.index("\n")
|
|
7
|
+
char = str[0..0]
|
|
8
|
+
opt_quote =
|
|
9
|
+
if '"' == char || "'" == char
|
|
10
|
+
max -= 1
|
|
11
|
+
char
|
|
12
|
+
else
|
|
13
|
+
''
|
|
14
|
+
end
|
|
15
|
+
"%s%s%s" % [ str[0...max], opt_quote, suffix ]
|
|
16
|
+
else
|
|
17
|
+
str
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
module_function :safe_repr
|
|
21
|
+
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
if __FILE__ == $0
|
|
26
|
+
include Trepan::Util
|
|
27
|
+
string = 'The time has come to talk of many things.'
|
|
28
|
+
puts safe_repr(string, 50)
|
|
29
|
+
puts safe_repr(string, 17)
|
|
30
|
+
puts safe_repr(string.inspect, 17)
|
|
31
|
+
puts safe_repr(string.inspect, 17, '')
|
|
32
|
+
end
|