ruby-debug-ide19 0.4.10

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.
Files changed (39) hide show
  1. data/CHANGES +75 -0
  2. data/ChangeLog +465 -0
  3. data/ChangeLog.archive +1073 -0
  4. data/MIT-LICENSE +24 -0
  5. data/Rakefile +110 -0
  6. data/bin/rdebug-ide +88 -0
  7. data/ext/mkrf_conf.rb +28 -0
  8. data/lib/ruby-debug-ide.rb +172 -0
  9. data/lib/ruby-debug/command.rb +169 -0
  10. data/lib/ruby-debug/commands/breakpoints.rb +129 -0
  11. data/lib/ruby-debug/commands/catchpoint.rb +52 -0
  12. data/lib/ruby-debug/commands/condition.rb +51 -0
  13. data/lib/ruby-debug/commands/control.rb +129 -0
  14. data/lib/ruby-debug/commands/enable.rb +203 -0
  15. data/lib/ruby-debug/commands/eval.rb +64 -0
  16. data/lib/ruby-debug/commands/frame.rb +155 -0
  17. data/lib/ruby-debug/commands/inspect.rb +24 -0
  18. data/lib/ruby-debug/commands/jump.rb +73 -0
  19. data/lib/ruby-debug/commands/load.rb +18 -0
  20. data/lib/ruby-debug/commands/stepping.rb +108 -0
  21. data/lib/ruby-debug/commands/threads.rb +153 -0
  22. data/lib/ruby-debug/commands/variables.rb +136 -0
  23. data/lib/ruby-debug/event_processor.rb +74 -0
  24. data/lib/ruby-debug/helper.rb +33 -0
  25. data/lib/ruby-debug/interface.rb +39 -0
  26. data/lib/ruby-debug/printers.rb +2 -0
  27. data/lib/ruby-debug/processor.rb +152 -0
  28. data/lib/ruby-debug/xml_printer.rb +268 -0
  29. data/test/rd_basic_test.rb +10 -0
  30. data/test/rd_catchpoint_test.rb +20 -0
  31. data/test/rd_condition_test.rb +11 -0
  32. data/test/rd_enable_disable_test.rb +43 -0
  33. data/test/rd_inspect_test.rb +11 -0
  34. data/test/rd_stepping_breakpoints_test.rb +36 -0
  35. data/test/rd_test_base.rb +44 -0
  36. data/test/rd_threads_and_frames_test.rb +11 -0
  37. data/test/rd_variables_test.rb +11 -0
  38. data/test/ruby-debug/xml_printer_test.rb +105 -0
  39. metadata +103 -0
