debugger 1.0.0.rc1
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/AUTHORS +10 -0
- data/CHANGES +334 -0
- data/ChangeLog +5655 -0
- data/INSTALL.SVN +154 -0
- data/LICENSE +23 -0
- data/Makefile.am +14 -0
- data/OLD_README +122 -0
- data/README.md +10 -0
- data/Rakefile +266 -0
- data/autogen.sh +4 -0
- data/bin/rdebug +398 -0
- data/cli/ruby-debug.rb +173 -0
- data/cli/ruby-debug/command.rb +228 -0
- data/cli/ruby-debug/commands/breakpoints.rb +153 -0
- data/cli/ruby-debug/commands/catchpoint.rb +55 -0
- data/cli/ruby-debug/commands/condition.rb +49 -0
- data/cli/ruby-debug/commands/continue.rb +38 -0
- data/cli/ruby-debug/commands/control.rb +107 -0
- data/cli/ruby-debug/commands/display.rb +120 -0
- data/cli/ruby-debug/commands/edit.rb +48 -0
- data/cli/ruby-debug/commands/enable.rb +202 -0
- data/cli/ruby-debug/commands/eval.rb +176 -0
- data/cli/ruby-debug/commands/finish.rb +42 -0
- data/cli/ruby-debug/commands/frame.rb +301 -0
- data/cli/ruby-debug/commands/help.rb +56 -0
- data/cli/ruby-debug/commands/info.rb +467 -0
- data/cli/ruby-debug/commands/irb.rb +123 -0
- data/cli/ruby-debug/commands/jump.rb +66 -0
- data/cli/ruby-debug/commands/kill.rb +51 -0
- data/cli/ruby-debug/commands/list.rb +94 -0
- data/cli/ruby-debug/commands/method.rb +84 -0
- data/cli/ruby-debug/commands/quit.rb +39 -0
- data/cli/ruby-debug/commands/reload.rb +40 -0
- data/cli/ruby-debug/commands/save.rb +90 -0
- data/cli/ruby-debug/commands/set.rb +221 -0
- data/cli/ruby-debug/commands/show.rb +247 -0
- data/cli/ruby-debug/commands/skip.rb +35 -0
- data/cli/ruby-debug/commands/source.rb +36 -0
- data/cli/ruby-debug/commands/stepping.rb +81 -0
- data/cli/ruby-debug/commands/threads.rb +189 -0
- data/cli/ruby-debug/commands/tmate.rb +36 -0
- data/cli/ruby-debug/commands/trace.rb +57 -0
- data/cli/ruby-debug/commands/variables.rb +199 -0
- data/cli/ruby-debug/debugger.rb +5 -0
- data/cli/ruby-debug/helper.rb +69 -0
- data/cli/ruby-debug/interface.rb +232 -0
- data/cli/ruby-debug/processor.rb +474 -0
- data/configure.ac +12 -0
- data/debugger.gemspec +24 -0
- data/doc/.cvsignore +42 -0
- data/doc/Makefile.am +63 -0
- data/doc/emacs-notes.txt +38 -0
- data/doc/hanoi.rb +35 -0
- data/doc/primes.rb +28 -0
- data/doc/rdebug-emacs.texi +1030 -0
- data/doc/rdebug.1 +241 -0
- data/doc/ruby-debug.texi +3791 -0
- data/doc/test-tri2.rb +18 -0
- data/doc/tri3.rb +8 -0
- data/doc/triangle.rb +12 -0
- data/emacs/Makefile.am +130 -0
- data/emacs/rdebug-annotate.el +385 -0
- data/emacs/rdebug-breaks.el +407 -0
- data/emacs/rdebug-cmd.el +92 -0
- data/emacs/rdebug-core.el +502 -0
- data/emacs/rdebug-dbg.el +62 -0
- data/emacs/rdebug-error.el +79 -0
- data/emacs/rdebug-fns.el +111 -0
- data/emacs/rdebug-frames.el +230 -0
- data/emacs/rdebug-gud.el +242 -0
- data/emacs/rdebug-help.el +104 -0
- data/emacs/rdebug-info.el +83 -0
- data/emacs/rdebug-layouts.el +180 -0
- data/emacs/rdebug-locring.el +118 -0
- data/emacs/rdebug-output.el +106 -0
- data/emacs/rdebug-regexp.el +118 -0
- data/emacs/rdebug-secondary.el +260 -0
- data/emacs/rdebug-shortkey.el +175 -0
- data/emacs/rdebug-source.el +568 -0
- data/emacs/rdebug-track.el +392 -0
- data/emacs/rdebug-varbuf.el +150 -0
- data/emacs/rdebug-vars.el +125 -0
- data/emacs/rdebug-watch.el +132 -0
- data/emacs/rdebug.el +326 -0
- data/emacs/test/elk-test.el +242 -0
- data/emacs/test/test-annotate.el +103 -0
- data/emacs/test/test-cmd.el +116 -0
- data/emacs/test/test-core.el +104 -0
- data/emacs/test/test-fns.el +65 -0
- data/emacs/test/test-frames.el +62 -0
- data/emacs/test/test-gud.el +35 -0
- data/emacs/test/test-indent.el +58 -0
- data/emacs/test/test-regexp.el +144 -0
- data/emacs/test/test-shortkey.el +61 -0
- data/ext/ruby_debug/breakpoint.c +586 -0
- data/ext/ruby_debug/extconf.rb +49 -0
- data/ext/ruby_debug/ruby_debug.c +2624 -0
- data/ext/ruby_debug/ruby_debug.h +148 -0
- data/lib/ChangeLog +1065 -0
- data/lib/debugger.rb +7 -0
- data/lib/debugger/version.rb +3 -0
- data/lib/ruby-debug-base.rb +304 -0
- data/rdbg.rb +33 -0
- data/runner.sh +7 -0
- data/svn2cl_usermap +3 -0
- data/test/.cvsignore +1 -0
- data/test/base/base.rb +74 -0
- data/test/base/binding.rb +31 -0
- data/test/base/catchpoint.rb +26 -0
- data/test/base/load.rb +40 -0
- data/test/bp_loop_issue.rb +3 -0
- data/test/classes.rb +11 -0
- data/test/cli/commands/catchpoint_test.rb +36 -0
- data/test/cli/commands/unit/regexp.rb +42 -0
- data/test/config.yaml +8 -0
- data/test/data/annotate.cmd +29 -0
- data/test/data/annotate.right +139 -0
- data/test/data/break_bad.cmd +18 -0
- data/test/data/break_bad.right +28 -0
- data/test/data/break_loop_bug.cmd +5 -0
- data/test/data/break_loop_bug.right +15 -0
- data/test/data/breakpoints.cmd +38 -0
- data/test/data/breakpoints.right +98 -0
- data/test/data/catch.cmd +20 -0
- data/test/data/catch.right +49 -0
- data/test/data/catch2.cmd +19 -0
- data/test/data/catch2.right +65 -0
- data/test/data/catch3.cmd +11 -0
- data/test/data/catch3.right +37 -0
- data/test/data/condition.cmd +28 -0
- data/test/data/condition.right +65 -0
- data/test/data/ctrl.cmd +23 -0
- data/test/data/ctrl.right +70 -0
- data/test/data/display.cmd +24 -0
- data/test/data/display.right +44 -0
- data/test/data/dollar-0.right +2 -0
- data/test/data/dollar-0a.right +2 -0
- data/test/data/dollar-0b.right +2 -0
- data/test/data/edit.cmd +12 -0
- data/test/data/edit.right +19 -0
- data/test/data/emacs_basic.cmd +43 -0
- data/test/data/emacs_basic.right +106 -0
- data/test/data/enable.cmd +20 -0
- data/test/data/enable.right +36 -0
- data/test/data/finish.cmd +16 -0
- data/test/data/finish.right +31 -0
- data/test/data/frame.cmd +26 -0
- data/test/data/frame.right +55 -0
- data/test/data/help.cmd +20 -0
- data/test/data/help.right +21 -0
- data/test/data/history.right +7 -0
- data/test/data/info-thread.cmd +13 -0
- data/test/data/info-thread.right +37 -0
- data/test/data/info-var-bug2.cmd +5 -0
- data/test/data/info-var-bug2.right +10 -0
- data/test/data/info-var.cmd +23 -0
- data/test/data/info-var.right +52 -0
- data/test/data/info.cmd +21 -0
- data/test/data/info.right +65 -0
- data/test/data/jump.cmd +16 -0
- data/test/data/jump.right +56 -0
- data/test/data/jump2.cmd +16 -0
- data/test/data/jump2.right +44 -0
- data/test/data/linetrace.cmd +6 -0
- data/test/data/linetrace.right +23 -0
- data/test/data/list.cmd +19 -0
- data/test/data/list.right +127 -0
- data/test/data/method.cmd +10 -0
- data/test/data/method.right +21 -0
- data/test/data/methodsig.cmd +10 -0
- data/test/data/methodsig.right +20 -0
- data/test/data/next.cmd +22 -0
- data/test/data/next.right +61 -0
- data/test/data/noquit.right +1 -0
- data/test/data/output.cmd +6 -0
- data/test/data/output.right +31 -0
- data/test/data/pm-bug.cmd +7 -0
- data/test/data/pm-bug.right +12 -0
- data/test/data/post-mortem-next.cmd +8 -0
- data/test/data/post-mortem-next.right +14 -0
- data/test/data/post-mortem-osx.right +31 -0
- data/test/data/post-mortem.cmd +13 -0
- data/test/data/post-mortem.right +32 -0
- data/test/data/quit.cmd +6 -0
- data/test/data/quit.right +0 -0
- data/test/data/raise.cmd +11 -0
- data/test/data/raise.right +23 -0
- data/test/data/save.cmd +34 -0
- data/test/data/save.right +59 -0
- data/test/data/scope-var.cmd +42 -0
- data/test/data/scope-var.right +587 -0
- data/test/data/setshow.cmd +56 -0
- data/test/data/setshow.right +98 -0
- data/test/data/source.cmd +5 -0
- data/test/data/source.right +15 -0
- data/test/data/stepping.cmd +21 -0
- data/test/data/stepping.right +50 -0
- data/test/data/test-init-cygwin.right +7 -0
- data/test/data/test-init-osx.right +4 -0
- data/test/data/test-init.right +5 -0
- data/test/data/trace.right +14 -0
- data/test/dollar-0.rb +5 -0
- data/test/gcd-dbg-nox.rb +31 -0
- data/test/gcd-dbg.rb +30 -0
- data/test/gcd.rb +18 -0
- data/test/helper.rb +144 -0
- data/test/info-var-bug.rb +47 -0
- data/test/info-var-bug2.rb +2 -0
- data/test/jump.rb +14 -0
- data/test/jump2.rb +27 -0
- data/test/next.rb +18 -0
- data/test/null.rb +1 -0
- data/test/output.rb +2 -0
- data/test/pm-base.rb +22 -0
- data/test/pm-bug.rb +3 -0
- data/test/pm-catch.rb +12 -0
- data/test/pm-catch2.rb +27 -0
- data/test/pm-catch3.rb +47 -0
- data/test/pm.rb +11 -0
- data/test/raise.rb +3 -0
- data/test/rdebug-save.1 +7 -0
- data/test/runall +12 -0
- data/test/scope-var.rb +29 -0
- data/test/tdebug.rb +248 -0
- data/test/test-annotate.rb +25 -0
- data/test/test-break-bad.rb +37 -0
- data/test/test-breakpoints.rb +25 -0
- data/test/test-catch.rb +25 -0
- data/test/test-catch2.rb +25 -0
- data/test/test-catch3.rb +25 -0
- data/test/test-condition.rb +25 -0
- data/test/test-ctrl.rb +55 -0
- data/test/test-display.rb +26 -0
- data/test/test-dollar-0.rb +40 -0
- data/test/test-edit.rb +26 -0
- data/test/test-emacs-basic.rb +26 -0
- data/test/test-enable.rb +25 -0
- data/test/test-finish.rb +34 -0
- data/test/test-frame.rb +34 -0
- data/test/test-help.rb +60 -0
- data/test/test-hist.rb +68 -0
- data/test/test-info-thread.rb +32 -0
- data/test/test-info-var.rb +47 -0
- data/test/test-info.rb +26 -0
- data/test/test-init.rb +44 -0
- data/test/test-jump.rb +35 -0
- data/test/test-list.rb +25 -0
- data/test/test-method.rb +34 -0
- data/test/test-next.rb +25 -0
- data/test/test-output.rb +26 -0
- data/test/test-quit.rb +30 -0
- data/test/test-raise.rb +25 -0
- data/test/test-save.rb +31 -0
- data/test/test-scope-var.rb +25 -0
- data/test/test-setshow.rb +25 -0
- data/test/test-source.rb +25 -0
- data/test/test-stepping.rb +26 -0
- data/test/test-trace.rb +47 -0
- data/test/thread1.rb +26 -0
- data/test/trunc-call.rb +31 -0
- metadata +364 -0
@@ -0,0 +1,69 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
module ParseFunctions
|
4
|
+
Position_regexp = '(?:(\d+)|(.+?)[:.#]([^.:\s]+))'
|
5
|
+
|
6
|
+
# Parse 'str' of command 'cmd' as an integer between
|
7
|
+
# min and max. If either min or max is nil, that
|
8
|
+
# value has no bound.
|
9
|
+
def get_int(str, cmd, min=nil, max=nil, default=1)
|
10
|
+
return default unless str
|
11
|
+
begin
|
12
|
+
int = Integer(str)
|
13
|
+
if min and int < min
|
14
|
+
print "%s argument '%s' needs to at least %s.\n" % [cmd, str, min]
|
15
|
+
return nil
|
16
|
+
elsif max and int > max
|
17
|
+
print "%s argument '%s' needs to at most %s.\n" % [cmd, str, max]
|
18
|
+
return nil
|
19
|
+
end
|
20
|
+
return int
|
21
|
+
rescue
|
22
|
+
print "%s argument '%s' needs to be a number.\n" % [cmd, str]
|
23
|
+
return nil
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# Return true if arg is 'on' or 1 and false arg is 'off' or 0.
|
28
|
+
# Any other value raises RuntimeError.
|
29
|
+
def get_onoff(arg, default=nil, print_error=true)
|
30
|
+
if arg.nil? or arg == ''
|
31
|
+
if default.nil?
|
32
|
+
if print_error
|
33
|
+
print "Expecting 'on', 1, 'off', or 0. Got nothing.\n"
|
34
|
+
raise RuntimeError
|
35
|
+
end
|
36
|
+
return default
|
37
|
+
end
|
38
|
+
end
|
39
|
+
case arg.downcase
|
40
|
+
when '1', 'on'
|
41
|
+
return true
|
42
|
+
when '0', 'off'
|
43
|
+
return false
|
44
|
+
else
|
45
|
+
if print_error
|
46
|
+
print "Expecting 'on', 1, 'off', or 0. Got: %s.\n" % arg.to_s
|
47
|
+
raise RuntimeError
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
# Return 'on' or 'off' for supplied parameter. The parmeter should
|
53
|
+
# be true, false or nil.
|
54
|
+
def show_onoff(bool)
|
55
|
+
if not [TrueClass, FalseClass, NilClass].member?(bool.class)
|
56
|
+
return "??"
|
57
|
+
end
|
58
|
+
return bool ? 'on' : 'off'
|
59
|
+
end
|
60
|
+
|
61
|
+
# Return true if code is syntactically correct for Ruby.
|
62
|
+
def syntax_valid?(code)
|
63
|
+
eval("BEGIN {return true}\n#{code}", nil, "", 0)
|
64
|
+
rescue Exception
|
65
|
+
false
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,232 @@
|
|
1
|
+
module Debugger
|
2
|
+
class Interface # :nodoc:
|
3
|
+
attr_writer :have_readline # true if Readline is available
|
4
|
+
|
5
|
+
def initialize
|
6
|
+
@have_readline = false
|
7
|
+
end
|
8
|
+
|
9
|
+
# Common routine for reporting debugger error messages.
|
10
|
+
# Derived classed may want to override this to capture output.
|
11
|
+
def errmsg(*args)
|
12
|
+
if Debugger.annotate.to_i > 2
|
13
|
+
aprint 'error-begin'
|
14
|
+
print(*args)
|
15
|
+
aprint ''
|
16
|
+
else
|
17
|
+
print '*** '
|
18
|
+
print(*args)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Format msg with gdb-style annotation header
|
23
|
+
def afmt(msg, newline="\n")
|
24
|
+
"\032\032#{msg}#{newline}"
|
25
|
+
end
|
26
|
+
|
27
|
+
def aprint(msg)
|
28
|
+
print afmt(msg)
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
32
|
+
|
33
|
+
class LocalInterface < Interface # :nodoc:
|
34
|
+
attr_accessor :command_queue
|
35
|
+
attr_accessor :histfile
|
36
|
+
attr_accessor :history_save
|
37
|
+
attr_accessor :history_length
|
38
|
+
attr_accessor :restart_file
|
39
|
+
|
40
|
+
unless defined?(FILE_HISTORY)
|
41
|
+
FILE_HISTORY = ".rdebug_hist"
|
42
|
+
end
|
43
|
+
def initialize()
|
44
|
+
super
|
45
|
+
@command_queue = []
|
46
|
+
@have_readline = false
|
47
|
+
@history_save = true
|
48
|
+
# take gdb's default
|
49
|
+
@history_length = ENV["HISTSIZE"] ? ENV["HISTSIZE"].to_i : 256
|
50
|
+
@histfile = File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
|
51
|
+
FILE_HISTORY)
|
52
|
+
open(@histfile, 'r') do |file|
|
53
|
+
file.each do |line|
|
54
|
+
line.chomp!
|
55
|
+
Readline::HISTORY << line
|
56
|
+
end
|
57
|
+
end if File.exist?(@histfile)
|
58
|
+
@restart_file = nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def read_command(prompt)
|
62
|
+
readline(prompt, true)
|
63
|
+
end
|
64
|
+
|
65
|
+
def confirm(prompt)
|
66
|
+
readline(prompt, false)
|
67
|
+
end
|
68
|
+
|
69
|
+
def print(*args)
|
70
|
+
STDOUT.printf(*args)
|
71
|
+
end
|
72
|
+
|
73
|
+
def close
|
74
|
+
end
|
75
|
+
|
76
|
+
# Things to do before quitting
|
77
|
+
def finalize
|
78
|
+
if Debugger.method_defined?("annotate") and Debugger.annotate.to_i > 2
|
79
|
+
print "\032\032exited\n\n"
|
80
|
+
end
|
81
|
+
if Debugger.respond_to?(:save_history)
|
82
|
+
Debugger.save_history
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
def readline_support?
|
87
|
+
@have_readline
|
88
|
+
end
|
89
|
+
|
90
|
+
private
|
91
|
+
begin
|
92
|
+
require 'readline'
|
93
|
+
class << Debugger
|
94
|
+
@have_readline = true
|
95
|
+
define_method(:save_history) do
|
96
|
+
iface = self.handler.interface
|
97
|
+
iface.histfile ||= File.join(ENV["HOME"]||ENV["HOMEPATH"]||".",
|
98
|
+
FILE_HISTORY)
|
99
|
+
open(iface.histfile, 'w') do |file|
|
100
|
+
Readline::HISTORY.to_a.last(iface.history_length).each do |line|
|
101
|
+
file.puts line unless line.strip.empty?
|
102
|
+
end if defined?(iface.history_save) and iface.history_save
|
103
|
+
end rescue nil
|
104
|
+
end
|
105
|
+
public :save_history
|
106
|
+
end
|
107
|
+
Debugger.debug_at_exit do
|
108
|
+
finalize if respond_to?(:finalize)
|
109
|
+
end
|
110
|
+
|
111
|
+
def readline(prompt, hist)
|
112
|
+
Readline::readline(prompt, hist)
|
113
|
+
end
|
114
|
+
rescue LoadError
|
115
|
+
def readline(prompt, hist)
|
116
|
+
@histfile = ''
|
117
|
+
@hist_save = false
|
118
|
+
STDOUT.print prompt
|
119
|
+
STDOUT.flush
|
120
|
+
line = STDIN.gets
|
121
|
+
exit unless line
|
122
|
+
line.chomp!
|
123
|
+
line
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
class RemoteInterface < Interface # :nodoc:
|
129
|
+
attr_accessor :command_queue
|
130
|
+
attr_accessor :histfile
|
131
|
+
attr_accessor :history_save
|
132
|
+
attr_accessor :history_length
|
133
|
+
attr_accessor :restart_file
|
134
|
+
|
135
|
+
def initialize(socket)
|
136
|
+
@command_queue = []
|
137
|
+
@socket = socket
|
138
|
+
@history_save = false
|
139
|
+
@history_length = 256
|
140
|
+
@histfile = ''
|
141
|
+
# Do we read the histfile?
|
142
|
+
# open(@histfile, 'r') do |file|
|
143
|
+
# file.each do |line|
|
144
|
+
# line.chomp!
|
145
|
+
# Readline::HISTORY << line
|
146
|
+
# end
|
147
|
+
# end if File.exist?(@histfile)
|
148
|
+
@restart_file = nil
|
149
|
+
end
|
150
|
+
|
151
|
+
def close
|
152
|
+
@socket.close
|
153
|
+
rescue Exception
|
154
|
+
end
|
155
|
+
|
156
|
+
def confirm(prompt)
|
157
|
+
send_command "CONFIRM #{prompt}"
|
158
|
+
end
|
159
|
+
|
160
|
+
def finalize
|
161
|
+
end
|
162
|
+
|
163
|
+
def read_command(prompt)
|
164
|
+
send_command "PROMPT #{prompt}"
|
165
|
+
end
|
166
|
+
|
167
|
+
def readline_support?
|
168
|
+
false
|
169
|
+
end
|
170
|
+
|
171
|
+
def print(*args)
|
172
|
+
@socket.printf(*args)
|
173
|
+
end
|
174
|
+
|
175
|
+
private
|
176
|
+
|
177
|
+
def send_command(msg)
|
178
|
+
@socket.puts msg
|
179
|
+
result = @socket.gets
|
180
|
+
raise IOError unless result
|
181
|
+
result.chomp
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
class ScriptInterface < Interface # :nodoc:
|
186
|
+
attr_accessor :command_queue
|
187
|
+
attr_accessor :histfile
|
188
|
+
attr_accessor :history_save
|
189
|
+
attr_accessor :history_length
|
190
|
+
attr_accessor :restart_file
|
191
|
+
def initialize(file, out, verbose=false)
|
192
|
+
super()
|
193
|
+
@command_queue = []
|
194
|
+
@file = file.respond_to?(:gets) ? file : open(file)
|
195
|
+
@out = out
|
196
|
+
@verbose = verbose
|
197
|
+
@history_save = false
|
198
|
+
@history_length = 256 # take gdb default
|
199
|
+
@histfile = ''
|
200
|
+
end
|
201
|
+
|
202
|
+
def finalize
|
203
|
+
end
|
204
|
+
|
205
|
+
def read_command(prompt)
|
206
|
+
while result = @file.gets
|
207
|
+
puts "# #{result}" if @verbose
|
208
|
+
next if result =~ /^\s*#/
|
209
|
+
next if result.strip.empty?
|
210
|
+
break
|
211
|
+
end
|
212
|
+
raise IOError unless result
|
213
|
+
result.chomp!
|
214
|
+
end
|
215
|
+
|
216
|
+
def readline_support?
|
217
|
+
false
|
218
|
+
end
|
219
|
+
|
220
|
+
def confirm(prompt)
|
221
|
+
'y'
|
222
|
+
end
|
223
|
+
|
224
|
+
def print(*args)
|
225
|
+
@out.printf(*args)
|
226
|
+
end
|
227
|
+
|
228
|
+
def close
|
229
|
+
@file.close
|
230
|
+
end
|
231
|
+
end
|
232
|
+
end
|
@@ -0,0 +1,474 @@
|
|
1
|
+
require_relative 'interface'
|
2
|
+
require_relative 'command'
|
3
|
+
|
4
|
+
module Debugger
|
5
|
+
|
6
|
+
# Should this be a mixin?
|
7
|
+
class Processor # :nodoc
|
8
|
+
attr_accessor :interface
|
9
|
+
|
10
|
+
# Format msg with gdb-style annotation header
|
11
|
+
def afmt(msg, newline="\n")
|
12
|
+
"\032\032#{msg}#{newline}"
|
13
|
+
end
|
14
|
+
|
15
|
+
def aprint(msg)
|
16
|
+
print afmt(msg) if Debugger.annotate.to_i > 2
|
17
|
+
end
|
18
|
+
|
19
|
+
# FIXME: use delegate?
|
20
|
+
def errmsg(*args)
|
21
|
+
@interface.errmsg(*args)
|
22
|
+
end
|
23
|
+
|
24
|
+
# Callers of this routine should make sure to use comma to
|
25
|
+
# separate format argments rather than %. Otherwise it seems that
|
26
|
+
# if the string you want to print has format specifier, which
|
27
|
+
# could happen if you are trying to show say a source-code line
|
28
|
+
# with "puts" or "print" in it, this print routine will give an
|
29
|
+
# error saying it is looking for more arguments.
|
30
|
+
def print(*args)
|
31
|
+
@interface.print(*args)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
class CommandProcessor < Processor # :nodoc:
|
37
|
+
attr_reader :display
|
38
|
+
|
39
|
+
# FIXME: get from Command regexp method.
|
40
|
+
@@Show_breakpoints_postcmd = [
|
41
|
+
/^\s*b(?:reak)?/,
|
42
|
+
/^\s* cond(?:ition)? (?:\s+(\d+)\s*(.*))?$/ix,
|
43
|
+
/^\s*del(?:ete)?(?:\s+(.*))?$/ix,
|
44
|
+
/^\s* dis(?:able)? (?:\s+(.*))?$/ix,
|
45
|
+
/^\s* en(?:able)? (?:\s+(.*))?$/ix,
|
46
|
+
# "tbreak", "clear",
|
47
|
+
]
|
48
|
+
@@Show_annotations_run = [
|
49
|
+
/^\s*c(?:ont(?:inue)?)?(?:\s+(.*))?$/,
|
50
|
+
/^\s*fin(?:ish)?$/,
|
51
|
+
/^\s*n(?:ext)?([+-])?(?:\s+(.*))?$/,
|
52
|
+
/^\s*s(?:tep)?([+-])?(?:\s+(.*))?$/
|
53
|
+
]
|
54
|
+
|
55
|
+
@@Show_annotations_postcmd = [
|
56
|
+
/^\s* down (?:\s+(.*))? .*$/x,
|
57
|
+
/^\s* f(?:rame)? (?:\s+ (.*))? \s*$/x,
|
58
|
+
/^\s* u(?:p)? (?:\s+(.*))?$/x
|
59
|
+
]
|
60
|
+
|
61
|
+
def initialize(interface = LocalInterface.new)
|
62
|
+
@interface = interface
|
63
|
+
@display = []
|
64
|
+
|
65
|
+
@mutex = Mutex.new
|
66
|
+
@last_cmd = nil
|
67
|
+
@last_file = nil # Filename the last time we stopped
|
68
|
+
@last_line = nil # line number the last time we stopped
|
69
|
+
@debugger_breakpoints_were_empty = false # Show breakpoints 1st time
|
70
|
+
@debugger_displays_were_empty = true # No display 1st time
|
71
|
+
@debugger_context_was_dead = true # Assume we haven't started.
|
72
|
+
end
|
73
|
+
|
74
|
+
def interface=(interface)
|
75
|
+
@mutex.synchronize do
|
76
|
+
@interface.close if @interface
|
77
|
+
@interface = interface
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
require 'pathname' # For cleanpath
|
82
|
+
|
83
|
+
# Regularize file name.
|
84
|
+
# This is also used as a common funnel place if basename is
|
85
|
+
# desired or if we are working remotely and want to change the
|
86
|
+
# basename. Or we are eliding filenames.
|
87
|
+
def self.canonic_file(filename)
|
88
|
+
# For now we want resolved filenames
|
89
|
+
if Command.settings[:basename]
|
90
|
+
File.basename(filename)
|
91
|
+
else
|
92
|
+
# Cache this?
|
93
|
+
Pathname.new(filename).cleanpath.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def self.print_location_and_text(file, line)
|
98
|
+
file_line = "%s:%s\n%s" % [canonic_file(file), line,
|
99
|
+
Debugger.line_at(file, line)]
|
100
|
+
# FIXME: use annotations routines
|
101
|
+
if Debugger.annotate.to_i > 2
|
102
|
+
file_line = "\032\032source #{file_line}"
|
103
|
+
elsif ENV['EMACS']
|
104
|
+
file_line = "\032\032#{file_line}"
|
105
|
+
end
|
106
|
+
print file_line
|
107
|
+
end
|
108
|
+
|
109
|
+
def self.protect(mname)
|
110
|
+
alias_method "__#{mname}", mname
|
111
|
+
module_eval %{
|
112
|
+
def #{mname}(*args)
|
113
|
+
@mutex.synchronize do
|
114
|
+
return unless @interface
|
115
|
+
__#{mname}(*args)
|
116
|
+
end
|
117
|
+
rescue IOError, Errno::EPIPE
|
118
|
+
self.interface = nil
|
119
|
+
rescue SignalException
|
120
|
+
raise
|
121
|
+
rescue Exception
|
122
|
+
print "INTERNAL ERROR!!! #\{$!\}\n" rescue nil
|
123
|
+
print $!.backtrace.map{|l| "\t#\{l\}"}.join("\n") rescue nil
|
124
|
+
end
|
125
|
+
}
|
126
|
+
end
|
127
|
+
|
128
|
+
def at_breakpoint(context, breakpoint)
|
129
|
+
aprint 'stopped' if Debugger.annotate.to_i > 2
|
130
|
+
n = Debugger.breakpoints.index(breakpoint) + 1
|
131
|
+
file = CommandProcessor.canonic_file(breakpoint.source)
|
132
|
+
line = breakpoint.pos
|
133
|
+
if Debugger.annotate.to_i > 2
|
134
|
+
print afmt("source #{file}:#{line}")
|
135
|
+
end
|
136
|
+
print "Breakpoint %d at %s:%s\n", n, file, line
|
137
|
+
end
|
138
|
+
protect :at_breakpoint
|
139
|
+
|
140
|
+
def at_catchpoint(context, excpt)
|
141
|
+
aprint 'stopped' if Debugger.annotate.to_i > 2
|
142
|
+
file = CommandProcessor.canonic_file(context.frame_file(0))
|
143
|
+
line = context.frame_line(0)
|
144
|
+
print afmt("%s:%d" % [file, line]) if ENV['EMACS']
|
145
|
+
print "Catchpoint at %s:%d: `%s' (%s)\n", file, line, excpt, excpt.class
|
146
|
+
fs = context.stack_size
|
147
|
+
tb = caller(0)[-fs..-1]
|
148
|
+
if tb
|
149
|
+
for i in tb
|
150
|
+
print "\tfrom %s\n", i
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
protect :at_catchpoint
|
155
|
+
|
156
|
+
def at_tracing(context, file, line)
|
157
|
+
return if defined?(Debugger::RDEBUG_FILE) &&
|
158
|
+
Debugger::RDEBUG_FILE == file # Don't trace ourself
|
159
|
+
@last_file = CommandProcessor.canonic_file(file)
|
160
|
+
file = CommandProcessor.canonic_file(file)
|
161
|
+
unless file == @last_file and @last_line == line and
|
162
|
+
Command.settings[:tracing_plus]
|
163
|
+
print "Tracing(%d):%s:%s %s",
|
164
|
+
context.thnum, file, line, Debugger.line_at(file, line)
|
165
|
+
@last_file = file
|
166
|
+
@last_line = line
|
167
|
+
end
|
168
|
+
always_run(context, file, line, 2)
|
169
|
+
end
|
170
|
+
protect :at_tracing
|
171
|
+
|
172
|
+
def at_line(context, file, line)
|
173
|
+
process_commands(context, file, line)
|
174
|
+
end
|
175
|
+
protect :at_line
|
176
|
+
|
177
|
+
def at_return(context, file, line)
|
178
|
+
context.stop_frame = -1
|
179
|
+
process_commands(context, file, line)
|
180
|
+
end
|
181
|
+
|
182
|
+
private
|
183
|
+
|
184
|
+
# The prompt shown before reading a command.
|
185
|
+
def prompt(context)
|
186
|
+
p = '(rdb:%s) ' % (context.dead? ? 'post-mortem' : context.thnum)
|
187
|
+
p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
|
188
|
+
Debugger.annotate.to_i > 2
|
189
|
+
return p
|
190
|
+
end
|
191
|
+
|
192
|
+
# Run these commands, for example display commands or possibly
|
193
|
+
# the list or irb in an "autolist" or "autoirb".
|
194
|
+
# We return a list of commands that are acceptable to run bound
|
195
|
+
# to the current state.
|
196
|
+
def always_run(context, file, line, run_level)
|
197
|
+
event_cmds = Command.commands.select{|cmd| cmd.event }
|
198
|
+
|
199
|
+
# Remove some commands in post-mortem
|
200
|
+
event_cmds = event_cmds.find_all do |cmd|
|
201
|
+
cmd.allow_in_post_mortem
|
202
|
+
end if context.dead?
|
203
|
+
|
204
|
+
state = State.new do |s|
|
205
|
+
s.context = context
|
206
|
+
s.file = file
|
207
|
+
s.line = line
|
208
|
+
s.binding = context.frame_binding(0)
|
209
|
+
s.display = display
|
210
|
+
s.interface = interface
|
211
|
+
s.commands = event_cmds
|
212
|
+
end
|
213
|
+
@interface.state = state if @interface.respond_to?('state=')
|
214
|
+
|
215
|
+
# Bind commands to the current state.
|
216
|
+
commands = event_cmds.map{|cmd| cmd.new(state)}
|
217
|
+
|
218
|
+
commands.select do |cmd|
|
219
|
+
cmd.class.always_run >= run_level
|
220
|
+
end.each {|cmd| cmd.execute}
|
221
|
+
return state, commands
|
222
|
+
end
|
223
|
+
|
224
|
+
# Handle debugger commands
|
225
|
+
def process_commands(context, file, line)
|
226
|
+
state, commands = always_run(context, file, line, 1)
|
227
|
+
$rdebug_state = state if Command.settings[:debuggertesting]
|
228
|
+
splitter = lambda do |str|
|
229
|
+
str.split(/;/).inject([]) do |m, v|
|
230
|
+
if m.empty?
|
231
|
+
m << v
|
232
|
+
else
|
233
|
+
if m.last[-1] == ?\\
|
234
|
+
m.last[-1,1] = ''
|
235
|
+
m.last << ';' << v
|
236
|
+
else
|
237
|
+
m << v
|
238
|
+
end
|
239
|
+
end
|
240
|
+
m
|
241
|
+
end
|
242
|
+
end
|
243
|
+
|
244
|
+
preloop(commands, context)
|
245
|
+
CommandProcessor.print_location_and_text(file, line)
|
246
|
+
while !state.proceed?
|
247
|
+
input = if @interface.command_queue.empty?
|
248
|
+
@interface.read_command(prompt(context))
|
249
|
+
else
|
250
|
+
@interface.command_queue.shift
|
251
|
+
end
|
252
|
+
break unless input
|
253
|
+
catch(:debug_error) do
|
254
|
+
if input == ""
|
255
|
+
next unless @last_cmd
|
256
|
+
input = @last_cmd
|
257
|
+
else
|
258
|
+
@last_cmd = input
|
259
|
+
end
|
260
|
+
splitter[input].each do |cmd|
|
261
|
+
one_cmd(commands, context, cmd)
|
262
|
+
postcmd(commands, context, cmd)
|
263
|
+
end
|
264
|
+
end
|
265
|
+
end
|
266
|
+
postloop(commands, context)
|
267
|
+
end # process_commands
|
268
|
+
|
269
|
+
def one_cmd(commands, context, input)
|
270
|
+
if cmd = commands.find{ |c| c.match(input) }
|
271
|
+
if context.dead? && cmd.class.need_context
|
272
|
+
p cmd
|
273
|
+
print "Command is unavailable\n"
|
274
|
+
else
|
275
|
+
cmd.execute
|
276
|
+
end
|
277
|
+
else
|
278
|
+
unknown_cmd = commands.find{ |c| c.class.unknown }
|
279
|
+
if unknown_cmd
|
280
|
+
unknown_cmd.execute
|
281
|
+
else
|
282
|
+
errmsg "Unknown command: \"#{input}\". Try \"help\".\n"
|
283
|
+
end
|
284
|
+
end
|
285
|
+
end
|
286
|
+
|
287
|
+
def preloop(commands, context)
|
288
|
+
aprint('stopped') if Debugger.annotate.to_i > 2
|
289
|
+
if context.dead?
|
290
|
+
unless @debugger_context_was_dead
|
291
|
+
if Debugger.annotate.to_i > 2
|
292
|
+
aprint('exited')
|
293
|
+
print "The program finished.\n"
|
294
|
+
end
|
295
|
+
@debugger_context_was_dead = true
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
if Debugger.annotate.to_i > 2
|
300
|
+
# if we are here, the stack frames have changed outside the
|
301
|
+
# command loop (e.g. after a "continue" command), so we show
|
302
|
+
# the annotations again
|
303
|
+
breakpoint_annotations(commands, context)
|
304
|
+
display_annotations(commands, context)
|
305
|
+
annotation('stack', commands, context, "where")
|
306
|
+
annotation('variables', commands, context, "info variables") unless
|
307
|
+
context.dead?
|
308
|
+
end
|
309
|
+
end
|
310
|
+
|
311
|
+
def postcmd(commands, context, cmd)
|
312
|
+
if Debugger.annotate.to_i > 0
|
313
|
+
cmd = @last_cmd unless cmd
|
314
|
+
breakpoint_annotations(commands, context) if
|
315
|
+
@@Show_breakpoints_postcmd.find{|pat| cmd =~ pat}
|
316
|
+
display_annotations(commands, context)
|
317
|
+
if @@Show_annotations_postcmd.find{|pat| cmd =~ pat}
|
318
|
+
annotation('stack', commands, context, "where") if
|
319
|
+
context.stack_size > 0
|
320
|
+
annotation('variables', commands, context, "info variables") unless
|
321
|
+
context.dead?
|
322
|
+
end
|
323
|
+
if not context.dead? and @@Show_annotations_run.find{|pat| cmd =~ pat}
|
324
|
+
aprint 'starting' if Debugger.annotate.to_i > 2
|
325
|
+
|
326
|
+
@debugger_context_was_dead = false
|
327
|
+
end
|
328
|
+
end
|
329
|
+
end
|
330
|
+
|
331
|
+
def postloop(commands, context)
|
332
|
+
end
|
333
|
+
|
334
|
+
def annotation(label, commands, context, cmd)
|
335
|
+
print afmt(label)
|
336
|
+
one_cmd(commands, context, cmd)
|
337
|
+
### FIXME ANNOTATE: the following line should be deleted
|
338
|
+
print "\032\032\n"
|
339
|
+
end
|
340
|
+
|
341
|
+
def breakpoint_annotations(commands, context)
|
342
|
+
unless Debugger.breakpoints.empty? and @debugger_breakpoints_were_empty
|
343
|
+
annotation('breakpoints', commands, context, "info breakpoints")
|
344
|
+
@debugger_breakpoints_were_empty = Debugger.breakpoints.empty?
|
345
|
+
end
|
346
|
+
end
|
347
|
+
|
348
|
+
def display_annotations(commands, context)
|
349
|
+
return if display.empty?
|
350
|
+
# have_display = display.find{|d| d[0]}
|
351
|
+
# return unless have_display and @debugger_displays_were_empty
|
352
|
+
# @debugger_displays_were_empty = have_display
|
353
|
+
annotation('display', commands, context, "display")
|
354
|
+
end
|
355
|
+
|
356
|
+
class State # :nodoc:
|
357
|
+
attr_accessor :context, :file, :line, :binding
|
358
|
+
attr_accessor :frame_pos, :previous_line, :display
|
359
|
+
attr_accessor :interface, :commands
|
360
|
+
|
361
|
+
def initialize
|
362
|
+
super()
|
363
|
+
@frame_pos = 0
|
364
|
+
@previous_line = nil
|
365
|
+
@proceed = false
|
366
|
+
yield self
|
367
|
+
end
|
368
|
+
|
369
|
+
# FIXME: use delegate?
|
370
|
+
def errmsg(*args)
|
371
|
+
@interface.errmsg(*args)
|
372
|
+
end
|
373
|
+
|
374
|
+
def print(*args)
|
375
|
+
@interface.print(*args)
|
376
|
+
end
|
377
|
+
|
378
|
+
def confirm(*args)
|
379
|
+
@interface.confirm(*args)
|
380
|
+
end
|
381
|
+
|
382
|
+
def proceed?
|
383
|
+
@proceed
|
384
|
+
end
|
385
|
+
|
386
|
+
def proceed
|
387
|
+
@proceed = true
|
388
|
+
end
|
389
|
+
end
|
390
|
+
end
|
391
|
+
|
392
|
+
class ControlCommandProcessor < Processor # :nodoc:
|
393
|
+
def initialize(interface)
|
394
|
+
super()
|
395
|
+
@interface = interface
|
396
|
+
@debugger_context_was_dead = true # Assume we haven't started.
|
397
|
+
end
|
398
|
+
|
399
|
+
def process_commands(verbose=false)
|
400
|
+
control_cmds = Command.commands.select do |cmd|
|
401
|
+
cmd.allow_in_control
|
402
|
+
end
|
403
|
+
state = State.new(@interface, control_cmds)
|
404
|
+
commands = control_cmds.map{|cmd| cmd.new(state) }
|
405
|
+
|
406
|
+
unless @debugger_context_was_dead
|
407
|
+
if Debugger.annotate.to_i > 2
|
408
|
+
aprint 'exited'
|
409
|
+
print "The program finished.\n"
|
410
|
+
end
|
411
|
+
@debugger_context_was_dead = true
|
412
|
+
end
|
413
|
+
|
414
|
+
while input = @interface.read_command(prompt(nil))
|
415
|
+
print "+#{input}" if verbose
|
416
|
+
catch(:debug_error) do
|
417
|
+
if cmd = commands.find{|c| c.match(input) }
|
418
|
+
cmd.execute
|
419
|
+
else
|
420
|
+
errmsg "Unknown command\n"
|
421
|
+
end
|
422
|
+
end
|
423
|
+
end
|
424
|
+
rescue IOError, Errno::EPIPE
|
425
|
+
rescue Exception
|
426
|
+
print "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
427
|
+
print $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
428
|
+
ensure
|
429
|
+
@interface.close
|
430
|
+
end
|
431
|
+
|
432
|
+
# The prompt shown before reading a command.
|
433
|
+
# Note: have an unused 'context' parameter to match the local interface.
|
434
|
+
def prompt(context)
|
435
|
+
p = '(rdb:ctrl) '
|
436
|
+
p = afmt("pre-prompt")+p+"\n"+afmt("prompt") if
|
437
|
+
Debugger.annotate.to_i > 2
|
438
|
+
return p
|
439
|
+
end
|
440
|
+
|
441
|
+
class State # :nodoc:
|
442
|
+
attr_reader :commands, :interface
|
443
|
+
|
444
|
+
def initialize(interface, commands)
|
445
|
+
@interface = interface
|
446
|
+
@commands = commands
|
447
|
+
end
|
448
|
+
|
449
|
+
def proceed
|
450
|
+
end
|
451
|
+
|
452
|
+
def errmsg(*args)
|
453
|
+
@interface.print(*args)
|
454
|
+
end
|
455
|
+
|
456
|
+
def print(*args)
|
457
|
+
@interface.print(*args)
|
458
|
+
end
|
459
|
+
|
460
|
+
def confirm(*args)
|
461
|
+
'y'
|
462
|
+
end
|
463
|
+
|
464
|
+
def context
|
465
|
+
nil
|
466
|
+
end
|
467
|
+
|
468
|
+
def file
|
469
|
+
errmsg "No filename given.\n"
|
470
|
+
throw :debug_error
|
471
|
+
end
|
472
|
+
end # State
|
473
|
+
end
|
474
|
+
end
|