ruby-debug-ide 0.1.2
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/README +0 -0
- data/bin/rdebug-ide +74 -0
- data/lib/ruby-debug.rb +128 -0
- data/lib/ruby-debug/command.rb +114 -0
- data/lib/ruby-debug/commands/breakpoints.rb +126 -0
- data/lib/ruby-debug/commands/catchpoint.rb +40 -0
- data/lib/ruby-debug/commands/control.rb +127 -0
- data/lib/ruby-debug/commands/eval.rb +64 -0
- data/lib/ruby-debug/commands/frame.rb +161 -0
- data/lib/ruby-debug/commands/inspect.rb +24 -0
- data/lib/ruby-debug/commands/load.rb +18 -0
- data/lib/ruby-debug/commands/stepping.rb +105 -0
- data/lib/ruby-debug/commands/threads.rb +153 -0
- data/lib/ruby-debug/commands/variables.rb +123 -0
- data/lib/ruby-debug/event_processor.rb +45 -0
- data/lib/ruby-debug/interface.rb +118 -0
- data/lib/ruby-debug/printers.rb +2 -0
- data/lib/ruby-debug/processor.rb +155 -0
- data/lib/ruby-debug/xml_printer.rb +237 -0
- metadata +74 -0
data/README
ADDED
File without changes
|
data/bin/rdebug-ide
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'optparse'
|
5
|
+
require "ostruct"
|
6
|
+
require 'ruby-debug'
|
7
|
+
|
8
|
+
$stdout.sync=true
|
9
|
+
|
10
|
+
options = OpenStruct.new(
|
11
|
+
'host' => nil,
|
12
|
+
'port' => 1234,
|
13
|
+
'tracing' => false,
|
14
|
+
'frame_bind' => false
|
15
|
+
)
|
16
|
+
|
17
|
+
opts = OptionParser.new do |opts|
|
18
|
+
opts.banner = <<EOB
|
19
|
+
Using ruby-debug-base #{Debugger::VERSION}
|
20
|
+
Usage: rdebug-javaide is supposed to be called from RDT or NetBeans. The command line interface to ruby-debug is rdebug.
|
21
|
+
EOB
|
22
|
+
opts.separator ""
|
23
|
+
opts.separator "Options:"
|
24
|
+
opts.on("-h", "--host HOST", "Host name used for remote debugging") {|options.host|}
|
25
|
+
opts.on("-p", "--port PORT", Integer, "Port used for remote debugging") {|options.port|}
|
26
|
+
opts.on("-x", "--trace", "turn on line tracing") {options.tracing = true}
|
27
|
+
opts.on("-d", "--debug", "Debug self - prints information for debugging ruby-debug itself") do
|
28
|
+
Debugger.is_debug = true
|
29
|
+
end
|
30
|
+
opts.on("-I", "--include PATH", String, "Add PATH to $LOAD_PATH") do |path|
|
31
|
+
$LOAD_PATH.unshift(path)
|
32
|
+
end
|
33
|
+
|
34
|
+
opts.on("--keep-frame-binding", "Keep frame bindings") {options.frame_bind = true}
|
35
|
+
opts.separator ""
|
36
|
+
opts.separator "Common options:"
|
37
|
+
opts.on_tail("-v", "--version", "Show version") do
|
38
|
+
puts "Using ruby-debug-base #{Debugger::VERSION}"
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
begin
|
44
|
+
Debugger::ARGV = ARGV.clone
|
45
|
+
rdebug_path = File.expand_path($0)
|
46
|
+
if RUBY_PLATFORM =~ /mswin/
|
47
|
+
rdebug_path += ".cmd" unless rdebug_path =~ /\.cmd$/i
|
48
|
+
end
|
49
|
+
Debugger::RDEBUG_SCRIPT = rdebug_path
|
50
|
+
opts.parse! ARGV
|
51
|
+
rescue StandardError => e
|
52
|
+
puts opts
|
53
|
+
puts
|
54
|
+
puts e.message
|
55
|
+
exit(1)
|
56
|
+
end
|
57
|
+
|
58
|
+
if ARGV.empty?
|
59
|
+
puts opts
|
60
|
+
puts
|
61
|
+
puts "Must specify a script to run"
|
62
|
+
exit(1)
|
63
|
+
end
|
64
|
+
|
65
|
+
# save script name
|
66
|
+
Debugger::PROG_SCRIPT = ARGV.shift
|
67
|
+
|
68
|
+
# install interruption handler
|
69
|
+
trap('INT') { Debugger.interrupt_last }
|
70
|
+
|
71
|
+
# set options
|
72
|
+
Debugger.keep_frame_binding = options.frame_bind
|
73
|
+
|
74
|
+
Debugger.main(options.host, options.port)
|
data/lib/ruby-debug.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'pp'
|
2
|
+
require 'stringio'
|
3
|
+
require "socket"
|
4
|
+
require 'thread'
|
5
|
+
require 'ruby_debug.so'
|
6
|
+
require 'ruby-debug/xml_printer'
|
7
|
+
require 'ruby-debug/processor'
|
8
|
+
require 'ruby-debug/event_processor'
|
9
|
+
|
10
|
+
module Debugger
|
11
|
+
|
12
|
+
class Context
|
13
|
+
def interrupt
|
14
|
+
self.stop_next = 1
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
|
19
|
+
def processor
|
20
|
+
Debugger.processor
|
21
|
+
end
|
22
|
+
|
23
|
+
def at_breakpoint(breakpoint)
|
24
|
+
processor.at_breakpoint(self, breakpoint)
|
25
|
+
end
|
26
|
+
|
27
|
+
def at_catchpoint(excpt)
|
28
|
+
processor.at_catchpoint(self, excpt)
|
29
|
+
end
|
30
|
+
|
31
|
+
def at_tracing(file, line)
|
32
|
+
processor.at_tracing(self, file, line)
|
33
|
+
end
|
34
|
+
|
35
|
+
def at_line(file, line)
|
36
|
+
processor.at_line(self, file, line)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
class << self
|
41
|
+
attr_accessor :processor, :is_debug
|
42
|
+
attr_reader :control_thread
|
43
|
+
|
44
|
+
#
|
45
|
+
# Interrupts the current thread
|
46
|
+
#
|
47
|
+
def interrupt
|
48
|
+
current_context.interrupt
|
49
|
+
end
|
50
|
+
|
51
|
+
#
|
52
|
+
# Interrupts the last debugged thread
|
53
|
+
#
|
54
|
+
def interrupt_last
|
55
|
+
skip do
|
56
|
+
if context = last_context
|
57
|
+
return nil unless context.thread.alive?
|
58
|
+
context.interrupt
|
59
|
+
end
|
60
|
+
context
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def main(host, port)
|
65
|
+
return if started?
|
66
|
+
|
67
|
+
start
|
68
|
+
|
69
|
+
start_control(host, port)
|
70
|
+
|
71
|
+
@mutex = Mutex.new
|
72
|
+
@proceed = ConditionVariable.new
|
73
|
+
|
74
|
+
# wait for start command
|
75
|
+
@mutex.synchronize do
|
76
|
+
@proceed.wait(@mutex)
|
77
|
+
end
|
78
|
+
|
79
|
+
debug_load Debugger::PROG_SCRIPT
|
80
|
+
end
|
81
|
+
|
82
|
+
def run_prog_script
|
83
|
+
@mutex.synchronize do
|
84
|
+
@proceed.signal
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def start_control(host, port)
|
89
|
+
raise "Debugger is not started" unless started?
|
90
|
+
return if @control_thread
|
91
|
+
@control_thread = DebugThread.new do
|
92
|
+
server = TCPServer.new(host, port)
|
93
|
+
while (session = server.accept)
|
94
|
+
begin
|
95
|
+
interface = RemoteInterface.new(session)
|
96
|
+
@processor = EventProcessor.new(interface)
|
97
|
+
processor = ControlCommandProcessor.new(interface)
|
98
|
+
processor.process_commands
|
99
|
+
rescue StandardError, ScriptError => ex
|
100
|
+
puts ex
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
|
108
|
+
class Exception # :nodoc:
|
109
|
+
attr_reader :__debug_file, :__debug_line, :__debug_binding, :__debug_context
|
110
|
+
end
|
111
|
+
|
112
|
+
module Kernel
|
113
|
+
#
|
114
|
+
# Stops the current thread after a number of _steps_ made.
|
115
|
+
#
|
116
|
+
def debugger(steps = 1)
|
117
|
+
Debugger.current_context.stop_next = steps
|
118
|
+
end
|
119
|
+
|
120
|
+
#
|
121
|
+
# Returns a binding of n-th call frame
|
122
|
+
#
|
123
|
+
def binding_n(n = 0)
|
124
|
+
Debugger.current_context.frame_binding[n+1]
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
module Debugger
|
2
|
+
class Command # :nodoc:
|
3
|
+
class << self
|
4
|
+
def commands
|
5
|
+
@commands ||= []
|
6
|
+
end
|
7
|
+
|
8
|
+
DEF_OPTIONS = {
|
9
|
+
:event => true,
|
10
|
+
:control => false,
|
11
|
+
:unknown => false,
|
12
|
+
:need_context => false,
|
13
|
+
}
|
14
|
+
|
15
|
+
def inherited(klass)
|
16
|
+
DEF_OPTIONS.each do |o, v|
|
17
|
+
klass.options[o] = v if klass.options[o].nil?
|
18
|
+
end
|
19
|
+
commands << klass
|
20
|
+
end
|
21
|
+
|
22
|
+
def load_commands
|
23
|
+
dir = File.dirname(__FILE__)
|
24
|
+
Dir[File.join(dir, 'commands', '*')].each do |file|
|
25
|
+
require file if file =~ /\.rb$/
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def method_missing(meth, *args, &block)
|
30
|
+
if meth.to_s =~ /^(.+?)=$/
|
31
|
+
@options[$1.intern] = args.first
|
32
|
+
else
|
33
|
+
if @options.has_key?(meth)
|
34
|
+
@options[meth]
|
35
|
+
else
|
36
|
+
super
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def options
|
42
|
+
@options ||= {}
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize(state, printer)
|
47
|
+
@state, @printer = state, printer
|
48
|
+
end
|
49
|
+
|
50
|
+
def match(input)
|
51
|
+
@match = regexp.match(input)
|
52
|
+
end
|
53
|
+
|
54
|
+
protected
|
55
|
+
|
56
|
+
def method_missing(meth, *args, &block)
|
57
|
+
if @printer.respond_to? meth
|
58
|
+
@printer.send meth, *args, &block
|
59
|
+
else
|
60
|
+
super
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def print(*args)
|
65
|
+
@state.print(*args)
|
66
|
+
end
|
67
|
+
|
68
|
+
def confirm(msg)
|
69
|
+
@state.confirm(msg) == 'y'
|
70
|
+
end
|
71
|
+
|
72
|
+
def debug_eval(str, b = get_binding)
|
73
|
+
begin
|
74
|
+
val = eval(str, b)
|
75
|
+
rescue StandardError, ScriptError => e
|
76
|
+
@printer.print_exception(e, @state.binding)
|
77
|
+
throw :debug_error
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def debug_silent_eval(str)
|
82
|
+
begin
|
83
|
+
eval(str, get_binding)
|
84
|
+
rescue StandardError, ScriptError
|
85
|
+
nil
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def hbinding(hash)
|
90
|
+
code = hash.keys.map{|k| "#{k} = hash['#{k}']"}.join(';') + ';binding'
|
91
|
+
if obj = @state.context.frame_self(@state.frame_pos)
|
92
|
+
obj.instance_eval code
|
93
|
+
else
|
94
|
+
eval code
|
95
|
+
end
|
96
|
+
end
|
97
|
+
private :hbinding
|
98
|
+
|
99
|
+
def get_binding
|
100
|
+
binding = @state.context.frame_binding(@state.frame_pos)
|
101
|
+
binding || hbinding(@state.context.frame_locals(@state.frame_pos))
|
102
|
+
end
|
103
|
+
|
104
|
+
def line_at(file, line)
|
105
|
+
Debugger.line_at(file, line)
|
106
|
+
end
|
107
|
+
|
108
|
+
def get_context(thnum)
|
109
|
+
Debugger.contexts.find{|c| c.thnum == thnum}
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
Command.load_commands
|
114
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
module Debugger
|
2
|
+
class AddBreakpoint < Command # :nodoc:
|
3
|
+
self.control = true
|
4
|
+
|
5
|
+
def regexp
|
6
|
+
/ ^\s*
|
7
|
+
b(?:reak)?
|
8
|
+
\s+
|
9
|
+
(?:
|
10
|
+
(\d+) |
|
11
|
+
(.+?)[:.#]([^.:\s]+)
|
12
|
+
)
|
13
|
+
(?:\s+
|
14
|
+
if\s+(.+)
|
15
|
+
)?
|
16
|
+
$
|
17
|
+
/x
|
18
|
+
end
|
19
|
+
|
20
|
+
def execute
|
21
|
+
if @match[1]
|
22
|
+
pos, _, _, expr = @match.captures
|
23
|
+
else
|
24
|
+
_, file, pos, expr = @match.captures
|
25
|
+
end
|
26
|
+
|
27
|
+
if file.nil?
|
28
|
+
file = File.basename(@state.file)
|
29
|
+
else
|
30
|
+
if pos !~ /^\d+$/
|
31
|
+
klass = debug_silent_eval(file)
|
32
|
+
if klass && !klass.kind_of?(Module)
|
33
|
+
print_error "Unknown class #{file}"
|
34
|
+
throw :debug_error
|
35
|
+
end
|
36
|
+
file = klass.name if klass
|
37
|
+
else
|
38
|
+
file = File.expand_path(file) if file.index(File::SEPARATOR) || \
|
39
|
+
File::ALT_SEPARATOR && file.index(File::ALT_SEPARATOR)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
if pos =~ /^\d+$/
|
44
|
+
pos = pos.to_i
|
45
|
+
else
|
46
|
+
pos = pos.intern.id2name
|
47
|
+
end
|
48
|
+
|
49
|
+
b = Debugger.add_breakpoint file, pos, expr
|
50
|
+
print_breakpoint_added b
|
51
|
+
end
|
52
|
+
|
53
|
+
class << self
|
54
|
+
def help_command
|
55
|
+
'break'
|
56
|
+
end
|
57
|
+
|
58
|
+
def help(cmd)
|
59
|
+
%{
|
60
|
+
b[reak] [file|class(:|.|#)]<line|method> [if expr] -
|
61
|
+
\tset breakpoint to some position, (optionally) if expr == true
|
62
|
+
}
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class BreakpointsCommand < Command # :nodoc:
|
68
|
+
self.control = true
|
69
|
+
|
70
|
+
def regexp
|
71
|
+
/^\s*b(?:reak)?$/
|
72
|
+
end
|
73
|
+
|
74
|
+
def execute
|
75
|
+
print_breakpoints Debugger.breakpoints
|
76
|
+
end
|
77
|
+
|
78
|
+
class << self
|
79
|
+
def help_command
|
80
|
+
'break'
|
81
|
+
end
|
82
|
+
|
83
|
+
def help(cmd)
|
84
|
+
%{
|
85
|
+
b[reak]\tlist breakpoints
|
86
|
+
}
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
class DeleteBreakpointCommand < Command # :nodoc:
|
92
|
+
self.control = true
|
93
|
+
|
94
|
+
def regexp
|
95
|
+
/^\s*del(?:ete)?(?:\s+(\d+))?$/
|
96
|
+
end
|
97
|
+
|
98
|
+
def execute
|
99
|
+
pos = @match[1]
|
100
|
+
unless pos
|
101
|
+
if confirm("Clear all breakpoints? (y/n) ")
|
102
|
+
Debugger.breakpoints.clear
|
103
|
+
end
|
104
|
+
else
|
105
|
+
pos = pos.to_i
|
106
|
+
if b = Debugger.remove_breakpoint(pos)
|
107
|
+
print_breakpoint_deleted b
|
108
|
+
else
|
109
|
+
print_error "Breakpoint %d is not defined", pos
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
class << self
|
115
|
+
def help_command
|
116
|
+
'delete'
|
117
|
+
end
|
118
|
+
|
119
|
+
def help(cmd)
|
120
|
+
%{
|
121
|
+
del[ete][ nnn]\tdelete some or all breakpoints
|
122
|
+
}
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Debugger
|
2
|
+
class CatchCommand < Command # :nodoc:
|
3
|
+
self.control = true
|
4
|
+
|
5
|
+
def regexp
|
6
|
+
/^\s*cat(?:ch)?(?:\s+(.+))?$/
|
7
|
+
end
|
8
|
+
|
9
|
+
def execute
|
10
|
+
if excn = @match[1]
|
11
|
+
if excn == 'off'
|
12
|
+
Debugger.catchpoint = nil
|
13
|
+
print_msg "Clear catchpoint."
|
14
|
+
else
|
15
|
+
Debugger.catchpoint = excn
|
16
|
+
print_msg "Set catchpoint %s.", excn
|
17
|
+
end
|
18
|
+
else
|
19
|
+
if Debugger.catchpoint
|
20
|
+
print_msg "Catchpoint %s.", Debugger.catchpoint
|
21
|
+
else
|
22
|
+
print_msg "No catchpoint."
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class << self
|
28
|
+
def help_command
|
29
|
+
'catch'
|
30
|
+
end
|
31
|
+
|
32
|
+
def help(cmd)
|
33
|
+
%{
|
34
|
+
cat[ch]\t\t\tshow catchpoint
|
35
|
+
cat[ch] <an Exception>\tset catchpoint to an exception
|
36
|
+
}
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|