@@ -0,0 +1,153 @@
1
+ module Debugger
2
+ class ThreadListCommand < Command # :nodoc:
3
+ self.control = true
4
+ def regexp
5
+ /^\s*th(?:read)?\s+l(?:ist)?\s*$/
6
+ end
7
+
8
+ def execute
9
+ contexts = Debugger.contexts.sort_by{|c| c.thnum}
10
+ print_contexts(contexts)
11
+ end
12
+
13
+ class << self
14
+ def help_command
15
+ 'thread'
16
+ end
17
+
18
+ def help(cmd)
19
+ %{
20
+ th[read] l[ist]\t\t\tlist all threads
21
+ }
22
+ end
23
+ end
24
+ end
25
+
26
+ class ThreadSwitchCommand < Command # :nodoc:
27
+ self.control = true
28
+ self.need_context = true
29
+
30
+ def regexp
31
+ /^\s*th(?:read)?\s+(?:sw(?:itch)?\s+)?(\d+)\s*$/
32
+ end
33
+
34
+ def execute
35
+ c = get_context(@match[1].to_i)
36
+ case
37
+ when c == @state.context
38
+ print_msg "It's the current thread."
39
+ when c.ignored?
40
+ print_msg "Can't switch to the debugger thread."
41
+ else
42
+ print_context(c)
43
+ c.stop_next = 1
44
+ c.thread.run
45
+ @state.proceed
46
+ end
47
+ end
48
+
49
+ class << self
50
+ def help_command
51
+ 'thread'
52
+ end
53
+
54
+ def help(cmd)
55
+ %{
56
+ th[read] [sw[itch]] <nnn>\tswitch thread context to nnn
57
+ }
58
+ end
59
+ end
60
+ end
61
+
62
+ class ThreadStopCommand < Command # :nodoc:
63
+ self.control = true
64
+ self.need_context = true
65
+
66
+ def regexp
67
+ /^\s*th(?:read)?\s+stop\s+(\d+)\s*$/
68
+ end
69
+
70
+ def execute
71
+ c = get_context(@match[1].to_i)
72
+ case
73
+ when c == @state.context
74
+ print_msg "It's the current thread."
75
+ when c.ignored?
76
+ print_msg "Can't stop the debugger thread."
77
+ else
78
+ c.suspend
79
+ print_context(c)
80
+ end
81
+ end
82
+
83
+ class << self
84
+ def help_command
85
+ 'thread'
86
+ end
87
+
88
+ def help(cmd)
89
+ %{
90
+ th[read] stop <nnn>\t\tstop thread nnn
91
+ }
92
+ end
93
+ end
94
+ end
95
+
96
+ class ThreadCurrentCommand < Command # :nodoc:
97
+ self.need_context = true
98
+
99
+ def regexp
100
+ /^\s*th(?:read)?\s+c(?:ur(?:rent)?)?\s*$/
101
+ end
102
+
103
+ def execute
104
+ print_context(@state.context)
105
+ end
106
+
107
+ class << self
108
+ def help_command
109
+ 'thread'
110
+ end
111
+
112
+ def help(cmd)
113
+ %{
114
+ th[read] c[ur[rent]]\t\tshow current thread
115
+ }
116
+ end
117
+ end
118
+ end
119
+
120
+ class ThreadResumeCommand < Command # :nodoc:
121
+ self.control = true
122
+ self.need_context = true
123
+
124
+ def regexp
125
+ /^\s*th(?:read)?\s+resume\s+(\d+)\s*$/
126
+ end
127
+
128
+ def execute
129
+ c = get_context(@match[1].to_i)
130
+ case
131
+ when c == @state.context
132
+ print_msg "It's the current thread."
133
+ when c.ignored?
134
+ print_msg "Can't resume the debugger thread."
135
+ else
136
+ c.resume
137
+ print_context(c)
138
+ end
139
+ end
140
+
141
+ class << self
142
+ def help_command
143
+ 'thread'
144
+ end
145
+
146
+ def help(cmd)
147
+ %{
148
+ th[read] resume <nnn>\t\tresume thread nnn
149
+ }
150
+ end
151
+ end
152
+ end
153
+ end
@@ -0,0 +1,136 @@
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
+ print_variables(global_variables, 'global') do |var|
38
+ debug_eval(var)
39
+ end
40
+ end
41
+
42
+ class << self
43
+ def help_command
44
+ 'var'
45
+ end
46
+
47
+ def help(cmd)
48
+ %{
49
+ v[ar] g[lobal]\t\t\tshow global variables
50
+ }
51
+ end
52
+ end
53
+ end
54
+
55
+ class VarInstanceCommand < Command # :nodoc:
56
+ def regexp
57
+ # id will be read as first match, name as post match
58
+ /^\s*v(?:ar)?\s+i(?:nstance)?\s+((?:[\\+-]0x)[\dabcdef]+)?/
59
+ end
60
+
61
+ def execute
62
+ if (@match[1])
63
+ obj = ObjectSpace._id2ref(@match[1].hex) rescue nil
64
+ unless obj
65
+ # TODO: ensure that empty variables frame will be printed
66
+ @printer.print_msg("Unknown object id : %s", @match[1])
67
+ end
68
+ else
69
+ obj = debug_eval(@match.post_match)
70
+ end
71
+ return unless obj
72
+ if (obj.is_a?(Array)) then
73
+ print_array(obj)
74
+ elsif (obj.is_a?(Hash)) then
75
+ print_hash(obj)
76
+ else
77
+ print_element("variables") do
78
+ # instance variables
79
+ kind = 'instance'
80
+ inst_vars = obj.instance_variables
81
+ instance_binding = obj.instance_eval{binding()}
82
+ # print self at top position
83
+ print_variable('self', debug_eval('self', instance_binding), kind) if inst_vars.include?('self')
84
+ inst_vars.sort.each do |var|
85
+ print_variable(var, debug_eval(var, instance_binding), kind) unless var == 'self'
86
+ end
87
+
88
+ # class variables
89
+ class_binding = obj.class.class_eval('binding()')
90
+ obj.class.class_variables.sort.each do |var|
91
+ print_variable(var, debug_eval(var, class_binding), 'class')
92
+ end
93
+ end
94
+ end
95
+ end
96
+
97
+ class << self
98
+ def help_command
99
+ 'var'
100
+ end
101
+
102
+ def help(cmd)
103
+ %{
104
+ v[ar] i[nstance] <object>\tshow instance variables of object, object can be given by its id or an expression
105
+ }
106
+ end
107
+ end
108
+ end
109
+
110
+ class VarLocalCommand < Command # :nodoc:
111
+ def regexp
112
+ /^\s*v(?:ar)?\s+l(?:ocal)?\s*$/
113
+ end
114
+
115
+ def execute
116
+ locals = @state.context.frame_locals(@state.frame_pos)
117
+ _self = @state.context.frame_self(@state.frame_pos)
118
+ locals['self'] = _self unless _self.to_s == "main"
119
+ print_variables(locals.keys, 'local') do |var|
120
+ locals[var]
121
+ end
122
+ end
123
+
124
+ class << self
125
+ def help_command
126
+ 'var'
127
+ end
128
+
129
+ def help(cmd)
130
+ %{
131
+ v[ar] l[ocal]\t\t\tshow local variables
132
+ }
133
+ end
134
+ end
135
+ end
136
+ end
@@ -0,0 +1,74 @@
1
+ if RUBY_VERSION < "1.9"
2
+ require 'ruby-debug/xml_printer'
3
+ else
4
+ require_relative 'xml_printer'
5
+ end
6
+ module Debugger
7
+
8
+ class EventProcessor
9
+
10
+ attr_accessor :line, :file, :context
11
+
12
+ def initialize(interface)
13
+ @printer = XmlPrinter.new(interface)
14
+ @line = nil
15
+ @file = nil
16
+ @last_breakpoint = nil
17
+ end
18
+
19
+ def at_breakpoint(context, breakpoint)
20
+ raise "@last_breakpoint supposed to be nil. is #{@last_breakpoint}" if @last_breakpoint
21
+ # at_breakpoint is immediately followed by #at_line event in
22
+ # ruby-debug-base. So postpone breakpoint printing until #at_line.
23
+ @last_breakpoint = breakpoint
24
+ end
25
+
26
+ def at_catchpoint(context, excpt)
27
+ @printer.print_catchpoint(excpt)
28
+ end
29
+
30
+ def at_tracing(context, file, line)
31
+ @printer.print_trace(context, file, line)
32
+ end
33
+
34
+ def at_line(context, file, line)
35
+ @printer.print_at_line(context, file, line) if context.nil? || context.stop_reason == :step
36
+ line_event(context, file, line)
37
+ end
38
+
39
+ def at_return(context, file, line)
40
+ @printer.print_at_line(context, file, line)
41
+ context.stop_frame = -1
42
+ line_event(context, file, line)
43
+ end
44
+
45
+ def line_event(context, file, line)
46
+ @line = line
47
+ @file = file
48
+ @context = context
49
+ if @last_breakpoint
50
+ # followed after #at_breakpoint in the same thread. Print breakpoint
51
+ # now when @line, @file and @context are correctly set to prevent race
52
+ # condition with `control thread'.
53
+ n = Debugger.breakpoints.index(@last_breakpoint) + 1
54
+ @printer.print_breakpoint n, @last_breakpoint
55
+ @last_breakpoint = nil
56
+ end
57
+ raise "DebuggerThread are not supposed to be traced (#{context.thread})" if context.thread.is_a?(Debugger::DebugThread)
58
+ @printer.print_debug("Stopping Thread %s", context.thread.to_s)
59
+ @printer.print_debug("Threads equal: %s", Thread.current == context.thread)
60
+ # will be resumed by commands like `step', `next', `continue', `finish'
61
+ # from `control thread'
62
+ Thread.stop
63
+ @printer.print_debug("Resumed Thread %s", context.thread.to_s)
64
+ @line = nil
65
+ @file = nil
66
+ @context = nil
67
+ end
68
+
69
+ def at_line?
70
+ @line
71
+ end
72
+
73
+ end
74
+ 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,39 @@
1
+ class TCPSocket
2
+
3
+ # Workaround for JRuby issue http://jira.codehaus.org/browse/JRUBY-2063
4
+ def non_blocking_gets
5
+ loop do
6
+ result, _, _ = IO.select( [self], nil, nil, 0.2 )
7
+ next unless result
8
+ return result[0].gets
9
+ end
10
+ end
11
+
12
+ end
13
+
14
+ module Debugger
15
+
16
+ class RemoteInterface # :nodoc:
17
+
18
+ def initialize(socket)
19
+ @socket = socket
20
+ end
21
+
22
+ def read_command
23
+ result = @socket.non_blocking_gets
24
+ raise IOError unless result
25
+ result.chomp
26
+ end
27
+
28
+ def print(*args)
29
+ @socket.printf(*args)
30
+ end
31
+
32
+ def close
33
+ @socket.close
34
+ rescue Exception
35
+ end
36
+
37
+ end
38
+
39
+ end
@@ -0,0 +1,2 @@
1
+ require 'ruby-debug/printers/plain_printer'
2
+ require 'ruby-debug/printers/xml_printer'
@@ -0,0 +1,152 @@
1
+ if RUBY_VERSION < "1.9"
2
+ require 'ruby-debug/interface'
3
+ require 'ruby-debug/command'
4
+ else
5
+ require_relative 'interface'
6
+ require_relative 'command'
7
+ end
8
+
9
+ module Debugger
10
+
11
+ class ControlCommandProcessor # :nodoc:
12
+ def initialize(interface)
13
+ @interface = interface
14
+ @printer = XmlPrinter.new(@interface)
15
+ end
16
+
17
+ def print(*args)
18
+ @interface.print(*args)
19
+ end
20
+
21
+ def process_commands
22
+ @printer.print_debug("Starting command read loop")
23
+ ctrl_cmd_classes = Command.commands.select{|cmd| cmd.control}
24
+ state = ControlState.new(@interface)
25
+ ctrl_cmds = ctrl_cmd_classes.map{|cmd| cmd.new(state, @printer)}
26
+
27
+ while input = @interface.read_command
28
+ # escape % since print_debug might use printf
29
+ @printer.print_debug "Processing: #{input.gsub('%', '%%')}"
30
+ # sleep 0.3
31
+ catch(:debug_error) do
32
+ if cmd = ctrl_cmds.find{|c| c.match(input) }
33
+ cmd.execute
34
+ else
35
+ process_context_commands(input)
36
+ end
37
+ end
38
+ end
39
+ rescue IOError, Errno::EPIPE
40
+ @printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
41
+ @printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
42
+ rescue Exception
43
+ @printer.print_error "INTERNAL ERROR!!! #{$!}\n" rescue nil
44
+ @printer.print_error $!.backtrace.map{|l| "\t#{l}"}.join("\n") rescue nil
45
+ ensure
46
+ @interface.close
47
+ end
48
+
49
+ def process_context_commands(input)
50
+ unless Debugger.event_processor.at_line?
51
+ @printer.print_error "There is no thread suspended at the time and therefore no context to execute '#{input.gsub('%', '%%')}'"
52
+ return
53
+ end
54
+ context = Debugger.event_processor.context
55
+ file = Debugger.event_processor.file
56
+ line = Debugger.event_processor.line
57
+ event_cmds_classes = Command.commands.select{|cmd| cmd.event}
58
+ state = State.new do |s|
59
+ s.context = context
60
+ s.file = file
61
+ s.line = line
62
+ s.binding = context.frame_binding(0)
63
+ s.interface = @interface
64
+ end
65
+ event_cmds = event_cmds_classes.map{|cmd| cmd.new(state, @printer) }
66
+ catch(:debug_error) do
67
+ splitter[input].each do |input|
68
+ # escape % since print_debug might use printf
69
+ @printer.print_debug "Processing context: #{input.gsub('%', '%%')}"
70
+ if cmd = event_cmds.find{ |c| c.match(input) }
71
+ if context.dead? && cmd.class.need_context
72
+ @printer.print_msg "Command is unavailable\n"
73
+ else
74
+ cmd.execute
75
+ end
76
+ else
77
+ @printer.print_msg "Unknown command: #{input}"
78
+ end
79
+ end
80
+ end
81
+
82
+ context.thread.run if state.proceed?
83
+ end
84
+
85
+ def splitter
86
+ return lambda do |str|
87
+ str.split(/;/).inject([]) do |m, v|
88
+ if m.empty?
89
+ m << v
90
+ else
91
+ if m.last[-1] == ?\\
92
+ m.last[-1,1] = ''
93
+ m.last << ';' << v
94
+ else
95
+ m << v
96
+ end
97
+ end
98
+ m
99
+ end
100
+ end
101
+ end
102
+ end
103
+
104
+ class State # :nodoc:
105
+
106
+ attr_accessor :context, :file, :line, :binding
107
+ attr_accessor :frame_pos, :previous_line
108
+ attr_accessor :interface
109
+
110
+ def initialize
111
+ @frame_pos = 0
112
+ @previous_line = nil
113
+ @proceed = false
114
+ yield self
115
+ end
116
+
117
+ def print(*args)
118
+ @interface.print(*args)
119
+ end
120
+
121
+ def proceed?
122
+ @proceed
123
+ end
124
+
125
+ def proceed
126
+ @proceed = true
127
+ end
128
+ end
129
+
130
+ class ControlState # :nodoc:
131
+
132
+ def initialize(interface)
133
+ @interface = interface
134
+ end
135
+
136
+ def proceed
137
+ end
138
+
139
+ def print(*args)
140
+ @interface.print(*args)
141
+ end
142
+
143
+ def context
144
+ nil
145
+ end
146
+
147
+ def file
148
+ print "ERROR: No filename given.\n"
149
+ throw :debug_error
150
+ end
151
+ end
152
+ end