ruby-debug-ide 0.1.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|