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.
Files changed (219) hide show
  1. data/ChangeLog +4422 -0
  2. data/LICENSE +23 -0
  3. data/NEWS +12 -0
  4. data/README.textile +56 -0
  5. data/Rakefile +171 -0
  6. data/app/Makefile +7 -0
  7. data/app/breakpoint.rb +157 -0
  8. data/app/brkptmgr.rb +149 -0
  9. data/app/condition.rb +22 -0
  10. data/app/core.rb +203 -0
  11. data/app/default.rb +54 -0
  12. data/app/disassemble.rb +61 -0
  13. data/app/display.rb +148 -0
  14. data/app/file.rb +135 -0
  15. data/app/frame.rb +275 -0
  16. data/app/irb.rb +112 -0
  17. data/app/mock.rb +22 -0
  18. data/app/options.rb +122 -0
  19. data/app/run.rb +95 -0
  20. data/app/thread.rb +24 -0
  21. data/app/util.rb +32 -0
  22. data/bin/trepan +63 -0
  23. data/data/custom_require.rb +44 -0
  24. data/data/irbrc +55 -0
  25. data/data/prelude.rb +38 -0
  26. data/interface/base_intf.rb +95 -0
  27. data/interface/script.rb +103 -0
  28. data/interface/user.rb +90 -0
  29. data/io/base_io.rb +92 -0
  30. data/io/input.rb +111 -0
  31. data/io/string_array.rb +155 -0
  32. data/lib/Makefile +7 -0
  33. data/lib/trepanning.rb +277 -0
  34. data/processor/breakpoint.rb +108 -0
  35. data/processor/command/alias.rb +55 -0
  36. data/processor/command/backtrace.rb +95 -0
  37. data/processor/command/base/cmd.rb +97 -0
  38. data/processor/command/base/subcmd.rb +207 -0
  39. data/processor/command/base/submgr.rb +178 -0
  40. data/processor/command/base/subsubcmd.rb +102 -0
  41. data/processor/command/base/subsubmgr.rb +182 -0
  42. data/processor/command/break.rb +85 -0
  43. data/processor/command/condition.rb +64 -0
  44. data/processor/command/continue.rb +61 -0
  45. data/processor/command/debug.rb +85 -0
  46. data/processor/command/delete.rb +54 -0
  47. data/processor/command/directory.rb +43 -0
  48. data/processor/command/disable.rb +65 -0
  49. data/processor/command/disassemble.rb +103 -0
  50. data/processor/command/display.rb +81 -0
  51. data/processor/command/down.rb +56 -0
  52. data/processor/command/enable.rb +43 -0
  53. data/processor/command/exit.rb +54 -0
  54. data/processor/command/finish.rb +81 -0
  55. data/processor/command/frame.rb +117 -0
  56. data/processor/command/help.rb +146 -0
  57. data/processor/command/info.rb +28 -0
  58. data/processor/command/info_subcmd/args.rb +56 -0
  59. data/processor/command/info_subcmd/breakpoints.rb +162 -0
  60. data/processor/command/info_subcmd/file.rb +162 -0
  61. data/processor/command/info_subcmd/frame.rb +39 -0
  62. data/processor/command/info_subcmd/iseq.rb +83 -0
  63. data/processor/command/info_subcmd/locals.rb +88 -0
  64. data/processor/command/info_subcmd/program.rb +54 -0
  65. data/processor/command/info_subcmd/registers.rb +72 -0
  66. data/processor/command/info_subcmd/registers_subcmd/dfp.rb +38 -0
  67. data/processor/command/info_subcmd/registers_subcmd/helper.rb +40 -0
  68. data/processor/command/info_subcmd/registers_subcmd/lfp.rb +54 -0
  69. data/processor/command/info_subcmd/registers_subcmd/pc.rb +44 -0
  70. data/processor/command/info_subcmd/registers_subcmd/sp.rb +75 -0
  71. data/processor/command/info_subcmd/return.rb +40 -0
  72. data/processor/command/info_subcmd/thread.rb +106 -0
  73. data/processor/command/irb.rb +106 -0
  74. data/processor/command/kill.rb +58 -0
  75. data/processor/command/list.rb +327 -0
  76. data/processor/command/macro.rb +65 -0
  77. data/processor/command/next.rb +89 -0
  78. data/processor/command/nocache.rb +33 -0
  79. data/processor/command/print.rb +37 -0
  80. data/processor/command/ps.rb +40 -0
  81. data/processor/command/quit.rb +62 -0
  82. data/processor/command/raise.rb +47 -0
  83. data/processor/command/reload.rb +28 -0
  84. data/processor/command/reload_subcmd/command.rb +34 -0
  85. data/processor/command/restart.rb +57 -0
  86. data/processor/command/save.rb +60 -0
  87. data/processor/command/set.rb +47 -0
  88. data/processor/command/set_subcmd/auto.rb +27 -0
  89. data/processor/command/set_subcmd/auto_subcmd/eval.rb +67 -0
  90. data/processor/command/set_subcmd/auto_subcmd/irb.rb +49 -0
  91. data/processor/command/set_subcmd/auto_subcmd/list.rb +51 -0
  92. data/processor/command/set_subcmd/basename.rb +39 -0
  93. data/processor/command/set_subcmd/debug.rb +27 -0
  94. data/processor/command/set_subcmd/debug_subcmd/dbgr.rb +49 -0
  95. data/processor/command/set_subcmd/debug_subcmd/except.rb +35 -0
  96. data/processor/command/set_subcmd/debug_subcmd/macro.rb +35 -0
  97. data/processor/command/set_subcmd/debug_subcmd/skip.rb +35 -0
  98. data/processor/command/set_subcmd/debug_subcmd/stack.rb +45 -0
  99. data/processor/command/set_subcmd/different.rb +67 -0
  100. data/processor/command/set_subcmd/events.rb +71 -0
  101. data/processor/command/set_subcmd/max.rb +35 -0
  102. data/processor/command/set_subcmd/max_subcmd/list.rb +50 -0
  103. data/processor/command/set_subcmd/max_subcmd/stack.rb +60 -0
  104. data/processor/command/set_subcmd/max_subcmd/string.rb +53 -0
  105. data/processor/command/set_subcmd/max_subcmd/width.rb +50 -0
  106. data/processor/command/set_subcmd/return.rb +66 -0
  107. data/processor/command/set_subcmd/sp.rb +62 -0
  108. data/processor/command/set_subcmd/substitute.rb +25 -0
  109. data/processor/command/set_subcmd/substitute_subcmd/eval.rb +98 -0
  110. data/processor/command/set_subcmd/substitute_subcmd/path.rb +55 -0
  111. data/processor/command/set_subcmd/substitute_subcmd/string.rb +72 -0
  112. data/processor/command/set_subcmd/timer.rb +68 -0
  113. data/processor/command/set_subcmd/trace.rb +43 -0
  114. data/processor/command/set_subcmd/trace_subcmd/buffer.rb +56 -0
  115. data/processor/command/set_subcmd/trace_subcmd/print.rb +54 -0
  116. data/processor/command/set_subcmd/trace_subcmd/var.rb +61 -0
  117. data/processor/command/show.rb +27 -0
  118. data/processor/command/show_subcmd/alias.rb +50 -0
  119. data/processor/command/show_subcmd/args.rb +50 -0
  120. data/processor/command/show_subcmd/auto.rb +27 -0
  121. data/processor/command/show_subcmd/auto_subcmd/eval.rb +38 -0
  122. data/processor/command/show_subcmd/auto_subcmd/irb.rb +34 -0
  123. data/processor/command/show_subcmd/auto_subcmd/list.rb +36 -0
  124. data/processor/command/show_subcmd/basename.rb +28 -0
  125. data/processor/command/show_subcmd/debug.rb +27 -0
  126. data/processor/command/show_subcmd/debug_subcmd/dbgr.rb +31 -0
  127. data/processor/command/show_subcmd/debug_subcmd/except.rb +33 -0
  128. data/processor/command/show_subcmd/debug_subcmd/macro.rb +32 -0
  129. data/processor/command/show_subcmd/debug_subcmd/skip.rb +33 -0
  130. data/processor/command/show_subcmd/debug_subcmd/stack.rb +32 -0
  131. data/processor/command/show_subcmd/different.rb +37 -0
  132. data/processor/command/show_subcmd/events.rb +40 -0
  133. data/processor/command/show_subcmd/macro.rb +45 -0
  134. data/processor/command/show_subcmd/max.rb +31 -0
  135. data/processor/command/show_subcmd/max_subcmd/list.rb +39 -0
  136. data/processor/command/show_subcmd/max_subcmd/stack.rb +35 -0
  137. data/processor/command/show_subcmd/max_subcmd/string.rb +41 -0
  138. data/processor/command/show_subcmd/max_subcmd/width.rb +36 -0
  139. data/processor/command/show_subcmd/trace.rb +29 -0
  140. data/processor/command/show_subcmd/trace_subcmd/buffer.rb +84 -0
  141. data/processor/command/show_subcmd/trace_subcmd/print.rb +38 -0
  142. data/processor/command/source.rb +74 -0
  143. data/processor/command/step.rb +139 -0
  144. data/processor/command/stepi.rb +63 -0
  145. data/processor/command/unalias.rb +44 -0
  146. data/processor/command/undisplay.rb +63 -0
  147. data/processor/command/up.rb +92 -0
  148. data/processor/default.rb +45 -0
  149. data/processor/display.rb +17 -0
  150. data/processor/eval.rb +88 -0
  151. data/processor/eventbuf.rb +131 -0
  152. data/processor/frame.rb +230 -0
  153. data/processor/help.rb +72 -0
  154. data/processor/hook.rb +128 -0
  155. data/processor/load_cmds.rb +102 -0
  156. data/processor/location.rb +126 -0
  157. data/processor/main.rb +364 -0
  158. data/processor/mock.rb +100 -0
  159. data/processor/msg.rb +26 -0
  160. data/processor/running.rb +170 -0
  161. data/processor/subcmd.rb +159 -0
  162. data/processor/validate.rb +395 -0
  163. data/test/example/fname with blank.rb +1 -0
  164. data/test/example/gcd-xx.rb +18 -0
  165. data/test/example/gcd.rb +19 -0
  166. data/test/example/gcd1.rb +24 -0
  167. data/test/example/null.rb +1 -0
  168. data/test/example/thread1.rb +3 -0
  169. data/test/functional/fn_helper.rb +119 -0
  170. data/test/functional/test-break.rb +87 -0
  171. data/test/functional/test-condition.rb +59 -0
  172. data/test/functional/test-debugger-call-bug.rb +31 -0
  173. data/test/functional/test-delete.rb +71 -0
  174. data/test/functional/test-finish.rb +44 -0
  175. data/test/functional/test-immediate-step-bug.rb +35 -0
  176. data/test/functional/test-next.rb +77 -0
  177. data/test/functional/test-raise.rb +73 -0
  178. data/test/functional/test-return.rb +100 -0
  179. data/test/functional/test-step.rb +274 -0
  180. data/test/functional/test-stepbug.rb +40 -0
  181. data/test/functional/test-trace-var.rb +40 -0
  182. data/test/functional/tmp/b1.rb +5 -0
  183. data/test/functional/tmp/s1.rb +9 -0
  184. data/test/functional/tmp/t2.rb +6 -0
  185. data/test/integration/file-diff.rb +88 -0
  186. data/test/integration/helper.rb +52 -0
  187. data/test/integration/test-fname-with-blank.rb +11 -0
  188. data/test/integration/test-quit.rb +11 -0
  189. data/test/integration/try-test-enable.rb +11 -0
  190. data/test/unit/cmd-helper.rb +44 -0
  191. data/test/unit/test-app-brkpt.rb +30 -0
  192. data/test/unit/test-app-brkptmgr.rb +56 -0
  193. data/test/unit/test-app-disassemble.rb +60 -0
  194. data/test/unit/test-app-file.rb +46 -0
  195. data/test/unit/test-app-frame.rb +49 -0
  196. data/test/unit/test-app-options.rb +60 -0
  197. data/test/unit/test-app-run.rb +19 -0
  198. data/test/unit/test-app-thread.rb +25 -0
  199. data/test/unit/test-app-util.rb +17 -0
  200. data/test/unit/test-base-subcmd.rb +59 -0
  201. data/test/unit/test-bin-trepan.rb +48 -0
  202. data/test/unit/test-cmd-alias.rb +50 -0
  203. data/test/unit/test-cmd-break.rb +80 -0
  204. data/test/unit/test-cmd-endisable.rb +59 -0
  205. data/test/unit/test-cmd-help.rb +100 -0
  206. data/test/unit/test-cmd-kill.rb +47 -0
  207. data/test/unit/test-cmd-quit.rb +26 -0
  208. data/test/unit/test-cmd-step.rb +45 -0
  209. data/test/unit/test-intf-user.rb +45 -0
  210. data/test/unit/test-io-input.rb +26 -0
  211. data/test/unit/test-proc-eval.rb +26 -0
  212. data/test/unit/test-proc-frame.rb +77 -0
  213. data/test/unit/test-proc-help.rb +15 -0
  214. data/test/unit/test-proc-hook.rb +29 -0
  215. data/test/unit/test-proc-load_cmds.rb +40 -0
  216. data/test/unit/test-proc-main.rb +99 -0
  217. data/test/unit/test-proc-validate.rb +90 -0
  218. data/test/unit/test-subcmd-help.rb +48 -0
  219. 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
@@ -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