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
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
# For recording hook events in a buffer for review later. Make use of
|
|
3
|
+
# Trace::Buffer for this prupose.
|
|
4
|
+
require 'trace'
|
|
5
|
+
require 'linecache'
|
|
6
|
+
|
|
7
|
+
class Trepan
|
|
8
|
+
class CmdProcessor
|
|
9
|
+
|
|
10
|
+
attr_reader :eventbuf
|
|
11
|
+
attr_reader :event_tracefilter
|
|
12
|
+
|
|
13
|
+
def eventbuf_initialize(size=100)
|
|
14
|
+
@eventbuf = Trace::EventBuffer.new(size)
|
|
15
|
+
# @event_tracefilter = Trace::Filter.new
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
# def event_processor(event, frame, arg=nil)
|
|
19
|
+
# @eventbuf.append(event, frame, arg)
|
|
20
|
+
# end
|
|
21
|
+
|
|
22
|
+
# Print event buffer entries from FROM up to TO try to stay within
|
|
23
|
+
# WIDTH. We show source lines only the first time they are
|
|
24
|
+
# encountered. Also we use separators to indicate points that the
|
|
25
|
+
# debugger has stopped at.
|
|
26
|
+
def eventbuf_print(from=nil, to=nil, width=80)
|
|
27
|
+
sep = '-' * ((width - 7) / 2)
|
|
28
|
+
last_container, last_location = nil, nil
|
|
29
|
+
if from == nil || !@eventbuf.marks[-1]
|
|
30
|
+
mark_index = 0
|
|
31
|
+
else
|
|
32
|
+
mark_index = @eventbuf.marks.size-1
|
|
33
|
+
translated_from = @eventbuf.zero_pos + from
|
|
34
|
+
@eventbuf.marks.each_with_index do
|
|
35
|
+
|m, i|
|
|
36
|
+
if m > translated_from
|
|
37
|
+
mark_index = [0, i-1].max
|
|
38
|
+
break
|
|
39
|
+
elsif m == translated_from
|
|
40
|
+
mark_index = i
|
|
41
|
+
break
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
nextmark = @eventbuf.marks[mark_index]
|
|
47
|
+
@eventbuf.each_with_index(from, to) do |e, i|
|
|
48
|
+
if nextmark
|
|
49
|
+
if nextmark == i
|
|
50
|
+
msg "#{sep} %5d #{sep}" % (mark_index - @eventbuf.marks.size)
|
|
51
|
+
mark_index += 1 if mark_index < @eventbuf.marks.size - 1
|
|
52
|
+
nextmark = @eventbuf.marks[mark_index]
|
|
53
|
+
elsif nextmark < i
|
|
54
|
+
mark_index += 1 if mark_index < @eventbuf.marks.size - 1
|
|
55
|
+
nextmark = @eventbuf.marks[mark_index]
|
|
56
|
+
end
|
|
57
|
+
end
|
|
58
|
+
last_container, last_location, mess =
|
|
59
|
+
format_eventbuf_entry(e, last_container, last_location) if e
|
|
60
|
+
msg mess
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# Show event buffer entry. If the location is the same as the previous
|
|
65
|
+
# location we don't show the duplicated location information.
|
|
66
|
+
def format_eventbuf_entry(item, last_container, last_location)
|
|
67
|
+
container =
|
|
68
|
+
if item.source_container[0] == 'file'
|
|
69
|
+
item.source_container[1]
|
|
70
|
+
else
|
|
71
|
+
item.source_container
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
location =
|
|
75
|
+
if 1 == item.source_location.size
|
|
76
|
+
item.source_location[0]
|
|
77
|
+
else
|
|
78
|
+
item.source_location
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
same_loc = (container == last_container && location == last_location)
|
|
82
|
+
mess = "#{item.event} "
|
|
83
|
+
mess +=
|
|
84
|
+
if %w(c-return return).member?(item.event)
|
|
85
|
+
"#{item.method} => #{item.arg.inspect}" + (same_loc ? '' : "\n\t")
|
|
86
|
+
elsif 'c-call' == item.event
|
|
87
|
+
"#{item.method} "
|
|
88
|
+
else
|
|
89
|
+
"#{item.type} #{item.method} "
|
|
90
|
+
end
|
|
91
|
+
mess += "#{container} at line #{location}" unless same_loc
|
|
92
|
+
|
|
93
|
+
if item.iseq # && long_format
|
|
94
|
+
mess += "\n\tVM offset #{item.pc_offset}"
|
|
95
|
+
end
|
|
96
|
+
unless same_loc
|
|
97
|
+
text = LineCache::getline(container, location, @reload_on_change)
|
|
98
|
+
mess += ":\n #{text.chomp}" if text
|
|
99
|
+
end
|
|
100
|
+
return container, location, mess
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
# FIXME: multiple hook mechanism needs work.
|
|
104
|
+
# def start_capture
|
|
105
|
+
# @event_tracefilter.add_trace_func(method(:event_processor).to_proc,
|
|
106
|
+
# Trace::DEFAULT_EVENT_MASK)
|
|
107
|
+
# end
|
|
108
|
+
|
|
109
|
+
# def stop_capture
|
|
110
|
+
# @event_tracefilter.set_trace_func(nil)
|
|
111
|
+
# end
|
|
112
|
+
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
if __FILE__ == $0
|
|
116
|
+
# Demo it.
|
|
117
|
+
cmdproc = Trepan::CmdProcessor.new
|
|
118
|
+
cmdproc.eventbuf_initialize(5)
|
|
119
|
+
|
|
120
|
+
def cmdproc.msg(mess)
|
|
121
|
+
puts mess
|
|
122
|
+
end
|
|
123
|
+
# cmdproc.start_capture
|
|
124
|
+
# z=5
|
|
125
|
+
# z.times do |i|
|
|
126
|
+
# x = i
|
|
127
|
+
# y = x+2
|
|
128
|
+
# end
|
|
129
|
+
# cmdproc.stop_capture
|
|
130
|
+
cmdproc.eventbuf_print
|
|
131
|
+
end
|
data/processor/frame.rb
ADDED
|
@@ -0,0 +1,230 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
require 'tempfile'
|
|
3
|
+
require 'linecache'
|
|
4
|
+
require_relative '../app/frame'
|
|
5
|
+
class Trepan
|
|
6
|
+
class CmdProcessor
|
|
7
|
+
|
|
8
|
+
attr_reader :current_thread
|
|
9
|
+
attr_accessor :frame # ThreadFrame, current frame
|
|
10
|
+
attr_accessor :frame_index # frame index in a "where" command
|
|
11
|
+
attr_accessor :hide_level #
|
|
12
|
+
attr_accessor :hidelevels # Hash[thread_id] -> FixNum, the
|
|
13
|
+
# level of the last frame to
|
|
14
|
+
# show. If we called the debugger
|
|
15
|
+
# directly, then there is generally
|
|
16
|
+
# a portion of a backtrace we don't
|
|
17
|
+
# want to show. We don't need to
|
|
18
|
+
# store this for all threads, just
|
|
19
|
+
# those we want to hide frame on. A
|
|
20
|
+
# value of 1 means to hide just the
|
|
21
|
+
# oldest level. The default or
|
|
22
|
+
# showing all levels is 0.
|
|
23
|
+
attr_accessor :remap_container # Hash[container] -> file container
|
|
24
|
+
# Gives us a way to map non-file
|
|
25
|
+
# container objects to a file
|
|
26
|
+
# container for display.
|
|
27
|
+
attr_accessor :remap_iseq # Hash[iseq] -> file container
|
|
28
|
+
|
|
29
|
+
attr_accessor :top_frame # top frame of current thread. Since
|
|
30
|
+
# right now the ThreadFrame method has "prev"
|
|
31
|
+
# but no way to move in the other direction.
|
|
32
|
+
# So we store the top frame.
|
|
33
|
+
attr_reader :threads2frames # Hash[thread_id] -> top_frame
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
def adjust_frame(frame_num, absolute_pos)
|
|
37
|
+
frame, frame_num = get_frame(frame_num, absolute_pos)
|
|
38
|
+
if frame
|
|
39
|
+
@frame = frame
|
|
40
|
+
@frame_index = frame_num
|
|
41
|
+
frame_eval_remap if 'EVAL' == @frame.type
|
|
42
|
+
print_location
|
|
43
|
+
@line_no = frame_line() - 1
|
|
44
|
+
@frame
|
|
45
|
+
else
|
|
46
|
+
nil
|
|
47
|
+
end
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
def frame_container(frame, canonicalize=true)
|
|
51
|
+
container =
|
|
52
|
+
if @remap_container.member?(frame.source_container)
|
|
53
|
+
@remap_container[frame.source_container]
|
|
54
|
+
elsif frame.iseq && @remap_iseq.member?(frame.iseq.sha1)
|
|
55
|
+
@remap_iseq[frame.iseq.sha1]
|
|
56
|
+
else
|
|
57
|
+
frame.source_container
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
container[1] = canonic_file(container[1]) if canonicalize
|
|
61
|
+
container
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
# If frame type is EVAL, set up to remap the string to a temporary file.
|
|
65
|
+
def frame_eval_remap
|
|
66
|
+
to_str = Trepan::Frame::eval_string(@frame)
|
|
67
|
+
return nil unless to_str.is_a?(String)
|
|
68
|
+
|
|
69
|
+
# All systems go!
|
|
70
|
+
unless @remap_iseq.member?(@frame.iseq.sha1)
|
|
71
|
+
tempfile = Tempfile.new(['eval-', '.rb'])
|
|
72
|
+
tempfile.open.puts(to_str)
|
|
73
|
+
|
|
74
|
+
@remap_iseq[@frame.iseq.sha1] = ['file', tempfile.path]
|
|
75
|
+
tempfile.close
|
|
76
|
+
LineCache::cache(tempfile.path)
|
|
77
|
+
end
|
|
78
|
+
return true
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def frame_line
|
|
82
|
+
if @event == 'vm-insn' && @frame.iseq
|
|
83
|
+
pc_offset = @frame.pc_offset
|
|
84
|
+
return 0 unless pc_offset
|
|
85
|
+
@frame.iseq.offset2lines(pc_offset)[0]
|
|
86
|
+
else
|
|
87
|
+
(@frame.source_location && @frame.source_location[0]) || 0
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Initializes the thread and frame variables: @frame, @top_frame,
|
|
92
|
+
# @frame_index, @current_thread, and @threads2frames
|
|
93
|
+
def frame_setup(frame_thread)
|
|
94
|
+
@frame_index = 0
|
|
95
|
+
@frame = @top_frame = frame_thread
|
|
96
|
+
@current_thread = @frame.thread
|
|
97
|
+
@stack_size = @frame.stack_size
|
|
98
|
+
|
|
99
|
+
@threads2frames ||= {}
|
|
100
|
+
@threads2frames[@current_thread] = @top_frame
|
|
101
|
+
@hide_level =
|
|
102
|
+
if @settings[:debugstack]
|
|
103
|
+
0
|
|
104
|
+
else
|
|
105
|
+
@hidelevels[@current_thread]
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
frame_eval_remap if 'EVAL' == @frame.type
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# Remove access to thread and frame variables
|
|
112
|
+
def frame_teardown
|
|
113
|
+
@top_frame = @frame = @frame_index = @current_thread = nil
|
|
114
|
+
@threads2frames = {}
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
def frame_initialize
|
|
118
|
+
@remap_container = {}
|
|
119
|
+
@remap_iseq = {}
|
|
120
|
+
@hidelevels = Hash.new(0) # Set default value to 0
|
|
121
|
+
@hide_level = 0
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
def get_frame(frame_num, absolute_pos)
|
|
125
|
+
stack_size = @top_frame.stack_size - @hide_level
|
|
126
|
+
|
|
127
|
+
if absolute_pos
|
|
128
|
+
frame_num += stack_size if frame_num < 0
|
|
129
|
+
else
|
|
130
|
+
frame_num += @frame_index
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
if frame_num < 0
|
|
134
|
+
errmsg('Adjusting would put us beyond the newest frame.')
|
|
135
|
+
return [nil, nil]
|
|
136
|
+
elsif frame_num >= stack_size
|
|
137
|
+
errmsg('Adjusting would put us beyond the oldest frame.')
|
|
138
|
+
return [nil, nil]
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
frame = @top_frame.prev(frame_num)
|
|
142
|
+
while 'IFUNC' == frame.type && frame.prev
|
|
143
|
+
frame = frame.prev
|
|
144
|
+
frame_num += 1
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
[frame, frame_num]
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def get_nonsync_frame(tf)
|
|
151
|
+
if (tf.stack_size > 10)
|
|
152
|
+
check_frames = (0..5).map{|i| tf.prev(i).method}
|
|
153
|
+
if check_frames ==
|
|
154
|
+
%w(synchronize event_processor IFUNC call trace_hook IFUNC)
|
|
155
|
+
return tf.prev(6)
|
|
156
|
+
end
|
|
157
|
+
end
|
|
158
|
+
tf
|
|
159
|
+
end
|
|
160
|
+
|
|
161
|
+
def get_frame_from_thread(th)
|
|
162
|
+
if th == Thread.current
|
|
163
|
+
@threads2frames[th]
|
|
164
|
+
else
|
|
165
|
+
# FIXME: Check to see if we are blocked on entry to debugger.
|
|
166
|
+
# If so, walk back frames.
|
|
167
|
+
tf = get_nonsync_frame(th.threadframe)
|
|
168
|
+
@threads2frames = tf
|
|
169
|
+
end
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# The dance we have to do to set debugger frame state to
|
|
173
|
+
# `frame', which is in the thread with id `thread_id'. We may
|
|
174
|
+
# need to the hide initial debugger frames.
|
|
175
|
+
def find_and_set_debugged_frame(th, position)
|
|
176
|
+
|
|
177
|
+
thread = threading._active[thread_id]
|
|
178
|
+
thread_name = thread.getName()
|
|
179
|
+
if (!@settings['dbg_pydbgr'] &&
|
|
180
|
+
thread_name == Mthread.current_thread_name())
|
|
181
|
+
# The frame we came in on ('current_thread_name') is
|
|
182
|
+
# the same as the one we want to switch to. In this case
|
|
183
|
+
# we need to some debugger frames are in this stack so
|
|
184
|
+
# we need to remove them.
|
|
185
|
+
newframe = Mthread.find_debugged_frame(frame)
|
|
186
|
+
frame = newframe unless newframe
|
|
187
|
+
end
|
|
188
|
+
## FIXME: else: we might be blocked on other threads which are
|
|
189
|
+
# about to go into the debugger it not for the fact this one got there
|
|
190
|
+
# first. Possibly in the future we want
|
|
191
|
+
# to hide the blocks into threading of that locking code as well.
|
|
192
|
+
|
|
193
|
+
# Set stack to new frame
|
|
194
|
+
@frame, @curindex = Mcmdproc.get_stack(frame, nil, self.proc)
|
|
195
|
+
@proc.stack, @proc.curindex = self.stack, self.curindex
|
|
196
|
+
|
|
197
|
+
# @frame_thread_name = thread_name
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
end
|
|
201
|
+
|
|
202
|
+
if __FILE__ == $0
|
|
203
|
+
# Demo it.
|
|
204
|
+
require 'thread_frame'
|
|
205
|
+
require_relative '../app/mock'
|
|
206
|
+
require_relative 'main' # Have to include before defining CmdProcessor!
|
|
207
|
+
# FIXME
|
|
208
|
+
class Trepan::CmdProcessor
|
|
209
|
+
def errmsg(msg)
|
|
210
|
+
puts msg
|
|
211
|
+
end
|
|
212
|
+
def print_location
|
|
213
|
+
puts "#{@frame.source_container} #{frame.source_location[0]}"
|
|
214
|
+
end
|
|
215
|
+
end
|
|
216
|
+
|
|
217
|
+
proc = Trepan::CmdProcessor.new(Trepan::MockCore.new())
|
|
218
|
+
proc.frame_setup(RubyVM::ThreadFrame.current)
|
|
219
|
+
proc.hidelevels = {}
|
|
220
|
+
puts "stack size: #{proc.top_frame.stack_size}"
|
|
221
|
+
0.upto(proc.top_frame.stack_size) { |i| proc.adjust_frame(i, true) }
|
|
222
|
+
puts '*' * 10
|
|
223
|
+
proc.adjust_frame(-1, true)
|
|
224
|
+
proc.adjust_frame(0, true)
|
|
225
|
+
puts '*' * 10
|
|
226
|
+
proc.top_frame.stack_size.times { proc.adjust_frame(1, false) }
|
|
227
|
+
proc.adjust_frame(proc.top_frame.stack_size-1, true)
|
|
228
|
+
proc.top_frame.stack_size.times { proc.adjust_frame(-1, false) }
|
|
229
|
+
|
|
230
|
+
end
|
data/processor/help.rb
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
class Trepan
|
|
3
|
+
module Help
|
|
4
|
+
|
|
5
|
+
def abbrev_stringify(name, min_abbrev)
|
|
6
|
+
"(#{name[0..min_abbrev-1]})#{name[min_abbrev..-1]}"
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def summary_help(subcmd)
|
|
10
|
+
# Set class constant SHORT_HELP to be the first line of HELP
|
|
11
|
+
# unless it has been defined in the class already.
|
|
12
|
+
# The below was the simplest way I could find to do this since
|
|
13
|
+
# we are the super class but want to set the subclass's constant.
|
|
14
|
+
# defined? didn't seem to work here.
|
|
15
|
+
c = subcmd.class.constants
|
|
16
|
+
if c.member?(:HELP) and !c.member?(:SHORT_HELP)
|
|
17
|
+
short_help = subcmd.class.const_get('HELP').split("\n")[0].chomp('.')
|
|
18
|
+
subcmd.class.const_set(:SHORT_HELP, short_help)
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
' %-12s -- %s' %
|
|
22
|
+
[abbrev_stringify(obj_const(subcmd, :NAME),
|
|
23
|
+
obj_const(subcmd, :MIN_ABBREV)),
|
|
24
|
+
obj_const(subcmd, :SHORT_HELP)]
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# We were given cmd without a subcommand; cmd is something
|
|
28
|
+
# like "show", "info" or "set". Generally this means list
|
|
29
|
+
# all of the subcommands.
|
|
30
|
+
def summary_list(name, subcmds)
|
|
31
|
+
msg "List of #{name} commands (with minimum abbreviation in parenthesis):"
|
|
32
|
+
subcmds.list.each do |subcmd_name|
|
|
33
|
+
# Some commands have lots of output.
|
|
34
|
+
# they are excluded here because 'in_list' is false.
|
|
35
|
+
msg summary_help(subcmds.subcmds[subcmd_name])
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
# Error message when subcommand asked for but doesn't exist
|
|
41
|
+
def undefined_subcmd(cmd, subcmd)
|
|
42
|
+
errmsg(('Undefined "%s" subcommand: "%s". ' +
|
|
43
|
+
'Try "help %s *".') % [cmd, subcmd, cmd])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
if __FILE__ == $0
|
|
50
|
+
class TestClass
|
|
51
|
+
include Trepan::Help
|
|
52
|
+
HELP = 'TestClass HELP.
|
|
53
|
+
|
|
54
|
+
Long description goes here.'
|
|
55
|
+
MIN_ABBREV = 1
|
|
56
|
+
NAME = File.basename(__FILE__)
|
|
57
|
+
def obj_const(obj, name)
|
|
58
|
+
obj.class.const_get(name)
|
|
59
|
+
end
|
|
60
|
+
def msg(mess)
|
|
61
|
+
puts mess
|
|
62
|
+
end
|
|
63
|
+
def errmsg(mess)
|
|
64
|
+
puts "***#{mess}"
|
|
65
|
+
end
|
|
66
|
+
def initialize
|
|
67
|
+
puts summary_help(self)
|
|
68
|
+
undefined_subcmd('foo', 'bar')
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
TestClass.new
|
|
72
|
+
end
|
data/processor/hook.rb
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
class Trepan
|
|
3
|
+
class CmdProcessor
|
|
4
|
+
# Command processor hooks.
|
|
5
|
+
attr_reader :autoirb_hook
|
|
6
|
+
attr_reader :autolist_hook
|
|
7
|
+
attr_reader :debug_dbgr_hook
|
|
8
|
+
attr_reader :display_hook
|
|
9
|
+
attr_reader :timer_hook
|
|
10
|
+
attr_reader :trace_hook
|
|
11
|
+
attr_reader :tracebuf_hook
|
|
12
|
+
attr_reader :unconditional_prehooks
|
|
13
|
+
attr_reader :cmdloop_posthooks
|
|
14
|
+
attr_reader :cmdloop_prehooks
|
|
15
|
+
|
|
16
|
+
# Used to time how long a debugger action takes
|
|
17
|
+
attr_accessor :time_last
|
|
18
|
+
|
|
19
|
+
class Hook
|
|
20
|
+
attr_accessor :list
|
|
21
|
+
|
|
22
|
+
def initialize(list=[])
|
|
23
|
+
@list = list
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
def delete_by_name(delete_name)
|
|
27
|
+
@list.delete_if {|hook_name, priority, hook| hook_name == delete_name}
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def empty?
|
|
31
|
+
@list.empty?
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def insert(priority, name, hook)
|
|
35
|
+
insert_loc = @list.size # at end
|
|
36
|
+
@list.each_with_index do |entry, index|
|
|
37
|
+
n, p, h = entry
|
|
38
|
+
if priority > p
|
|
39
|
+
insert_loc = index
|
|
40
|
+
break
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
@list.insert(insert_loc, [name, priority, hook])
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
def insert_if_new(priority, name, hook)
|
|
47
|
+
insert(priority, name, hook) unless
|
|
48
|
+
@list.find {|try_name, try_priority, try_hook| try_name == name}
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Run each function in `hooks' with args
|
|
52
|
+
def run(*args)
|
|
53
|
+
@list.each do |name, priority, hook|
|
|
54
|
+
hook.call(name, *args)
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Could add delete_at and delete if necessary.
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def hook_initialize(commands)
|
|
62
|
+
@cmdloop_posthooks = Hook.new
|
|
63
|
+
@cmdloop_prehooks = Hook.new
|
|
64
|
+
@unconditional_prehooks = Hook.new
|
|
65
|
+
|
|
66
|
+
irb_cmd = commands['irb']
|
|
67
|
+
@autoirb_hook = ['autoirb',
|
|
68
|
+
Proc.new{|*args| irb_cmd.run(['irb']) if irb_cmd}]
|
|
69
|
+
|
|
70
|
+
@debug_dbgr_hook = ['dbgdbgr',
|
|
71
|
+
Proc.new{|*args|
|
|
72
|
+
if settings[:debugdbgr]
|
|
73
|
+
$trepan_cmdproc = self
|
|
74
|
+
$trepan_frame = frame
|
|
75
|
+
else
|
|
76
|
+
$trepan_cmdproc = nil
|
|
77
|
+
$trepan_frame = nil
|
|
78
|
+
end}]
|
|
79
|
+
|
|
80
|
+
display_cmd = commands['display']
|
|
81
|
+
@display_hook = ['display',
|
|
82
|
+
Proc.new{|*args| display_cmd.run(['display']) if
|
|
83
|
+
display_cmd}]
|
|
84
|
+
|
|
85
|
+
list_cmd = commands['list']
|
|
86
|
+
@autolist_hook = ['autolist',
|
|
87
|
+
Proc.new{|*args| list_cmd.run(['list']) if list_cmd}]
|
|
88
|
+
|
|
89
|
+
@timer_hook = ['timer',
|
|
90
|
+
Proc.new{|*args|
|
|
91
|
+
now = Time.now
|
|
92
|
+
msg("%g seconds" %
|
|
93
|
+
(now - @time_last)) if @time_last
|
|
94
|
+
@time_last = now
|
|
95
|
+
}]
|
|
96
|
+
@timer_posthook = ['timer', Proc.new{|*args| @time_last = Time.now}]
|
|
97
|
+
@trace_hook = ['trace',
|
|
98
|
+
Proc.new{|*args| print_location}]
|
|
99
|
+
@tracebuf_hook = ['tracebuffer',
|
|
100
|
+
Proc.new{|*args| @eventbuf.append(@event, @frame,
|
|
101
|
+
@core.hook_arg)}]
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
if __FILE__ == $0
|
|
107
|
+
# Demo it.
|
|
108
|
+
hooks = Trepan::CmdProcessor::Hook.new
|
|
109
|
+
hooks.run(5)
|
|
110
|
+
hook1 = Proc.new {|name, a| puts "#{name} called with #{a}"}
|
|
111
|
+
hooks = Trepan::CmdProcessor::Hook.new()
|
|
112
|
+
hooks.insert(-1, 'hook1', hook1)
|
|
113
|
+
p hooks.list
|
|
114
|
+
hooks.insert_if_new(-1, 'hook1', hook1)
|
|
115
|
+
puts '-' * 30
|
|
116
|
+
p hooks.list
|
|
117
|
+
hooks.run(10)
|
|
118
|
+
puts '-' * 30
|
|
119
|
+
hooks.insert(-1, 'hook2', hook1)
|
|
120
|
+
hooks.run(20)
|
|
121
|
+
puts '-' * 30
|
|
122
|
+
hooks.delete_by_name('hook2')
|
|
123
|
+
hooks.run(30)
|
|
124
|
+
puts '-' * 30
|
|
125
|
+
hooks.delete_by_name('hook1')
|
|
126
|
+
hooks.run(30)
|
|
127
|
+
puts '-' * 30
|
|
128
|
+
end
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
# Copyright (C) 2010 Rocky Bernstein <rockyb@rubyforge.net>
|
|
2
|
+
# Trepan::CmdProcess that loads up debugger commands from builtin and
|
|
3
|
+
# user directories.
|
|
4
|
+
# Sets @commands, @aliases, @macros
|
|
5
|
+
class Trepan
|
|
6
|
+
class CmdProcessor
|
|
7
|
+
|
|
8
|
+
attr_reader :aliases # Hash[String] of command names
|
|
9
|
+
# indexed by alias name
|
|
10
|
+
attr_reader :commands # Hash[String] of command objects
|
|
11
|
+
# indexed by name
|
|
12
|
+
attr_reader :macros # Hash[String] of Proc objects
|
|
13
|
+
# indexed by macro name.
|
|
14
|
+
|
|
15
|
+
# "initialize" for multi-file class. Called from main.rb's "initialize".
|
|
16
|
+
def load_cmds_initialize
|
|
17
|
+
@commands = {}
|
|
18
|
+
@aliases = {}
|
|
19
|
+
@macros = {}
|
|
20
|
+
|
|
21
|
+
cmd_dirs = [ File.join(File.dirname(__FILE__), 'command') ]
|
|
22
|
+
cmd_dirs << @settings[:user_cmd_dir] if @settings[:user_cmd_dir]
|
|
23
|
+
cmd_dirs.each do |cmd_dir|
|
|
24
|
+
load_debugger_commands(cmd_dir) if File.directory?(cmd_dir)
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
# Loads in debugger commands by require'ing each ruby file in the
|
|
29
|
+
# 'command' directory. Then a new instance of each class of the
|
|
30
|
+
# form Trepan::xxCommand is added to @commands and that array
|
|
31
|
+
# is returned.
|
|
32
|
+
|
|
33
|
+
def load_debugger_commands(cmd_dir)
|
|
34
|
+
Dir.glob(File.join(cmd_dir, '*.rb')).each do |rb|
|
|
35
|
+
require rb
|
|
36
|
+
end if File.directory?(cmd_dir)
|
|
37
|
+
# Instantiate each Command class found by the above require(s).
|
|
38
|
+
### p Trepan::Command.constants ## REMOVE ME
|
|
39
|
+
Trepan::Command.constants.grep(/.Command$/).each do |command|
|
|
40
|
+
# Note: there is probably a non-eval way to instantiate the
|
|
41
|
+
# command, but I don't know it. And eval works.
|
|
42
|
+
new_cmd = "Trepan::Command::#{command}.new(self)"
|
|
43
|
+
cmd = self.instance_eval(new_cmd)
|
|
44
|
+
|
|
45
|
+
# Add to list of commands and aliases.
|
|
46
|
+
cc = cmd.class
|
|
47
|
+
cmd_name = cc.const_get(:NAME)
|
|
48
|
+
if cc.constants.member?(:ALIASES)
|
|
49
|
+
aliases= cc.const_get(:ALIASES)
|
|
50
|
+
aliases.each {|a| @aliases[a] = cmd_name}
|
|
51
|
+
end
|
|
52
|
+
@commands[cmd_name] = cmd
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
# Looks up cmd_array[0] in @commands and runs that. We do lots of
|
|
57
|
+
# validity testing on cmd_array.
|
|
58
|
+
def run_cmd(cmd_array)
|
|
59
|
+
unless cmd_array.is_a?(Array)
|
|
60
|
+
errmsg "run_cmd argument should be an Array, got: #{cmd_array.class}"
|
|
61
|
+
return
|
|
62
|
+
end
|
|
63
|
+
if cmd_array.detect{|item| !item.is_a?(String)}
|
|
64
|
+
errmsg "run_cmd argument Array should only contain strings. " +
|
|
65
|
+
"Got #{cmd_array.inspect}"
|
|
66
|
+
return
|
|
67
|
+
end
|
|
68
|
+
if cmd_array.empty?
|
|
69
|
+
errmsg "run_cmd Array should have at least one item. " +
|
|
70
|
+
"Got: #{cmd_array.inspect}"
|
|
71
|
+
return
|
|
72
|
+
end
|
|
73
|
+
cmd_name = cmd_array[0]
|
|
74
|
+
if @commands.member?(cmd_name)
|
|
75
|
+
@commands[cmd_name].run(cmd_array)
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
if __FILE__ == $0
|
|
81
|
+
cmdproc = Trepan::CmdProcessor.new
|
|
82
|
+
cmddir = File.join(File.dirname(__FILE__), 'command')
|
|
83
|
+
cmdproc.instance_variable_set('@settings', {})
|
|
84
|
+
cmdproc.load_cmds_initialize
|
|
85
|
+
require 'columnize'
|
|
86
|
+
puts Columnize.columnize(cmdproc.commands.keys.sort)
|
|
87
|
+
puts '=' * 20
|
|
88
|
+
puts Columnize.columnize(cmdproc.aliases.keys.sort)
|
|
89
|
+
puts '=' * 20
|
|
90
|
+
|
|
91
|
+
def cmdproc.errmsg(mess)
|
|
92
|
+
puts "** #{mess}"
|
|
93
|
+
end
|
|
94
|
+
|
|
95
|
+
def cmdproc.msg(mess)
|
|
96
|
+
puts mess
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
cmdproc.run_cmd('foo') # Invalid - not an Array
|
|
100
|
+
cmdproc.run_cmd([]) # Invalid - empty Array
|
|
101
|
+
cmdproc.run_cmd(['list', 5]) # Invalid - nonstring arg
|
|
102
|
+
end
|