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