ruby-debug-ide19 0.4.10

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