needy_debugger 1.4.0
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/.gitignore +14 -0
- data/.travis.yml +8 -0
- data/AUTHORS +10 -0
- data/CHANGELOG.md +68 -0
- data/CONTRIBUTING.md +1 -0
- data/Gemfile +3 -0
- data/LICENSE +23 -0
- data/OLDER_CHANGELOG +334 -0
- data/OLD_CHANGELOG +5655 -0
- data/OLD_README +122 -0
- data/README.md +141 -0
- data/Rakefile +78 -0
- data/bin/rdebug +397 -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/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/192/breakpoint.c +586 -0
- data/ext/ruby_debug/192/ruby_debug.c +2645 -0
- data/ext/ruby_debug/192/ruby_debug.h +148 -0
- data/ext/ruby_debug/193/breakpoint.c +586 -0
- data/ext/ruby_debug/193/ruby_debug.c +2626 -0
- data/ext/ruby_debug/193/ruby_debug.h +148 -0
- data/ext/ruby_debug/200/breakpoint.c +586 -0
- data/ext/ruby_debug/200/ruby_debug.c +2692 -0
- data/ext/ruby_debug/200/ruby_debug.h +148 -0
- data/ext/ruby_debug/extconf.rb +94 -0
- data/lib/debugger.rb +5 -0
- data/lib/debugger/version.rb +5 -0
- data/lib/ruby-debug-base.rb +305 -0
- data/lib/ruby-debug.rb +177 -0
- data/lib/ruby-debug/command.rb +227 -0
- data/lib/ruby-debug/commands/breakpoints.rb +153 -0
- data/lib/ruby-debug/commands/catchpoint.rb +55 -0
- data/lib/ruby-debug/commands/condition.rb +49 -0
- data/lib/ruby-debug/commands/continue.rb +38 -0
- data/lib/ruby-debug/commands/control.rb +107 -0
- data/lib/ruby-debug/commands/display.rb +120 -0
- data/lib/ruby-debug/commands/edit.rb +48 -0
- data/lib/ruby-debug/commands/enable.rb +202 -0
- data/lib/ruby-debug/commands/eval.rb +176 -0
- data/lib/ruby-debug/commands/finish.rb +42 -0
- data/lib/ruby-debug/commands/frame.rb +301 -0
- data/lib/ruby-debug/commands/help.rb +56 -0
- data/lib/ruby-debug/commands/info.rb +467 -0
- data/lib/ruby-debug/commands/irb.rb +123 -0
- data/lib/ruby-debug/commands/jump.rb +66 -0
- data/lib/ruby-debug/commands/kill.rb +51 -0
- data/lib/ruby-debug/commands/list.rb +94 -0
- data/lib/ruby-debug/commands/method.rb +84 -0
- data/lib/ruby-debug/commands/quit.rb +50 -0
- data/lib/ruby-debug/commands/reload.rb +40 -0
- data/lib/ruby-debug/commands/save.rb +90 -0
- data/lib/ruby-debug/commands/set.rb +223 -0
- data/lib/ruby-debug/commands/show.rb +247 -0
- data/lib/ruby-debug/commands/skip.rb +35 -0
- data/lib/ruby-debug/commands/source.rb +36 -0
- data/lib/ruby-debug/commands/stepping.rb +81 -0
- data/lib/ruby-debug/commands/threads.rb +189 -0
- data/lib/ruby-debug/commands/tmate.rb +36 -0
- data/lib/ruby-debug/commands/trace.rb +57 -0
- data/lib/ruby-debug/commands/variables.rb +199 -0
- data/lib/ruby-debug/debugger.rb +5 -0
- data/lib/ruby-debug/helper.rb +69 -0
- data/lib/ruby-debug/interface.rb +232 -0
- data/lib/ruby-debug/processor.rb +474 -0
- data/man/rdebug.1 +241 -0
- data/needy_debugger.gemspec +31 -0
- data/old_scripts/Makefile.am +14 -0
- data/old_scripts/README.md +2 -0
- data/old_scripts/autogen.sh +4 -0
- data/old_scripts/configure.ac +12 -0
- data/old_scripts/rdbg.rb +33 -0
- data/old_scripts/runner.sh +7 -0
- data/old_scripts/svn2cl_usermap +3 -0
- data/test/.cvsignore +1 -0
- data/test/breakpoints_test.rb +365 -0
- data/test/conditions_test.rb +76 -0
- data/test/continue_test.rb +28 -0
- data/test/display_test.rb +141 -0
- data/test/edit_test.rb +55 -0
- data/test/eval_test.rb +92 -0
- data/test/examples/breakpoint1.rb +15 -0
- data/test/examples/breakpoint2.rb +7 -0
- data/test/examples/conditions.rb +4 -0
- data/test/examples/continue.rb +4 -0
- data/test/examples/display.rb +5 -0
- data/test/examples/edit.rb +3 -0
- data/test/examples/edit2.rb +3 -0
- data/test/examples/eval.rb +4 -0
- data/test/examples/finish.rb +20 -0
- data/test/examples/frame.rb +31 -0
- data/test/examples/help.rb +2 -0
- data/test/examples/info.rb +48 -0
- data/test/examples/info2.rb +3 -0
- data/test/examples/irb.rb +6 -0
- data/test/examples/jump.rb +14 -0
- data/test/examples/kill.rb +2 -0
- data/test/examples/list.rb +12 -0
- data/test/examples/method.rb +15 -0
- data/test/examples/post_mortem.rb +19 -0
- data/test/examples/quit.rb +2 -0
- data/test/examples/reload.rb +6 -0
- data/test/examples/restart.rb +6 -0
- data/test/examples/save.rb +3 -0
- data/test/examples/set.rb +3 -0
- data/test/examples/set_annotate.rb +12 -0
- data/test/examples/settings.rb +1 -0
- data/test/examples/show.rb +2 -0
- data/test/examples/source.rb +3 -0
- data/test/examples/stepping.rb +21 -0
- data/test/examples/thread.rb +32 -0
- data/test/examples/tmate.rb +10 -0
- data/test/examples/trace.rb +7 -0
- data/test/examples/trace_threads.rb +20 -0
- data/test/examples/variables.rb +26 -0
- data/test/finish_test.rb +48 -0
- data/test/frame_test.rb +140 -0
- data/test/help_test.rb +50 -0
- data/test/info_test.rb +325 -0
- data/test/irb_test.rb +81 -0
- data/test/jump_test.rb +70 -0
- data/test/kill_test.rb +47 -0
- data/test/list_test.rb +145 -0
- data/test/method_test.rb +70 -0
- data/test/post_mortem_test.rb +25 -0
- data/test/quit_test.rb +55 -0
- data/test/reload_test.rb +43 -0
- data/test/restart_test.rb +143 -0
- data/test/save_test.rb +92 -0
- data/test/set_test.rb +177 -0
- data/test/show_test.rb +292 -0
- data/test/source_test.rb +44 -0
- data/test/stepping_test.rb +118 -0
- data/test/support/breakpoint.rb +12 -0
- data/test/support/context.rb +14 -0
- data/test/support/matchers.rb +67 -0
- data/test/support/mocha_extensions.rb +71 -0
- data/test/support/processor.rb +7 -0
- data/test/support/test_dsl.rb +205 -0
- data/test/support/test_interface.rb +66 -0
- data/test/test_helper.rb +8 -0
- data/test/thread_test.rb +122 -0
- data/test/tmate_test.rb +43 -0
- data/test/trace_test.rb +154 -0
- data/test/variables_test.rb +114 -0
- metadata +352 -0
data/lib/ruby-debug.rb
ADDED
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'stringio'
|
3
|
+
require 'socket'
|
4
|
+
require 'thread'
|
5
|
+
require 'ruby-debug-base'
|
6
|
+
require 'ruby-debug/processor'
|
7
|
+
|
8
|
+
module Debugger
|
9
|
+
self.handler = CommandProcessor.new
|
10
|
+
|
11
|
+
# the port number used for remote debugging
|
12
|
+
PORT = 8989 unless defined?(PORT)
|
13
|
+
|
14
|
+
# What file is used for debugger startup commands.
|
15
|
+
unless defined?(INITFILE)
|
16
|
+
if RUBY_PLATFORM =~ /mswin/
|
17
|
+
# Of course MS Windows has to be different
|
18
|
+
INITFILE = 'rdebug.ini'
|
19
|
+
HOME_DIR = (ENV['HOME'] ||
|
20
|
+
ENV['HOMEDRIVE'].to_s + ENV['HOMEPATH'].to_s).to_s
|
21
|
+
else
|
22
|
+
INITFILE = '.rdebugrc'
|
23
|
+
HOME_DIR = ENV['HOME'].to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
# gdb-style annotation mode. Used in GNU Emacs interface
|
29
|
+
attr_accessor :annotate
|
30
|
+
|
31
|
+
# in remote mode, wait for the remote connection
|
32
|
+
attr_accessor :wait_connection
|
33
|
+
|
34
|
+
# If set, a string to look for in caller() and is used to see
|
35
|
+
# if the call stack is truncated.
|
36
|
+
attr_accessor :start_sentinal
|
37
|
+
|
38
|
+
attr_reader :thread, :control_thread, :cmd_port, :ctrl_port
|
39
|
+
|
40
|
+
def interface=(value) # :nodoc:
|
41
|
+
handler.interface = value
|
42
|
+
end
|
43
|
+
|
44
|
+
#
|
45
|
+
# Starts a remote debugger.
|
46
|
+
#
|
47
|
+
def start_remote(host = nil, port = PORT)
|
48
|
+
return if @thread
|
49
|
+
|
50
|
+
self.interface = nil
|
51
|
+
start
|
52
|
+
|
53
|
+
if port.kind_of?(Array)
|
54
|
+
cmd_port, ctrl_port = port
|
55
|
+
else
|
56
|
+
cmd_port, ctrl_port = port, port + 1
|
57
|
+
end
|
58
|
+
|
59
|
+
ctrl_port = start_control(host, ctrl_port)
|
60
|
+
|
61
|
+
yield if block_given?
|
62
|
+
|
63
|
+
mutex = Mutex.new
|
64
|
+
proceed = ConditionVariable.new
|
65
|
+
|
66
|
+
server = TCPServer.new(host, cmd_port)
|
67
|
+
@cmd_port = cmd_port = server.addr[1]
|
68
|
+
|
69
|
+
@thread = DebugThread.new do
|
70
|
+
while (session = server.accept)
|
71
|
+
self.interface = RemoteInterface.new(session)
|
72
|
+
if wait_connection
|
73
|
+
mutex.synchronize do
|
74
|
+
proceed.signal
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
if wait_connection
|
80
|
+
mutex.synchronize do
|
81
|
+
proceed.wait(mutex)
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
alias start_server start_remote
|
86
|
+
|
87
|
+
def start_control(host = nil, ctrl_port = PORT + 1) # :nodoc:
|
88
|
+
return @ctrl_port if defined?(@control_thread) && @control_thread
|
89
|
+
server = TCPServer.new(host, ctrl_port)
|
90
|
+
@ctrl_port = server.addr[1]
|
91
|
+
@control_thread = DebugThread.new do
|
92
|
+
while (session = server.accept)
|
93
|
+
interface = RemoteInterface.new(session)
|
94
|
+
processor = ControlCommandProcessor.new(interface)
|
95
|
+
processor.process_commands
|
96
|
+
end
|
97
|
+
end
|
98
|
+
@ctrl_port
|
99
|
+
end
|
100
|
+
|
101
|
+
#
|
102
|
+
# Connects to the remote debugger
|
103
|
+
#
|
104
|
+
def start_client(host = 'localhost', port = PORT)
|
105
|
+
require "socket"
|
106
|
+
interface = Debugger::LocalInterface.new
|
107
|
+
socket = TCPSocket.new(host, port)
|
108
|
+
puts "Connected."
|
109
|
+
|
110
|
+
catch(:exit) do
|
111
|
+
while (line = socket.gets)
|
112
|
+
case line
|
113
|
+
when /^PROMPT (.*)$/
|
114
|
+
input = interface.read_command($1)
|
115
|
+
throw :exit unless input
|
116
|
+
socket.puts input
|
117
|
+
when /^CONFIRM (.*)$/
|
118
|
+
input = interface.confirm($1)
|
119
|
+
throw :exit unless input
|
120
|
+
socket.puts input
|
121
|
+
else
|
122
|
+
print line
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
socket.close
|
127
|
+
end
|
128
|
+
|
129
|
+
# Runs normal debugger initialization scripts
|
130
|
+
# Reads and executes the commands from init file (if any) in the
|
131
|
+
# current working directory. This is only done if the current
|
132
|
+
# directory is different from your home directory. Thus, you can
|
133
|
+
# have more than one init file, one generic in your home directory,
|
134
|
+
# and another, specific to the program you are debugging, in the
|
135
|
+
# directory where you invoke ruby-debug.
|
136
|
+
def run_init_script(out = handler.interface)
|
137
|
+
cwd_script_file = File.expand_path(File.join(".", INITFILE))
|
138
|
+
run_script(cwd_script_file, out) if File.exists?(cwd_script_file)
|
139
|
+
|
140
|
+
home_script_file = File.expand_path(File.join(HOME_DIR, INITFILE))
|
141
|
+
run_script(home_script_file, out) if File.exists?(home_script_file) and
|
142
|
+
cwd_script_file != home_script_file
|
143
|
+
end
|
144
|
+
|
145
|
+
#
|
146
|
+
# Runs a script file
|
147
|
+
#
|
148
|
+
def run_script(file, out = handler.interface, verbose=false)
|
149
|
+
interface = ScriptInterface.new(File.expand_path(file), out)
|
150
|
+
processor = ControlCommandProcessor.new(interface)
|
151
|
+
processor.process_commands(verbose)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
module Kernel
|
157
|
+
|
158
|
+
# Enters the debugger in the current thread after _steps_ line events occur.
|
159
|
+
# Before entering the debugger startup script is read.
|
160
|
+
#
|
161
|
+
# Setting _steps_ to 0 will cause a break in the debugger subroutine
|
162
|
+
# and not wait for a line event to occur. You will have to go "up 1"
|
163
|
+
# in order to be back in your debugged program rather than the
|
164
|
+
# debugger. Settings _steps_ to 0 could be useful you want to stop
|
165
|
+
# right after the last statement in some scope, because the next
|
166
|
+
# step will take you out of some scope.
|
167
|
+
def debugger(steps = 1)
|
168
|
+
Debugger.start
|
169
|
+
Debugger.run_init_script(StringIO.new)
|
170
|
+
if 0 == steps
|
171
|
+
Debugger.current_context.stop_frame = 0
|
172
|
+
else
|
173
|
+
Debugger.current_context.stop_next = steps
|
174
|
+
end
|
175
|
+
end
|
176
|
+
alias breakpoint debugger unless respond_to?(:breakpoint)
|
177
|
+
end
|
@@ -0,0 +1,227 @@
|
|
1
|
+
require 'columnize'
|
2
|
+
require 'ruby-debug/helper'
|
3
|
+
|
4
|
+
module Debugger
|
5
|
+
RUBY_DEBUG_DIR = File.expand_path(File.dirname(__FILE__)) unless
|
6
|
+
defined?(RUBY_DEBUG_DIR)
|
7
|
+
|
8
|
+
class Command # :nodoc:
|
9
|
+
SubcmdStruct=Struct.new(:name, :min, :short_help, :long_help) unless
|
10
|
+
defined?(SubcmdStruct)
|
11
|
+
|
12
|
+
include Columnize
|
13
|
+
|
14
|
+
# Find param in subcmds. param id downcased and can be abbreviated
|
15
|
+
# to the minimum length listed in the subcommands
|
16
|
+
def find(subcmds, param)
|
17
|
+
param.downcase!
|
18
|
+
for try_subcmd in subcmds do
|
19
|
+
if (param.size >= try_subcmd.min) and
|
20
|
+
(try_subcmd.name[0..param.size-1] == param)
|
21
|
+
return try_subcmd
|
22
|
+
end
|
23
|
+
end
|
24
|
+
return nil
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def commands
|
29
|
+
@commands ||= []
|
30
|
+
end
|
31
|
+
|
32
|
+
DEF_OPTIONS = {
|
33
|
+
:allow_in_control => false,
|
34
|
+
:allow_in_post_mortem => true,
|
35
|
+
:event => true,
|
36
|
+
:always_run => 0,
|
37
|
+
:unknown => false,
|
38
|
+
:need_context => false,
|
39
|
+
} unless defined?(DEF_OPTIONS)
|
40
|
+
|
41
|
+
def inherited(klass)
|
42
|
+
DEF_OPTIONS.each do |o, v|
|
43
|
+
klass.options[o] = v if klass.options[o].nil?
|
44
|
+
end
|
45
|
+
commands << klass
|
46
|
+
end
|
47
|
+
|
48
|
+
def load_commands
|
49
|
+
Dir[File.join(Debugger.const_get(:RUBY_DEBUG_DIR), 'commands', '*')].each do |file|
|
50
|
+
require file if file =~ /\.rb$/
|
51
|
+
end
|
52
|
+
Debugger.constants.grep(/Functions$/).map { |name| Debugger.const_get(name) }.each do |mod|
|
53
|
+
include mod
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
def method_missing(meth, *args, &block)
|
58
|
+
if meth.to_s =~ /^(.+?)=$/
|
59
|
+
@options[$1.intern] = args.first
|
60
|
+
else
|
61
|
+
if @options.has_key?(meth)
|
62
|
+
@options[meth]
|
63
|
+
else
|
64
|
+
super
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def options
|
70
|
+
@options ||= {}
|
71
|
+
end
|
72
|
+
|
73
|
+
def settings_map
|
74
|
+
@@settings_map ||= {}
|
75
|
+
end
|
76
|
+
private :settings_map
|
77
|
+
|
78
|
+
def settings
|
79
|
+
unless true and defined? @settings and @settings
|
80
|
+
@settings = Object.new
|
81
|
+
map = settings_map
|
82
|
+
c = class << @settings; self end
|
83
|
+
if c.respond_to?(:funcall)
|
84
|
+
c.funcall(:define_method, :[]) do |name|
|
85
|
+
raise "No such setting #{name}" unless map.has_key?(name)
|
86
|
+
map[name][:getter].call
|
87
|
+
end
|
88
|
+
else
|
89
|
+
c.send(:define_method, :[]) do |name|
|
90
|
+
raise "No such setting #{name}" unless map.has_key?(name)
|
91
|
+
map[name][:getter].call
|
92
|
+
end
|
93
|
+
end
|
94
|
+
c = class << @settings; self end
|
95
|
+
if c.respond_to?(:funcall)
|
96
|
+
c.funcall(:define_method, :[]=) do |name, value|
|
97
|
+
raise "No such setting #{name}" unless map.has_key?(name)
|
98
|
+
map[name][:setter].call(value)
|
99
|
+
end
|
100
|
+
else
|
101
|
+
c.send(:define_method, :[]=) do |name, value|
|
102
|
+
raise "No such setting #{name}" unless map.has_key?(name)
|
103
|
+
map[name][:setter].call(value)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
@settings
|
108
|
+
end
|
109
|
+
|
110
|
+
def register_setting_var(name, default)
|
111
|
+
var_name = "@@#{name}"
|
112
|
+
class_variable_set(var_name, default)
|
113
|
+
register_setting_get(name) { class_variable_get(var_name) }
|
114
|
+
register_setting_set(name) { |value| class_variable_set(var_name, value) }
|
115
|
+
end
|
116
|
+
|
117
|
+
def register_setting_get(name, &block)
|
118
|
+
settings_map[name] ||= {}
|
119
|
+
settings_map[name][:getter] = block
|
120
|
+
end
|
121
|
+
|
122
|
+
def register_setting_set(name, &block)
|
123
|
+
settings_map[name] ||= {}
|
124
|
+
settings_map[name][:setter] = block
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
register_setting_var(:basename, false) # use basename in showing files?
|
129
|
+
register_setting_var(:callstyle, :last)
|
130
|
+
register_setting_var(:debuggertesting, false)
|
131
|
+
register_setting_var(:force_stepping, false)
|
132
|
+
register_setting_var(:full_path, true)
|
133
|
+
register_setting_var(:listsize, 10) # number of lines in list command
|
134
|
+
register_setting_var(:stack_trace_on_error, false)
|
135
|
+
register_setting_var(:tracing_plus, false) # different linetrace lines?
|
136
|
+
|
137
|
+
# width of line output. Use COLUMNS value if it exists and is
|
138
|
+
# not too rediculously large.
|
139
|
+
width = ENV['COLUMNS'].to_i
|
140
|
+
width = 80 unless width > 10
|
141
|
+
register_setting_var(:width, width)
|
142
|
+
|
143
|
+
if not defined? Debugger::ARGV
|
144
|
+
Debugger::ARGV = ARGV.clone
|
145
|
+
end
|
146
|
+
register_setting_var(:argv, Debugger::ARGV)
|
147
|
+
|
148
|
+
def initialize(state)
|
149
|
+
@state = state
|
150
|
+
end
|
151
|
+
|
152
|
+
def match(input)
|
153
|
+
@match = regexp.match(input)
|
154
|
+
end
|
155
|
+
|
156
|
+
protected
|
157
|
+
|
158
|
+
# FIXME: use delegate?
|
159
|
+
def errmsg(*args)
|
160
|
+
@state.errmsg(*args)
|
161
|
+
end
|
162
|
+
|
163
|
+
def print(*args)
|
164
|
+
@state.print(*args)
|
165
|
+
end
|
166
|
+
|
167
|
+
def confirm(msg)
|
168
|
+
@state.confirm(msg) == 'y'
|
169
|
+
end
|
170
|
+
|
171
|
+
def debug_eval(str, b = get_binding)
|
172
|
+
begin
|
173
|
+
val = eval(str, b)
|
174
|
+
rescue StandardError, ScriptError => e
|
175
|
+
if Command.settings[:stack_trace_on_error]
|
176
|
+
at = eval("caller(1)", b)
|
177
|
+
print "%s:%s\n", at.shift, e.to_s.sub(/\(eval\):1:(in `.*?':)?/, '')
|
178
|
+
for i in at
|
179
|
+
print "\tfrom %s\n", i
|
180
|
+
end
|
181
|
+
else
|
182
|
+
print "#{e.class} Exception: #{e.message}\n"
|
183
|
+
end
|
184
|
+
throw :debug_error
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
def debug_silent_eval(str)
|
189
|
+
begin
|
190
|
+
eval(str, get_binding)
|
191
|
+
rescue StandardError, ScriptError
|
192
|
+
nil
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
def get_binding
|
197
|
+
@state.context.frame_binding(@state.frame_pos)
|
198
|
+
end
|
199
|
+
|
200
|
+
def line_at(file, line)
|
201
|
+
Debugger.line_at(file, line)
|
202
|
+
end
|
203
|
+
|
204
|
+
def get_context(thnum)
|
205
|
+
Debugger.contexts.find{|c| c.thnum == thnum}
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
Command.load_commands
|
210
|
+
|
211
|
+
# Returns setting object.
|
212
|
+
# Use Debugger.settings[] and Debugger.settings[]= methods to query and set
|
213
|
+
# debugger settings. These settings are available:
|
214
|
+
#
|
215
|
+
# - :autolist - automatically calls 'list' command on breakpoint
|
216
|
+
# - :autoeval - evaluates input in the current binding if it's not recognized as a debugger command
|
217
|
+
# - :autoirb - automatically calls 'irb' command on breakpoint
|
218
|
+
# - :stack_trace_on_error - shows full stack trace if eval command results with an exception
|
219
|
+
# - :frame_full_path - displays full paths when showing frame stack
|
220
|
+
# - :frame_class_names - displays method's class name when showing frame stack
|
221
|
+
# - :reload_source_on_change - makes 'list' command to always display up-to-date source code
|
222
|
+
# - :force_stepping - stepping command asways move to the new line
|
223
|
+
#
|
224
|
+
def self.settings
|
225
|
+
Command.settings
|
226
|
+
end
|
227
|
+
end
|
@@ -0,0 +1,153 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
# Implements debugger "break" command.
|
4
|
+
class AddBreakpoint < Command
|
5
|
+
self.allow_in_control = true
|
6
|
+
|
7
|
+
def regexp
|
8
|
+
/ ^\s*
|
9
|
+
b(?:reak)?
|
10
|
+
(?: \s+ #{Position_regexp})? \s*
|
11
|
+
(?: \s+ (.*))? \s*
|
12
|
+
$
|
13
|
+
/x
|
14
|
+
end
|
15
|
+
|
16
|
+
def execute
|
17
|
+
if @match[1]
|
18
|
+
line, _, _, expr = @match.captures
|
19
|
+
else
|
20
|
+
_, file, line, expr = @match.captures
|
21
|
+
end
|
22
|
+
if expr
|
23
|
+
if expr !~ /^\s*if\s+(.+)/
|
24
|
+
if file or line
|
25
|
+
errmsg "Expecting 'if' in breakpoint condition; got: #{expr}.\n"
|
26
|
+
else
|
27
|
+
errmsg "Invalid breakpoint location: #{expr}.\n"
|
28
|
+
end
|
29
|
+
return
|
30
|
+
else
|
31
|
+
expr = $1
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
brkpt_filename = nil
|
36
|
+
if file.nil?
|
37
|
+
unless @state.context
|
38
|
+
errmsg "We are not in a state that has an associated file.\n"
|
39
|
+
return
|
40
|
+
end
|
41
|
+
brkpt_filename = @state.file
|
42
|
+
file = File.basename(@state.file)
|
43
|
+
if line.nil?
|
44
|
+
# Set breakpoint at current line
|
45
|
+
line = @state.line.to_s
|
46
|
+
end
|
47
|
+
elsif line !~ /^\d+$/
|
48
|
+
# See if "line" is a method/function name
|
49
|
+
klass = debug_silent_eval(file)
|
50
|
+
if klass && klass.kind_of?(Module)
|
51
|
+
class_name = klass.name if klass
|
52
|
+
else
|
53
|
+
errmsg "Unknown class #{file}.\n"
|
54
|
+
throw :debug_error
|
55
|
+
end
|
56
|
+
else
|
57
|
+
# FIXME: This should be done in LineCache.
|
58
|
+
file = File.expand_path(file) if file.index(File::SEPARATOR) || \
|
59
|
+
File::ALT_SEPARATOR && file.index(File::ALT_SEPARATOR)
|
60
|
+
brkpt_filename = file
|
61
|
+
end
|
62
|
+
|
63
|
+
if line =~ /^\d+$/
|
64
|
+
line = line.to_i
|
65
|
+
if LineCache.cache(brkpt_filename, Command.settings[:reload_source_on_change])
|
66
|
+
last_line = LineCache.size(brkpt_filename)
|
67
|
+
if line > last_line
|
68
|
+
errmsg("There are only %d lines in file \"%s\".\n", last_line, file)
|
69
|
+
return
|
70
|
+
end
|
71
|
+
unless LineCache.trace_line_numbers(brkpt_filename).member?(line)
|
72
|
+
errmsg("Line %d is not a stopping point in file \"%s\".\n", line, file)
|
73
|
+
return
|
74
|
+
end
|
75
|
+
else
|
76
|
+
errmsg("No source file named %s\n" % file)
|
77
|
+
return unless confirm("Set breakpoint anyway? (y/n) ")
|
78
|
+
end
|
79
|
+
|
80
|
+
unless @state.context
|
81
|
+
errmsg "We are not in a state we can add breakpoints.\n"
|
82
|
+
return
|
83
|
+
end
|
84
|
+
b = Debugger.add_breakpoint brkpt_filename, line, expr
|
85
|
+
print "Breakpoint %d file %s, line %s\n", b.id, brkpt_filename, line.to_s
|
86
|
+
unless syntax_valid?(expr)
|
87
|
+
errmsg("Expression \"#{expr}\" syntactically incorrect; breakpoint disabled.\n")
|
88
|
+
b.enabled = false
|
89
|
+
end
|
90
|
+
else
|
91
|
+
method = line.intern.id2name
|
92
|
+
b = Debugger.add_breakpoint class_name, method, expr
|
93
|
+
print "Breakpoint %d at %s::%s\n", b.id, class_name, method.to_s
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
class << self
|
98
|
+
def help_command
|
99
|
+
'break'
|
100
|
+
end
|
101
|
+
|
102
|
+
def help(cmd)
|
103
|
+
%{
|
104
|
+
b[reak] file:line [if expr]
|
105
|
+
b[reak] class(.|#)method [if expr]
|
106
|
+
\tset breakpoint to some position, (optionally) if expr == true
|
107
|
+
}
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
# Implements debugger "delete" command.
|
113
|
+
class DeleteBreakpointCommand < Command
|
114
|
+
self.allow_in_control = true
|
115
|
+
|
116
|
+
def regexp
|
117
|
+
/^\s *del(?:ete)? (?:\s+(.*))?$/ix
|
118
|
+
end
|
119
|
+
|
120
|
+
def execute
|
121
|
+
unless @state.context
|
122
|
+
errmsg "We are not in a state we can delete breakpoints.\n"
|
123
|
+
return
|
124
|
+
end
|
125
|
+
brkpts = @match[1]
|
126
|
+
unless brkpts
|
127
|
+
if confirm("Delete all breakpoints? (y or n) ")
|
128
|
+
Debugger.breakpoints.clear
|
129
|
+
end
|
130
|
+
else
|
131
|
+
brkpts.split(/[ \t]+/).each do |pos|
|
132
|
+
pos = get_int(pos, "Delete", 1)
|
133
|
+
return unless pos
|
134
|
+
unless Debugger.remove_breakpoint(pos)
|
135
|
+
errmsg "No breakpoint number %d\n", pos
|
136
|
+
end
|
137
|
+
end
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class << self
|
142
|
+
def help_command
|
143
|
+
'delete'
|
144
|
+
end
|
145
|
+
|
146
|
+
def help(cmd)
|
147
|
+
%{
|
148
|
+
del[ete][ nnn...]\tdelete some or all breakpoints
|
149
|
+
}
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|