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.
- data/CHANGES +75 -0
- data/ChangeLog +465 -0
- data/ChangeLog.archive +1073 -0
- data/MIT-LICENSE +24 -0
- data/Rakefile +110 -0
- data/bin/rdebug-ide +88 -0
- data/ext/mkrf_conf.rb +28 -0
- data/lib/ruby-debug-ide.rb +172 -0
- data/lib/ruby-debug/command.rb +169 -0
- data/lib/ruby-debug/commands/breakpoints.rb +129 -0
- data/lib/ruby-debug/commands/catchpoint.rb +52 -0
- data/lib/ruby-debug/commands/condition.rb +51 -0
- data/lib/ruby-debug/commands/control.rb +129 -0
- data/lib/ruby-debug/commands/enable.rb +203 -0
- data/lib/ruby-debug/commands/eval.rb +64 -0
- data/lib/ruby-debug/commands/frame.rb +155 -0
- data/lib/ruby-debug/commands/inspect.rb +24 -0
- data/lib/ruby-debug/commands/jump.rb +73 -0
- data/lib/ruby-debug/commands/load.rb +18 -0
- data/lib/ruby-debug/commands/stepping.rb +108 -0
- data/lib/ruby-debug/commands/threads.rb +153 -0
- data/lib/ruby-debug/commands/variables.rb +136 -0
- data/lib/ruby-debug/event_processor.rb +74 -0
- data/lib/ruby-debug/helper.rb +33 -0
- data/lib/ruby-debug/interface.rb +39 -0
- data/lib/ruby-debug/printers.rb +2 -0
- data/lib/ruby-debug/processor.rb +152 -0
- data/lib/ruby-debug/xml_printer.rb +268 -0
- data/test/rd_basic_test.rb +10 -0
- data/test/rd_catchpoint_test.rb +20 -0
- data/test/rd_condition_test.rb +11 -0
- data/test/rd_enable_disable_test.rb +43 -0
- data/test/rd_inspect_test.rb +11 -0
- data/test/rd_stepping_breakpoints_test.rb +36 -0
- data/test/rd_test_base.rb +44 -0
- data/test/rd_threads_and_frames_test.rb +11 -0
- data/test/rd_variables_test.rb +11 -0
- data/test/ruby-debug/xml_printer_test.rb +105 -0
- metadata +103 -0
@@ -0,0 +1,268 @@
|
|
1
|
+
require 'cgi'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Debugger
|
5
|
+
|
6
|
+
class XmlPrinter # :nodoc:
|
7
|
+
attr_accessor :interface
|
8
|
+
|
9
|
+
def initialize(interface)
|
10
|
+
@interface = interface
|
11
|
+
end
|
12
|
+
|
13
|
+
def print_msg(*args)
|
14
|
+
msg, *args = args
|
15
|
+
xml_message = CGI.escapeHTML(msg % args)
|
16
|
+
print "<message>#{xml_message}</message>"
|
17
|
+
end
|
18
|
+
|
19
|
+
# Sends debug message to the frontend if XML debug logging flag (--xml-debug) is on.
|
20
|
+
def print_debug(*args)
|
21
|
+
Debugger.print_debug(*args)
|
22
|
+
if Debugger.xml_debug
|
23
|
+
msg, *args = args
|
24
|
+
xml_message = CGI.escapeHTML(msg % args)
|
25
|
+
@interface.print("<message debug='true'>#{xml_message}</message>")
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def print_error(*args)
|
30
|
+
print_element("error") do
|
31
|
+
msg, *args = args
|
32
|
+
print CGI.escapeHTML(msg % args)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def print_frames(context, current_frame_id)
|
37
|
+
print_element("frames") do
|
38
|
+
(0...context.stack_size).each do |id|
|
39
|
+
print_frame(context, id, current_frame_id)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
def print_current_frame(frame_pos)
|
45
|
+
print_debug "Selected frame no #{frame_pos}"
|
46
|
+
end
|
47
|
+
|
48
|
+
def print_frame(context, frame_id, current_frame_id)
|
49
|
+
# idx + 1: one-based numbering as classic-debugger
|
50
|
+
file = context.frame_file(frame_id)
|
51
|
+
print "<frame no=\'%s\' file=\'%s\' line=\'%s\' #{"current='true' " if frame_id == current_frame_id}/>",
|
52
|
+
frame_id + 1, File.expand_path(file), context.frame_line(frame_id)
|
53
|
+
end
|
54
|
+
|
55
|
+
def print_contexts(contexts)
|
56
|
+
print_element("threads") do
|
57
|
+
contexts.each do |c|
|
58
|
+
print_context(c) unless c.ignored?
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def print_context(context)
|
64
|
+
current = 'current="yes"' if context.thread == Thread.current
|
65
|
+
print "<thread id=\"%s\" status=\"%s\" #{current}/>", context.thnum, context.thread.status
|
66
|
+
end
|
67
|
+
|
68
|
+
def print_variables(vars, kind)
|
69
|
+
print_element("variables") do
|
70
|
+
# print self at top position
|
71
|
+
print_variable('self', yield('self'), kind) if vars.include?('self')
|
72
|
+
vars.sort.each do |v|
|
73
|
+
print_variable(v, yield(v), kind) unless v == 'self'
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def print_array(array)
|
79
|
+
print_element("variables") do
|
80
|
+
index = 0
|
81
|
+
array.each { |e|
|
82
|
+
print_variable('[' + index.to_s + ']', e, 'instance')
|
83
|
+
index += 1
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def print_hash(hash)
|
89
|
+
print_element("variables") do
|
90
|
+
hash.keys.each { | k |
|
91
|
+
if k.class.name == "String"
|
92
|
+
name = '\'' + k + '\''
|
93
|
+
else
|
94
|
+
name = k.to_s
|
95
|
+
end
|
96
|
+
print_variable(name, hash[k], 'instance')
|
97
|
+
}
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def print_variable(name, value, kind)
|
102
|
+
name = name.to_s
|
103
|
+
unless value
|
104
|
+
print("<variable name=\"%s\" kind=\"%s\"/>", CGI.escapeHTML(name), kind)
|
105
|
+
return
|
106
|
+
end
|
107
|
+
if value.is_a?(Array) || value.is_a?(Hash)
|
108
|
+
has_children = !value.empty?
|
109
|
+
unless has_children
|
110
|
+
value_str = "Empty #{value.class}"
|
111
|
+
else
|
112
|
+
value_str = "#{value.class} (#{value.size} element(s))"
|
113
|
+
end
|
114
|
+
else
|
115
|
+
has_children = !value.instance_variables.empty? || !value.class.class_variables.empty?
|
116
|
+
value_str = value.to_s || 'nil' rescue "<#to_s method raised exception: #$!>"
|
117
|
+
unless value_str.is_a?(String)
|
118
|
+
value_str = "ERROR: #{value.class}.to_s method returns #{value_str.class}. Should return String."
|
119
|
+
end
|
120
|
+
if value_str =~ /^\"(.*)"$/
|
121
|
+
value_str = $1
|
122
|
+
end
|
123
|
+
end
|
124
|
+
value_str = "[Binary Data]" if value_str.is_binary_data?
|
125
|
+
print("<variable name=\"%s\" kind=\"%s\" value=\"%s\" type=\"%s\" hasChildren=\"%s\" objectId=\"%#+x\"/>",
|
126
|
+
CGI.escapeHTML(name), kind, CGI.escapeHTML(value_str), value.class,
|
127
|
+
has_children, value.respond_to?(:object_id) ? value.object_id : value.id)
|
128
|
+
end
|
129
|
+
|
130
|
+
def print_breakpoints(breakpoints)
|
131
|
+
print_element 'breakpoints' do
|
132
|
+
breakpoints.sort_by{|b| b.id }.each do |b|
|
133
|
+
print "<breakpoint n=\"%d\" file=\"%s\" line=\"%s\" />", b.id, b.source, b.pos.to_s
|
134
|
+
end
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
def print_breakpoint_added(b)
|
139
|
+
print "<breakpointAdded no=\"%s\" location=\"%s:%s\"/>", b.id, b.source, b.pos
|
140
|
+
end
|
141
|
+
|
142
|
+
def print_breakpoint_deleted(b)
|
143
|
+
print "<breakpointDeleted no=\"%s\"/>", b.id
|
144
|
+
end
|
145
|
+
|
146
|
+
def print_breakpoint_enabled(b)
|
147
|
+
print "<breakpointEnabled bp_id=\"%s\"/>", b.id
|
148
|
+
end
|
149
|
+
|
150
|
+
def print_breakpoint_disabled(b)
|
151
|
+
print "<breakpointDisabled bp_id=\"%s\"/>", b.id
|
152
|
+
end
|
153
|
+
|
154
|
+
def print_contdition_set(bp_id)
|
155
|
+
print "<conditionSet bp_id=\"%d\"/>", bp_id
|
156
|
+
end
|
157
|
+
|
158
|
+
def print_catchpoint_set(exception_class_name)
|
159
|
+
print "<catchpointSet exception=\"%s\"/>", exception_class_name
|
160
|
+
end
|
161
|
+
|
162
|
+
def print_expressions(exps)
|
163
|
+
print_element "expressions" do
|
164
|
+
exps.each_with_index do |(exp, value), idx|
|
165
|
+
print_expression(exp, value, idx+1)
|
166
|
+
end
|
167
|
+
end unless exps.empty?
|
168
|
+
end
|
169
|
+
|
170
|
+
def print_expression(exp, value, idx)
|
171
|
+
print "<dispay name=\"%s\" value=\"%s\" no=\"%d\" />", exp, value, idx
|
172
|
+
end
|
173
|
+
|
174
|
+
def print_eval(exp, value)
|
175
|
+
print "<eval expression=\"%s\" value=\"%s\" />", CGI.escapeHTML(exp), value
|
176
|
+
end
|
177
|
+
|
178
|
+
def print_pp(value)
|
179
|
+
print value
|
180
|
+
end
|
181
|
+
|
182
|
+
def print_list(b, e, file, line)
|
183
|
+
print "[%d, %d] in %s\n", b, e, file
|
184
|
+
if lines = Debugger.source_for(file)
|
185
|
+
b.upto(e) do |n|
|
186
|
+
if n > 0 && lines[n-1]
|
187
|
+
if n == line
|
188
|
+
print "=> %d %s\n", n, lines[n-1].chomp
|
189
|
+
else
|
190
|
+
print " %d %s\n", n, lines[n-1].chomp
|
191
|
+
end
|
192
|
+
end
|
193
|
+
end
|
194
|
+
else
|
195
|
+
print "No sourcefile available for %s\n", file
|
196
|
+
end
|
197
|
+
end
|
198
|
+
|
199
|
+
def print_methods(methods)
|
200
|
+
print_element "methods" do
|
201
|
+
methods.each do |method|
|
202
|
+
print "<method name=\"%s\" />", method
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Events
|
208
|
+
|
209
|
+
def print_breakpoint(n, breakpoint)
|
210
|
+
print("<breakpoint file=\"%s\" line=\"%s\" threadId=\"%d\"/>",
|
211
|
+
breakpoint.source, breakpoint.pos, Debugger.current_context.thnum)
|
212
|
+
end
|
213
|
+
|
214
|
+
def print_catchpoint(exception)
|
215
|
+
context = Debugger.current_context
|
216
|
+
print("<exception file=\"%s\" line=\"%s\" type=\"%s\" message=\"%s\" threadId=\"%d\"/>",
|
217
|
+
context.frame_file(0), context.frame_line(0), exception.class, CGI.escapeHTML(exception.to_s), context.thnum)
|
218
|
+
end
|
219
|
+
|
220
|
+
def print_trace(context, file, line)
|
221
|
+
Debugger::print_debug "trace: location=\"%s:%s\", threadId=%d", file, line, context.thnum
|
222
|
+
# TBD: do we want to clog fronend with the <trace> elements? There are tons of them.
|
223
|
+
# print "<trace file=\"%s\" line=\"%s\" threadId=\"%d\" />", file, line, context.thnum
|
224
|
+
end
|
225
|
+
|
226
|
+
def print_at_line(context, file, line)
|
227
|
+
print "<suspended file=\'%s\' line=\'%s\' threadId=\'%d\' frames=\'%d\'/>",
|
228
|
+
File.expand_path(file), line, context.thnum, context.stack_size
|
229
|
+
end
|
230
|
+
|
231
|
+
def print_exception(exception, binding)
|
232
|
+
print "<processingException type=\"%s\" message=\"%s\"/>",
|
233
|
+
exception.class, CGI.escapeHTML(exception.to_s)
|
234
|
+
end
|
235
|
+
|
236
|
+
def print_inspect(eval_result)
|
237
|
+
print_element("variables") do
|
238
|
+
print_variable("eval_result", eval_result, 'local')
|
239
|
+
end
|
240
|
+
end
|
241
|
+
|
242
|
+
def print_load_result(file, exception=nil)
|
243
|
+
if exception then
|
244
|
+
print("<loadResult file=\"%s\" exceptionType=\"%s\" exceptionMessage=\"%s\"/>", file, exception.class, CGI.escapeHTML(exception.to_s))
|
245
|
+
else
|
246
|
+
print("<loadResult file=\"%s\" status=\"OK\"/>", file)
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
def print_element(name)
|
251
|
+
print("<#{name}>")
|
252
|
+
begin
|
253
|
+
yield
|
254
|
+
ensure
|
255
|
+
print("</#{name}>")
|
256
|
+
end
|
257
|
+
end
|
258
|
+
|
259
|
+
private
|
260
|
+
|
261
|
+
def print(*params)
|
262
|
+
Debugger::print_debug(*params)
|
263
|
+
@interface.print(*params)
|
264
|
+
end
|
265
|
+
|
266
|
+
end
|
267
|
+
|
268
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rd_test_base'
|
4
|
+
|
5
|
+
class RDCatchpointTest < RDTestBase
|
6
|
+
|
7
|
+
def test_catchpoint_basics
|
8
|
+
create_socket ['sleep 0.01', '5/0', 'sleep 0.01']
|
9
|
+
run_to_line(1)
|
10
|
+
send_next
|
11
|
+
assert_suspension(@test_path, 2, 1)
|
12
|
+
send_ruby('catch ZeroDivisionError')
|
13
|
+
assert_catchpoint_set('ZeroDivisionError')
|
14
|
+
send_next
|
15
|
+
assert_exception(@test_path, 2, 'ZeroDivisionError')
|
16
|
+
send_next
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,43 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rd_test_base'
|
4
|
+
|
5
|
+
class RDEnableDisableTest < RDTestBase
|
6
|
+
|
7
|
+
def test_enable_disable_basics
|
8
|
+
create_socket ['1.upto(10) do', 'sleep 0.01', 'sleep 0.01', 'end']
|
9
|
+
|
10
|
+
send_test_breakpoint(2)
|
11
|
+
assert_breakpoint_added_no(1)
|
12
|
+
send_test_breakpoint(3)
|
13
|
+
assert_breakpoint_added_no(2)
|
14
|
+
|
15
|
+
start_debugger
|
16
|
+
assert_test_breakpoint(2)
|
17
|
+
send_cont
|
18
|
+
assert_test_breakpoint(3)
|
19
|
+
send_cont
|
20
|
+
assert_test_breakpoint(2)
|
21
|
+
send_ruby('disable 2')
|
22
|
+
assert_breakpoint_disabled(2)
|
23
|
+
send_cont
|
24
|
+
assert_test_breakpoint(2)
|
25
|
+
send_cont
|
26
|
+
assert_test_breakpoint(2)
|
27
|
+
send_ruby('enable 2')
|
28
|
+
assert_breakpoint_enabled(2)
|
29
|
+
send_cont
|
30
|
+
assert_test_breakpoint(3)
|
31
|
+
send_cont
|
32
|
+
assert_test_breakpoint(2)
|
33
|
+
send_cont
|
34
|
+
assert_test_breakpoint(3)
|
35
|
+
send_ruby('disable 1')
|
36
|
+
assert_breakpoint_disabled(1)
|
37
|
+
send_ruby('disable 2')
|
38
|
+
assert_breakpoint_disabled(2)
|
39
|
+
send_cont
|
40
|
+
end
|
41
|
+
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'rd_test_base'
|
4
|
+
require 'stepping_breakpoints_test'
|
5
|
+
|
6
|
+
class RDSteppingAndBreakpointsTest < RDTestBase
|
7
|
+
|
8
|
+
include SteppingAndBreakpointsTest
|
9
|
+
|
10
|
+
def test_hit_breakpoint_while_stepping_over
|
11
|
+
create_test2 ["class Test2", "def print", "puts 'XX'", "puts 'XX'", "end", "end"]
|
12
|
+
create_socket ["require 'test2.rb'", "Test2.new.print", "puts 'a'"]
|
13
|
+
send_ruby("b #{@test2_name}:4")
|
14
|
+
assert_breakpoint_added_no(1)
|
15
|
+
run_to_line(2)
|
16
|
+
send_next
|
17
|
+
assert_breakpoint(@test2_name, 4)
|
18
|
+
send_cont
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_breakpoint_and_continue_from_other_file
|
22
|
+
create_test2 ["class Test2", "def print12", "puts 'one'","puts 'two'", "end", "end"]
|
23
|
+
create_socket ["require 'test2.rb'", "Test2.new.print12", "puts 'three'"]
|
24
|
+
send_test_breakpoint(2)
|
25
|
+
assert_breakpoint_added_no(1)
|
26
|
+
send_ruby("b #{@test2_name}:4")
|
27
|
+
assert_breakpoint_added_no(2)
|
28
|
+
start_debugger
|
29
|
+
assert_test_breakpoint(2)
|
30
|
+
send_next # test:1 -> test2:4
|
31
|
+
assert_breakpoint(@test2_name, 4)
|
32
|
+
send_cont # test2:4 -> test:3
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
@@ -0,0 +1,44 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "test-base")
|
4
|
+
$:.unshift File.join(File.dirname(__FILE__), "..", "lib")
|
5
|
+
|
6
|
+
require 'test_base'
|
7
|
+
|
8
|
+
class RDTestBase < TestBase
|
9
|
+
|
10
|
+
def setup
|
11
|
+
super
|
12
|
+
@rdebug_ide = config_load('rdebug_ide', true) || find_rdebug_ide
|
13
|
+
unless @rdebug_ide and File.exist?(@rdebug_ide)
|
14
|
+
@fast_fail = true
|
15
|
+
assert_not_nil(@rdebug_ide, "Cannot find rdebug-ide executable. " +
|
16
|
+
"Neither set in the config(.private).yaml nor found on the PATH")
|
17
|
+
assert(false, "#{@rdebug_ide} exist")
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def debug_command(script, port)
|
22
|
+
cmd = "#{interpreter}"
|
23
|
+
cmd << " --debug" if jruby?
|
24
|
+
cmd << " -J-Xdebug -J-Xrunjdwp:transport=dt_socket,address=8000,server=y,suspend=y" if jruby? and debug_jruby?
|
25
|
+
cmd << " -I '#{File.dirname(script)}' #{@rdebug_ide} _0.4.9_" +
|
26
|
+
(@verbose_server ? " -d" : "") +
|
27
|
+
" -p #{port} -- '#{script}'"
|
28
|
+
end
|
29
|
+
|
30
|
+
def start_debugger
|
31
|
+
send_ruby("start")
|
32
|
+
end
|
33
|
+
|
34
|
+
private
|
35
|
+
|
36
|
+
def find_rdebug_ide
|
37
|
+
ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
|
38
|
+
rdebug_ide = File.join(dir, 'rdebug-ide')
|
39
|
+
return rdebug_ide if File.exists?(rdebug_ide)
|
40
|
+
end
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|