ruby-debug-ide22 0.7.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.
- checksums.yaml +7 -0
- data/CHANGES +75 -0
- data/ChangeLog.archive +1073 -0
- data/ChangeLog.md +594 -0
- data/Gemfile +38 -0
- data/MIT-LICENSE +24 -0
- data/Rakefile +93 -0
- data/bin/gdb_wrapper +96 -0
- data/bin/rdebug-ide +200 -0
- data/ext/mkrf_conf.rb +44 -0
- data/lib/ruby-debug-ide/attach/debugger_loader.rb +20 -0
- data/lib/ruby-debug-ide/attach/gdb.rb +73 -0
- data/lib/ruby-debug-ide/attach/lldb.rb +71 -0
- data/lib/ruby-debug-ide/attach/native_debugger.rb +133 -0
- data/lib/ruby-debug-ide/attach/process_thread.rb +54 -0
- data/lib/ruby-debug-ide/attach/util.rb +115 -0
- data/lib/ruby-debug-ide/command.rb +187 -0
- data/lib/ruby-debug-ide/commands/breakpoints.rb +128 -0
- data/lib/ruby-debug-ide/commands/catchpoint.rb +64 -0
- data/lib/ruby-debug-ide/commands/condition.rb +51 -0
- data/lib/ruby-debug-ide/commands/control.rb +158 -0
- data/lib/ruby-debug-ide/commands/enable.rb +203 -0
- data/lib/ruby-debug-ide/commands/eval.rb +64 -0
- data/lib/ruby-debug-ide/commands/expression_info.rb +71 -0
- data/lib/ruby-debug-ide/commands/file_filtering.rb +107 -0
- data/lib/ruby-debug-ide/commands/frame.rb +155 -0
- data/lib/ruby-debug-ide/commands/inspect.rb +25 -0
- data/lib/ruby-debug-ide/commands/jump.rb +73 -0
- data/lib/ruby-debug-ide/commands/load.rb +18 -0
- data/lib/ruby-debug-ide/commands/pause.rb +33 -0
- data/lib/ruby-debug-ide/commands/set_type.rb +47 -0
- data/lib/ruby-debug-ide/commands/stepping.rb +108 -0
- data/lib/ruby-debug-ide/commands/threads.rb +178 -0
- data/lib/ruby-debug-ide/commands/variables.rb +154 -0
- data/lib/ruby-debug-ide/event_processor.rb +71 -0
- data/lib/ruby-debug-ide/greeter.rb +42 -0
- data/lib/ruby-debug-ide/helper.rb +33 -0
- data/lib/ruby-debug-ide/ide_processor.rb +155 -0
- data/lib/ruby-debug-ide/interface.rb +45 -0
- data/lib/ruby-debug-ide/multiprocess/monkey.rb +47 -0
- data/lib/ruby-debug-ide/multiprocess/pre_child.rb +59 -0
- data/lib/ruby-debug-ide/multiprocess/starter.rb +11 -0
- data/lib/ruby-debug-ide/multiprocess/unmonkey.rb +31 -0
- data/lib/ruby-debug-ide/multiprocess.rb +23 -0
- data/lib/ruby-debug-ide/thread_alias.rb +27 -0
- data/lib/ruby-debug-ide/version.rb +3 -0
- data/lib/ruby-debug-ide/xml_printer.rb +571 -0
- data/lib/ruby-debug-ide.rb +228 -0
- data/ruby-debug-ide.gemspec +47 -0
- metadata +110 -0
@@ -0,0 +1,154 @@
|
|
1
|
+
module Debugger
|
2
|
+
class VarConstantCommand < Command # :nodoc:
|
3
|
+
def regexp
|
4
|
+
/^\s*v(?:ar)?\s+c(?:onst(?:ant)?)?\s+/
|
5
|
+
end
|
6
|
+
|
7
|
+
def execute
|
8
|
+
obj = debug_eval(@match.post_match)
|
9
|
+
unless obj.kind_of? Module
|
10
|
+
print_msg "Should be Class/Module: %s", @match.post_match
|
11
|
+
else
|
12
|
+
print_variables(obj.constants, "constant") do |var|
|
13
|
+
obj.const_get(var)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def help_command
|
20
|
+
'var'
|
21
|
+
end
|
22
|
+
|
23
|
+
def help(cmd)
|
24
|
+
%{
|
25
|
+
v[ar] c[onst] <object>\t\tshow constants of object
|
26
|
+
}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
class VarGlobalCommand < Command # :nodoc:
|
32
|
+
def regexp
|
33
|
+
/^\s*v(?:ar)?\s+g(?:lobal)?\s*$/
|
34
|
+
end
|
35
|
+
|
36
|
+
def execute
|
37
|
+
globals = []
|
38
|
+
if RUBY_VERSION < "1.9"
|
39
|
+
globals = global_variables - ['$=', '$IGNORECASE']
|
40
|
+
else
|
41
|
+
globals = global_variables - [:$KCODE, :$-K, :$=, :$IGNORECASE, :$FILENAME]
|
42
|
+
end
|
43
|
+
print_variables(globals, 'global') do |var|
|
44
|
+
debug_eval(var)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
class << self
|
49
|
+
def help_command
|
50
|
+
'var'
|
51
|
+
end
|
52
|
+
|
53
|
+
def help(cmd)
|
54
|
+
%{
|
55
|
+
v[ar] g[lobal]\t\t\tshow global variables
|
56
|
+
}
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
class VarInstanceCommand < Command # :nodoc:
|
62
|
+
# TODO: try to find out a way to use Kernel.binding for Rubinius
|
63
|
+
# ::Kernel.binding doesn't for for ruby 1.8 (see RUBY-14679)
|
64
|
+
BINDING_COMMAND = (defined?(Rubinius) || RUBY_VERSION < '1.9') ? 'binding' : '::Kernel.binding'
|
65
|
+
|
66
|
+
def regexp
|
67
|
+
# id will be read as first match, name as post match
|
68
|
+
/^\s*v(?:ar)?\s+i(?:nstance)?\s+((?:[\\+-]0x)[\dabcdef]+)?/
|
69
|
+
end
|
70
|
+
|
71
|
+
def execute
|
72
|
+
if (@match[1])
|
73
|
+
obj = ObjectSpace._id2ref(@match[1].hex) rescue nil
|
74
|
+
|
75
|
+
unless obj
|
76
|
+
print_element("variables")
|
77
|
+
@printer.print_msg("Unknown object id : %s", @match[1])
|
78
|
+
end
|
79
|
+
else
|
80
|
+
obj = debug_eval(@match.post_match)
|
81
|
+
end
|
82
|
+
return unless obj
|
83
|
+
if obj.is_a?(Array)
|
84
|
+
print_array(obj)
|
85
|
+
elsif obj.is_a?(Hash)
|
86
|
+
print_hash(obj)
|
87
|
+
elsif obj.is_a?(String)
|
88
|
+
print_string(obj)
|
89
|
+
else
|
90
|
+
print_element("variables") do
|
91
|
+
# instance variables
|
92
|
+
kind = 'instance'
|
93
|
+
inst_vars = obj.instance_variables
|
94
|
+
instance_binding = obj.instance_eval(BINDING_COMMAND)
|
95
|
+
# print self at top position
|
96
|
+
print_variable('self', debug_eval('self', instance_binding), kind) if inst_vars.include?('self')
|
97
|
+
inst_vars.sort.each do |var|
|
98
|
+
print_variable(var, debug_eval(var, instance_binding), kind) unless var == 'self'
|
99
|
+
end
|
100
|
+
|
101
|
+
# class variables
|
102
|
+
class_binding = obj.class.class_eval(BINDING_COMMAND)
|
103
|
+
obj.class.class_variables.sort.each do |var|
|
104
|
+
print_variable(var, debug_eval(var, class_binding), 'class')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
class << self
|
111
|
+
def help_command
|
112
|
+
'var'
|
113
|
+
end
|
114
|
+
|
115
|
+
def help(cmd)
|
116
|
+
%{
|
117
|
+
v[ar] i[nstance] <object>\tshow instance variables of object, object can be given by its id or an expression
|
118
|
+
}
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
class VarLocalCommand < Command # :nodoc:
|
124
|
+
def regexp
|
125
|
+
/^\s*v(?:ar)?\s+l(?:ocal)?\s*$/
|
126
|
+
end
|
127
|
+
|
128
|
+
def execute
|
129
|
+
locals = @state.context.frame_locals(@state.frame_pos)
|
130
|
+
_self = @state.context.frame_self(@state.frame_pos)
|
131
|
+
begin
|
132
|
+
locals['self'] = _self unless TOPLEVEL_BINDING.eval('self') == _self
|
133
|
+
rescue => ex
|
134
|
+
locals['self'] = "<Cannot evaluate self>"
|
135
|
+
$stderr << "Cannot evaluate self\n#{ex.class.name}: #{ex.message}\n #{ex.backtrace.join("\n ")}"
|
136
|
+
end
|
137
|
+
print_variables(locals.keys, 'local') do |var|
|
138
|
+
locals[var]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
class << self
|
143
|
+
def help_command
|
144
|
+
'var'
|
145
|
+
end
|
146
|
+
|
147
|
+
def help(cmd)
|
148
|
+
%{
|
149
|
+
v[ar] l[ocal]\t\t\tshow local variables
|
150
|
+
}
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ruby-debug-ide/xml_printer'
|
2
|
+
|
3
|
+
module Debugger
|
4
|
+
|
5
|
+
class EventProcessor
|
6
|
+
|
7
|
+
attr_accessor :line, :file, :context
|
8
|
+
|
9
|
+
def initialize(interface)
|
10
|
+
@printer = XmlPrinter.new(interface)
|
11
|
+
@interface = interface
|
12
|
+
@line = nil
|
13
|
+
@file = nil
|
14
|
+
@last_breakpoint = nil
|
15
|
+
end
|
16
|
+
|
17
|
+
def at_breakpoint(context, breakpoint)
|
18
|
+
raise "@last_breakpoint supposed to be nil. is #{@last_breakpoint}" if @last_breakpoint
|
19
|
+
# at_breakpoint is immediately followed by #at_line event in
|
20
|
+
# ruby-debug-base. So postpone breakpoint printing until #at_line.
|
21
|
+
@last_breakpoint = breakpoint
|
22
|
+
end
|
23
|
+
|
24
|
+
def at_catchpoint(context, excpt)
|
25
|
+
@printer.print_catchpoint(excpt)
|
26
|
+
end
|
27
|
+
|
28
|
+
def at_tracing(context, file, line)
|
29
|
+
@printer.print_trace(context, file, line)
|
30
|
+
end
|
31
|
+
|
32
|
+
def at_line(context, file, line)
|
33
|
+
@printer.print_at_line(context, file, line) if context.nil? || context.stop_reason == :step
|
34
|
+
line_event(context, file, line)
|
35
|
+
end
|
36
|
+
|
37
|
+
def at_return(context, file, line)
|
38
|
+
@printer.print_at_line(context, file, line)
|
39
|
+
context.stop_frame = -1
|
40
|
+
line_event(context, file, line)
|
41
|
+
end
|
42
|
+
|
43
|
+
def line_event(context, file, line)
|
44
|
+
@line = line
|
45
|
+
@file = file
|
46
|
+
@context = context
|
47
|
+
if @last_breakpoint
|
48
|
+
# followed after #at_breakpoint in the same thread. Print breakpoint
|
49
|
+
# now when @line, @file and @context are correctly set to prevent race
|
50
|
+
# condition with `control thread'.
|
51
|
+
n = Debugger.breakpoints.index(@last_breakpoint) + 1
|
52
|
+
@printer.print_breakpoint n, @last_breakpoint
|
53
|
+
@last_breakpoint = nil
|
54
|
+
end
|
55
|
+
raise "DebuggerThread are not supposed to be traced (#{context.thread})" if context.thread.is_a?(Debugger::DebugThread)
|
56
|
+
@printer.print_debug("Stopping Thread %s (%s)", context.thread.to_s, Process.pid.to_s)
|
57
|
+
@printer.print_debug("Threads equal: %s", Thread.current == context.thread)
|
58
|
+
IdeCommandProcessor.new(@interface).process_commands
|
59
|
+
InspectCommand.clear_references
|
60
|
+
@printer.print_debug("Resumed Thread %s", context.thread.to_s)
|
61
|
+
@line = nil
|
62
|
+
@file = nil
|
63
|
+
@context = nil
|
64
|
+
end
|
65
|
+
|
66
|
+
def at_line?
|
67
|
+
@line
|
68
|
+
end
|
69
|
+
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
if RUBY_VERSION < '2.0' || defined?(JRUBY_VERSION)
|
2
|
+
require 'ruby-debug-base'
|
3
|
+
else
|
4
|
+
require 'debase'
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'ruby-debug-ide/version'
|
8
|
+
require 'ruby-debug-ide/ide_processor'
|
9
|
+
|
10
|
+
module Debugger
|
11
|
+
|
12
|
+
class << self
|
13
|
+
def print_greeting_msg(stream, host, port, debugger_name = "Fast", socket_path = nil)
|
14
|
+
base_gem_name = if defined?(JRUBY_VERSION) || RUBY_VERSION < '1.9.0'
|
15
|
+
'ruby-debug-base'
|
16
|
+
elsif RUBY_VERSION < '2.0.0'
|
17
|
+
'ruby-debug-base19x'
|
18
|
+
else
|
19
|
+
'debase'
|
20
|
+
end
|
21
|
+
|
22
|
+
file_filtering_support = if Command.file_filter_supported?
|
23
|
+
'supported'
|
24
|
+
else
|
25
|
+
'not supported'
|
26
|
+
end
|
27
|
+
|
28
|
+
if host && port
|
29
|
+
listens_on = " listens on #{host}:#{port}\n"
|
30
|
+
elsif socket_path
|
31
|
+
listens_on = " listens on #{socket_path}\n"
|
32
|
+
else
|
33
|
+
listens_on = "\n"
|
34
|
+
end
|
35
|
+
|
36
|
+
msg = "#{debugger_name} Debugger (ruby-debug-ide #{IDE_VERSION}, #{base_gem_name} #{VERSION}, file filtering is #{file_filtering_support})" + listens_on
|
37
|
+
|
38
|
+
stream.printf msg
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Debugger
|
2
|
+
|
3
|
+
module ParseFunctions
|
4
|
+
# Parse 'str' of command 'cmd' as an integer between
|
5
|
+
# min and max. If either min or max is nil, that
|
6
|
+
# value has no bound.
|
7
|
+
def get_int(str, cmd, min=nil, max=nil, default=1)
|
8
|
+
return default unless str
|
9
|
+
begin
|
10
|
+
int = Integer(str)
|
11
|
+
if min and int < min
|
12
|
+
print_error "%s argument '%s' needs to at least %s.\n" % [cmd, str, min]
|
13
|
+
return nil
|
14
|
+
elsif max and int > max
|
15
|
+
print_error "%s argument '%s' needs to at most %s.\n" % [cmd, str, max]
|
16
|
+
return nil
|
17
|
+
end
|
18
|
+
return int
|
19
|
+
rescue
|
20
|
+
print_error "%s argument '%s' needs to be a number.\n" % [cmd, str]
|
21
|
+
return nil
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
# Return true if code is syntactically correct for Ruby.
|
26
|
+
def syntax_valid?(code)
|
27
|
+
eval("BEGIN {return true}\n#{code}", nil, "", 0)
|
28
|
+
rescue Exception
|
29
|
+
false
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
require 'ruby-debug-ide/interface'
|
2
|
+
require 'ruby-debug-ide/command'
|
3
|
+
|
4
|
+
module Debugger
|
5
|
+
class IdeCommandProcessor
|
6
|
+
def initialize(interface = nil)
|
7
|
+
@interface = interface
|
8
|
+
@printer = XmlPrinter.new(@interface)
|
9
|
+
end
|
10
|
+
|
11
|
+
def print(*args)
|
12
|
+
@interface.print(*args)
|
13
|
+
end
|
14
|
+
|
15
|
+
def process_commands
|
16
|
+
unless Debugger.handler.at_line?
|
17
|
+
@printer.print_error "There is no thread suspended at the time and therefore no context to execute '#{input.gsub('%', '%%')}'"
|
18
|
+
return
|
19
|
+
end
|
20
|
+
context = Debugger.handler.context
|
21
|
+
file = Debugger.handler.file
|
22
|
+
line = Debugger.handler.line
|
23
|
+
state = State.new do |s|
|
24
|
+
s.context = context
|
25
|
+
s.file = file
|
26
|
+
s.line = line
|
27
|
+
s.binding = context.frame_binding(0)
|
28
|
+
s.interface = @interface
|
29
|
+
end
|
30
|
+
event_cmds = Command.commands.map{|cmd| cmd.new(state, @printer) }
|
31
|
+
until state.proceed? do
|
32
|
+
input = @interface.command_queue.pop
|
33
|
+
catch(:debug_error) do
|
34
|
+
splitter[input].each do |input|
|
35
|
+
# escape % since print_debug might use printf
|
36
|
+
@printer.print_debug "Processing in context: #{input.gsub('%', '%%')}"
|
37
|
+
if (cmd = event_cmds.find { |c| c.match(input) })
|
38
|
+
if context.dead? && cmd.class.need_context
|
39
|
+
@printer.print_msg "Command is unavailable\n"
|
40
|
+
else
|
41
|
+
cmd.execute
|
42
|
+
end
|
43
|
+
else
|
44
|
+
@printer.print_msg "Unknown command: #{input}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
state.restore_context
|
49
|
+
end
|
50
|
+
rescue ::Exception
|
51
|
+
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
52
|
+
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
53
|
+
end
|
54
|
+
|
55
|
+
def splitter
|
56
|
+
lambda do |str|
|
57
|
+
str.split(/;/).inject([]) do |m, v|
|
58
|
+
if m.empty?
|
59
|
+
m << v
|
60
|
+
else
|
61
|
+
if m.last[-1] == ?\\
|
62
|
+
m.last[-1,1] = ''
|
63
|
+
m.last << ';' << v
|
64
|
+
else
|
65
|
+
m << v
|
66
|
+
end
|
67
|
+
end
|
68
|
+
m
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class IdeControlCommandProcessor < IdeCommandProcessor# :nodoc:
|
75
|
+
def process_commands
|
76
|
+
@printer.print_debug("Starting control thread")
|
77
|
+
ctrl_cmd_classes = Command.commands.select{|cmd| cmd.control}
|
78
|
+
state = ControlState.new(@interface)
|
79
|
+
ctrl_cmds = ctrl_cmd_classes.map{|cmd| cmd.new(state, @printer)}
|
80
|
+
while input = @interface.read_command
|
81
|
+
# escape % since print_debug might use printf
|
82
|
+
# sleep 0.3
|
83
|
+
catch(:debug_error) do
|
84
|
+
if cmd = ctrl_cmds.find{|c| c.match(input) }
|
85
|
+
@printer.print_debug "Processing in control: #{input.gsub('%', '%%')}"
|
86
|
+
cmd.execute
|
87
|
+
else
|
88
|
+
@interface.command_queue << input
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
rescue ::Exception
|
93
|
+
@printer.print_debug "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
94
|
+
@printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
|
95
|
+
@printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
|
96
|
+
ensure
|
97
|
+
@interface.close
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
class State # :nodoc:
|
102
|
+
|
103
|
+
attr_accessor :context, :original_context
|
104
|
+
attr_accessor :file, :line, :binding
|
105
|
+
attr_accessor :frame_pos, :previous_line
|
106
|
+
attr_accessor :interface
|
107
|
+
|
108
|
+
def initialize
|
109
|
+
@frame_pos = 0
|
110
|
+
@previous_line = nil
|
111
|
+
@proceed = false
|
112
|
+
yield self
|
113
|
+
@original_context = context
|
114
|
+
end
|
115
|
+
|
116
|
+
def print(*args)
|
117
|
+
@interface.print(*args)
|
118
|
+
end
|
119
|
+
|
120
|
+
def proceed?
|
121
|
+
@proceed
|
122
|
+
end
|
123
|
+
|
124
|
+
def proceed
|
125
|
+
@proceed = true
|
126
|
+
end
|
127
|
+
|
128
|
+
def restore_context
|
129
|
+
@context = @original_context
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
class ControlState # :nodoc:
|
134
|
+
|
135
|
+
def initialize(interface)
|
136
|
+
@interface = interface
|
137
|
+
end
|
138
|
+
|
139
|
+
def proceed
|
140
|
+
end
|
141
|
+
|
142
|
+
def print(*args)
|
143
|
+
@interface.print(*args)
|
144
|
+
end
|
145
|
+
|
146
|
+
def context
|
147
|
+
nil
|
148
|
+
end
|
149
|
+
|
150
|
+
def file
|
151
|
+
print "ERROR: No filename given.\n"
|
152
|
+
throw :debug_error
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'thread'
|
2
|
+
|
3
|
+
module Debugger
|
4
|
+
class Interface
|
5
|
+
end
|
6
|
+
|
7
|
+
class LocalInterface < Interface
|
8
|
+
end
|
9
|
+
|
10
|
+
|
11
|
+
class RemoteInterface < Interface # :nodoc:
|
12
|
+
attr_accessor :command_queue
|
13
|
+
|
14
|
+
def initialize(socket)
|
15
|
+
@socket = socket
|
16
|
+
@command_queue = Queue.new
|
17
|
+
end
|
18
|
+
|
19
|
+
def read_command
|
20
|
+
result = non_blocking_gets
|
21
|
+
raise IOError unless result
|
22
|
+
result.chomp
|
23
|
+
end
|
24
|
+
|
25
|
+
def print(*args)
|
26
|
+
@socket.printf(*args)
|
27
|
+
end
|
28
|
+
|
29
|
+
def close
|
30
|
+
@socket.close
|
31
|
+
rescue IOError, SystemCallError
|
32
|
+
end
|
33
|
+
|
34
|
+
# Workaround for JRuby issue http://jira.codehaus.org/browse/JRUBY-2063
|
35
|
+
def non_blocking_gets
|
36
|
+
loop do
|
37
|
+
result, _, _ = IO.select( [@socket], nil, nil, 0.2 )
|
38
|
+
next unless result
|
39
|
+
return result[0].gets
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module Debugger
|
2
|
+
module MultiProcess
|
3
|
+
def self.create_mp_fork(private=false)
|
4
|
+
%Q{
|
5
|
+
alias pre_debugger_fork fork
|
6
|
+
|
7
|
+
#{private ? "private" : ""}
|
8
|
+
def fork(*args)
|
9
|
+
if block_given?
|
10
|
+
return pre_debugger_fork{Debugger::MultiProcess::pre_child; yield}
|
11
|
+
end
|
12
|
+
result = pre_debugger_fork
|
13
|
+
Debugger::MultiProcess::pre_child unless result
|
14
|
+
result
|
15
|
+
end
|
16
|
+
}
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.create_mp_exec(private=false)
|
20
|
+
%Q{
|
21
|
+
alias pre_debugger_exec exec
|
22
|
+
|
23
|
+
#{private ? "private" : ""}
|
24
|
+
def exec(*args)
|
25
|
+
Debugger.interface.close
|
26
|
+
pre_debugger_exec(*args)
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
module Kernel
|
34
|
+
class << self
|
35
|
+
module_eval Debugger::MultiProcess.create_mp_fork
|
36
|
+
module_eval Debugger::MultiProcess.create_mp_exec
|
37
|
+
end
|
38
|
+
module_eval Debugger::MultiProcess.create_mp_fork(true)
|
39
|
+
module_eval Debugger::MultiProcess.create_mp_exec(true)
|
40
|
+
end
|
41
|
+
|
42
|
+
module Process
|
43
|
+
class << self
|
44
|
+
module_eval Debugger::MultiProcess.create_mp_fork
|
45
|
+
module_eval Debugger::MultiProcess.create_mp_exec
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Debugger
|
2
|
+
module MultiProcess
|
3
|
+
class << self
|
4
|
+
def pre_child(options = nil)
|
5
|
+
require 'socket'
|
6
|
+
require 'ostruct'
|
7
|
+
|
8
|
+
host = ENV['DEBUGGER_HOST']
|
9
|
+
|
10
|
+
options ||= OpenStruct.new(
|
11
|
+
'frame_bind' => false,
|
12
|
+
'host' => host,
|
13
|
+
'load_mode' => false,
|
14
|
+
'port' => Debugger.find_free_port(host),
|
15
|
+
'stop' => false,
|
16
|
+
'tracing' => false,
|
17
|
+
'int_handler' => true,
|
18
|
+
'cli_debug' => (ENV['DEBUGGER_CLI_DEBUG'] == 'true'),
|
19
|
+
'notify_dispatcher' => true,
|
20
|
+
'evaluation_timeout' => 10,
|
21
|
+
'trace_to_s' => false,
|
22
|
+
'debugger_memory_limit' => 10,
|
23
|
+
'inspect_time_limit' => 100
|
24
|
+
)
|
25
|
+
|
26
|
+
if(options.ignore_port)
|
27
|
+
options.port = Debugger.find_free_port(options.host)
|
28
|
+
options.notify_dispatcher = true
|
29
|
+
end
|
30
|
+
|
31
|
+
start_debugger(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def start_debugger(options)
|
35
|
+
if Debugger.started?
|
36
|
+
# we're in forked child, only need to restart control thread
|
37
|
+
Debugger.breakpoints.clear
|
38
|
+
Debugger.control_thread = nil
|
39
|
+
Debugger.start_control(options.host, options.port, options.notify_dispatcher)
|
40
|
+
end
|
41
|
+
|
42
|
+
if options.int_handler
|
43
|
+
# install interruption handler
|
44
|
+
trap('INT') { Debugger.interrupt_last }
|
45
|
+
end
|
46
|
+
|
47
|
+
# set options
|
48
|
+
Debugger.keep_frame_binding = options.frame_bind
|
49
|
+
Debugger.tracing = options.tracing
|
50
|
+
Debugger.evaluation_timeout = options.evaluation_timeout
|
51
|
+
Debugger.trace_to_s = options.trace_to_s
|
52
|
+
Debugger.debugger_memory_limit = options.debugger_memory_limit
|
53
|
+
Debugger.inspect_time_limit = options.inspect_time_limit
|
54
|
+
Debugger.cli_debug = options.cli_debug
|
55
|
+
Debugger.prepare_debugger(options)
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
if ENV['IDE_PROCESS_DISPATCHER']
|
2
|
+
require 'rubygems'
|
3
|
+
ENV['DEBUGGER_STORED_RUBYLIB'].split(File::PATH_SEPARATOR).each do |path|
|
4
|
+
next unless path =~ /ruby-debug-ide|ruby-debug-base|linecache|debase/
|
5
|
+
$LOAD_PATH << path
|
6
|
+
end
|
7
|
+
require 'ruby-debug-ide'
|
8
|
+
require 'ruby-debug-ide/multiprocess'
|
9
|
+
Debugger::MultiProcess::do_monkey
|
10
|
+
Debugger::MultiProcess::pre_child
|
11
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
module Debugger
|
2
|
+
module MultiProcess
|
3
|
+
def self.restore_fork
|
4
|
+
%Q{
|
5
|
+
alias fork pre_debugger_fork
|
6
|
+
}
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.restore_exec
|
10
|
+
%Q{
|
11
|
+
alias exec pre_debugger_exec
|
12
|
+
}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
module Kernel
|
18
|
+
class << self
|
19
|
+
module_eval Debugger::MultiProcess.restore_fork
|
20
|
+
module_eval Debugger::MultiProcess.restore_exec
|
21
|
+
end
|
22
|
+
module_eval Debugger::MultiProcess.restore_fork
|
23
|
+
module_eval Debugger::MultiProcess.restore_exec
|
24
|
+
end
|
25
|
+
|
26
|
+
module Process
|
27
|
+
class << self
|
28
|
+
module_eval Debugger::MultiProcess.restore_fork
|
29
|
+
module_eval Debugger::MultiProcess.restore_exec
|
30
|
+
end
|
31
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
if RUBY_VERSION < '1.9'
|
2
|
+
require 'ruby-debug-ide/multiprocess/pre_child'
|
3
|
+
else
|
4
|
+
require_relative 'multiprocess/pre_child'
|
5
|
+
end
|
6
|
+
|
7
|
+
module Debugger
|
8
|
+
module MultiProcess
|
9
|
+
class << self
|
10
|
+
def do_monkey
|
11
|
+
load File.expand_path(File.dirname(__FILE__) + '/multiprocess/monkey.rb')
|
12
|
+
end
|
13
|
+
|
14
|
+
def undo_monkey
|
15
|
+
if ENV['IDE_PROCESS_DISPATCHER']
|
16
|
+
load File.expand_path(File.dirname(__FILE__) + '/multiprocess/unmonkey.rb')
|
17
|
+
ruby_opts = ENV['RUBYOPT'].split(' ')
|
18
|
+
ENV['RUBYOPT'] = ruby_opts.keep_if {|opt| !opt.end_with?('ruby-debug-ide/multiprocess/starter')}.join(' ')
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Debugger
|
2
|
+
module TimeoutHandler
|
3
|
+
class << self
|
4
|
+
def do_thread_alias
|
5
|
+
if defined? ::OldThread
|
6
|
+
Debugger.print_debug 'Tried to re-alias thread for eval'
|
7
|
+
return
|
8
|
+
end
|
9
|
+
|
10
|
+
Object.const_set :OldThread, ::Thread
|
11
|
+
Object.__send__ :remove_const, :Thread
|
12
|
+
Object.const_set :Thread, ::Debugger::DebugThread
|
13
|
+
end
|
14
|
+
|
15
|
+
def undo_thread_alias
|
16
|
+
unless defined? ::OldThread
|
17
|
+
Debugger.print_debug 'Tried to de-alias thread twice'
|
18
|
+
return
|
19
|
+
end
|
20
|
+
|
21
|
+
Object.__send__ :remove_const, :Thread
|
22
|
+
Object.const_set :Thread, ::OldThread
|
23
|
+
Object.__send__ :remove_const, :OldThread
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|