debug 1.4.0 → 1.5.0
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 +4 -4
- data/CONTRIBUTING.md +124 -0
- data/Gemfile +1 -0
- data/README.md +16 -16
- data/Rakefile +5 -5
- data/debug.gemspec +6 -4
- data/ext/debug/debug.c +84 -0
- data/ext/debug/extconf.rb +11 -0
- data/lib/debug/breakpoint.rb +24 -35
- data/lib/debug/client.rb +9 -3
- data/lib/debug/console.rb +34 -13
- data/lib/debug/frame_info.rb +4 -5
- data/lib/debug/local.rb +1 -1
- data/lib/debug/server.rb +65 -27
- data/lib/debug/server_cdp.rb +369 -148
- data/lib/debug/server_dap.rb +72 -75
- data/lib/debug/session.rb +225 -114
- data/lib/debug/source_repository.rb +91 -51
- data/lib/debug/thread_client.rb +94 -42
- data/lib/debug/tracer.rb +3 -9
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +6 -6
- metadata +4 -13
- data/.github/ISSUE_TEMPLATE/bug_report.md +0 -24
- data/.github/ISSUE_TEMPLATE/custom.md +0 -10
- data/.github/ISSUE_TEMPLATE/feature_request.md +0 -14
- data/.github/pull_request_template.md +0 -9
- data/.github/workflows/ruby.yml +0 -34
- data/.gitignore +0 -12
- data/bin/console +0 -14
- data/bin/gentest +0 -30
- data/bin/setup +0 -8
data/lib/debug/server_dap.rb
CHANGED
@@ -3,52 +3,51 @@
|
|
3
3
|
require 'json'
|
4
4
|
require 'irb/completion'
|
5
5
|
require 'tmpdir'
|
6
|
-
require 'json'
|
7
6
|
require 'fileutils'
|
8
7
|
|
9
8
|
module DEBUGGER__
|
10
9
|
module UI_DAP
|
11
10
|
SHOW_PROTOCOL = ENV['DEBUG_DAP_SHOW_PROTOCOL'] == '1' || ENV['RUBY_DEBUG_DAP_SHOW_PROTOCOL'] == '1'
|
12
11
|
|
13
|
-
def self.setup
|
14
|
-
|
15
|
-
|
12
|
+
def self.setup debug_port
|
13
|
+
if File.directory? '.vscode'
|
14
|
+
dir = Dir.pwd
|
15
|
+
else
|
16
|
+
dir = Dir.mktmpdir("ruby-debug-vscode-")
|
17
|
+
tempdir = true
|
18
|
+
end
|
19
|
+
|
20
|
+
at_exit do
|
16
21
|
CONFIG[:skip_path] = [//] # skip all
|
17
|
-
FileUtils.rm_rf dir
|
18
|
-
|
22
|
+
FileUtils.rm_rf dir if tempdir
|
23
|
+
end
|
24
|
+
|
25
|
+
key = rand.to_s
|
26
|
+
|
19
27
|
Dir.chdir(dir) do
|
20
|
-
Dir.mkdir('.vscode')
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
# This file will be removed at the end of the debuggee process.
|
25
|
-
#
|
26
|
-
# Note that vscode-rdbg extension is needed. Please install if you don't have.
|
27
|
-
MSG
|
28
|
-
}
|
29
|
-
open('.vscode/launch.json', 'w'){|f|
|
28
|
+
Dir.mkdir('.vscode') if tempdir
|
29
|
+
|
30
|
+
# vscode-rdbg 0.0.9 or later is needed
|
31
|
+
open('.vscode/rdbg_autoattach.json', 'w') do |f|
|
30
32
|
f.puts JSON.pretty_generate({
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
debugPort: sock_path,
|
39
|
-
autoAttach: true,
|
40
|
-
}
|
41
|
-
]
|
33
|
+
type: "rdbg",
|
34
|
+
name: "Attach with rdbg",
|
35
|
+
request: "attach",
|
36
|
+
rdbgPath: File.expand_path('../../exe/rdbg', __dir__),
|
37
|
+
debugPort: debug_port,
|
38
|
+
localfs: true,
|
39
|
+
autoAttach: key,
|
42
40
|
})
|
43
|
-
|
41
|
+
end
|
44
42
|
end
|
45
43
|
|
46
|
-
cmds = ['code', "#{dir}/"
|
44
|
+
cmds = ['code', "#{dir}/"]
|
47
45
|
cmdline = cmds.join(' ')
|
48
|
-
ssh_cmdline = "code --remote ssh-remote+[SSH hostname] #{dir}/
|
46
|
+
ssh_cmdline = "code --remote ssh-remote+[SSH hostname] #{dir}/"
|
49
47
|
|
50
48
|
STDERR.puts "Launching: #{cmdline}"
|
51
49
|
env = ENV.delete_if{|k, h| /RUBY/ =~ k}.to_h
|
50
|
+
env['RUBY_DEBUG_AUTOATTACH'] = key
|
52
51
|
|
53
52
|
unless system(env, *cmds)
|
54
53
|
DEBUGGER__.warn <<~MESSAGE
|
@@ -71,9 +70,20 @@ module DEBUGGER__
|
|
71
70
|
end
|
72
71
|
end
|
73
72
|
|
73
|
+
@local_fs = false
|
74
|
+
|
75
|
+
def self.local_fs
|
76
|
+
@local_fs
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.local_fs_set
|
80
|
+
@local_fs = true
|
81
|
+
end
|
82
|
+
|
74
83
|
def dap_setup bytes
|
75
84
|
CONFIG.set_config no_color: true
|
76
85
|
@seq = 0
|
86
|
+
UI_DAP.local_fs_set if self.kind_of?(UI_UnixDomainServer)
|
77
87
|
|
78
88
|
show_protocol :>, bytes
|
79
89
|
req = JSON.load(bytes)
|
@@ -145,8 +155,8 @@ module DEBUGGER__
|
|
145
155
|
def send **kw
|
146
156
|
kw[:seq] = @seq += 1
|
147
157
|
str = JSON.dump(kw)
|
148
|
-
show_protocol '<', str
|
149
158
|
@sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
|
159
|
+
show_protocol '<', str
|
150
160
|
end
|
151
161
|
|
152
162
|
def send_response req, success: true, message: nil, **kw
|
@@ -212,15 +222,18 @@ module DEBUGGER__
|
|
212
222
|
when 'launch'
|
213
223
|
send_response req
|
214
224
|
@is_attach = false
|
225
|
+
UI_DAP.local_fs_set if req.dig('arguments', 'localfs')
|
215
226
|
when 'attach'
|
216
227
|
send_response req
|
217
|
-
Process.kill(
|
228
|
+
Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
|
218
229
|
@is_attach = true
|
230
|
+
UI_DAP.local_fs_set if req.dig('arguments', 'localfs')
|
219
231
|
when 'setBreakpoints'
|
220
232
|
path = args.dig('source', 'path')
|
221
|
-
|
233
|
+
SESSION.clear_line_breakpoints path
|
234
|
+
|
222
235
|
bps = []
|
223
|
-
|
236
|
+
args['breakpoints'].each{|bp|
|
224
237
|
line = bp['line']
|
225
238
|
if cond = bp['condition']
|
226
239
|
bps << SESSION.add_line_breakpoint(path, line, cond: cond)
|
@@ -313,7 +326,7 @@ module DEBUGGER__
|
|
313
326
|
exit
|
314
327
|
when 'pause'
|
315
328
|
send_response req
|
316
|
-
Process.kill(
|
329
|
+
Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
|
317
330
|
when 'reverseContinue'
|
318
331
|
send_response req,
|
319
332
|
success: false, message: 'cancelled',
|
@@ -341,6 +354,8 @@ module DEBUGGER__
|
|
341
354
|
raise "Unknown request: #{req.inspect}"
|
342
355
|
end
|
343
356
|
end
|
357
|
+
ensure
|
358
|
+
send_event :terminated unless @sock.closed?
|
344
359
|
end
|
345
360
|
|
346
361
|
## called by the SESSION thread
|
@@ -496,7 +511,7 @@ module DEBUGGER__
|
|
496
511
|
when 'source'
|
497
512
|
ref = req.dig('arguments', 'sourceReference')
|
498
513
|
if src = @src_map[ref]
|
499
|
-
@ui.respond req, content: src.join
|
514
|
+
@ui.respond req, content: src.join("\n")
|
500
515
|
else
|
501
516
|
fail_response req, message: 'not found...'
|
502
517
|
end
|
@@ -595,7 +610,11 @@ module DEBUGGER__
|
|
595
610
|
event! :dap_result, :backtrace, req, {
|
596
611
|
stackFrames: @target_frames.map{|frame|
|
597
612
|
path = frame.realpath || frame.path
|
598
|
-
|
613
|
+
source_name = path ? File.basename(path) : frame.location.to_s
|
614
|
+
|
615
|
+
if !UI_DAP.local_fs || !(path && File.exist?(path))
|
616
|
+
ref = frame.file_lines
|
617
|
+
end
|
599
618
|
|
600
619
|
{
|
601
620
|
# id: ??? # filled by SESSION
|
@@ -603,7 +622,7 @@ module DEBUGGER__
|
|
603
622
|
line: frame.location.lineno,
|
604
623
|
column: 1,
|
605
624
|
source: {
|
606
|
-
name:
|
625
|
+
name: source_name,
|
607
626
|
path: path,
|
608
627
|
sourceReference: ref,
|
609
628
|
},
|
@@ -612,7 +631,7 @@ module DEBUGGER__
|
|
612
631
|
}
|
613
632
|
when :scopes
|
614
633
|
fid = args.shift
|
615
|
-
frame =
|
634
|
+
frame = get_frame(fid)
|
616
635
|
|
617
636
|
lnum =
|
618
637
|
if frame.binding
|
@@ -640,28 +659,12 @@ module DEBUGGER__
|
|
640
659
|
}]
|
641
660
|
when :scope
|
642
661
|
fid = args.shift
|
643
|
-
frame =
|
644
|
-
|
645
|
-
|
646
|
-
v = b.local_variable_get(name)
|
647
|
-
variable(name, v)
|
648
|
-
}
|
649
|
-
special_local_variables frame do |name, val|
|
650
|
-
vars.unshift variable(name, val)
|
651
|
-
end
|
652
|
-
vars.unshift variable('%self', b.receiver)
|
653
|
-
elsif lvars = frame.local_variables
|
654
|
-
vars = lvars.map{|var, val|
|
655
|
-
variable(var, val)
|
656
|
-
}
|
657
|
-
else
|
658
|
-
vars = [variable('%self', frame.self)]
|
659
|
-
special_local_variables frame do |name, val|
|
660
|
-
vars.push variable(name, val)
|
661
|
-
end
|
662
|
+
frame = get_frame(fid)
|
663
|
+
vars = collect_locals(frame).map do |var, val|
|
664
|
+
variable(var, val)
|
662
665
|
end
|
663
|
-
event! :dap_result, :scope, req, variables: vars, tid: self.id
|
664
666
|
|
667
|
+
event! :dap_result, :scope, req, variables: vars, tid: self.id
|
665
668
|
when :variable
|
666
669
|
vid = args.shift
|
667
670
|
obj = @var_map[vid]
|
@@ -712,12 +715,11 @@ module DEBUGGER__
|
|
712
715
|
|
713
716
|
when :evaluate
|
714
717
|
fid, expr, context = args
|
715
|
-
frame =
|
718
|
+
frame = get_frame(fid)
|
716
719
|
message = nil
|
717
720
|
|
718
|
-
if frame && (b = frame.
|
719
|
-
|
720
|
-
special_local_variables current_frame do |name, var|
|
721
|
+
if frame && (b = frame.eval_binding)
|
722
|
+
special_local_variables frame do |name, var|
|
721
723
|
b.local_variable_set(name, var) if /\%/ !~ name
|
722
724
|
end
|
723
725
|
|
@@ -733,8 +735,7 @@ module DEBUGGER__
|
|
733
735
|
case expr
|
734
736
|
when /\A\@\S/
|
735
737
|
begin
|
736
|
-
|
737
|
-
result = r.instance_variable_get(expr)
|
738
|
+
result = b.receiver.instance_variable_get(expr)
|
738
739
|
rescue NameError
|
739
740
|
message = "Error: Not defined instance variable: #{expr.inspect}"
|
740
741
|
end
|
@@ -745,14 +746,12 @@ module DEBUGGER__
|
|
745
746
|
break false
|
746
747
|
end
|
747
748
|
} and (message = "Error: Not defined global variable: #{expr.inspect}")
|
748
|
-
when
|
749
|
-
unless result = search_const(b,
|
749
|
+
when /(\A((::[A-Z]|[A-Z])\w*)+)/
|
750
|
+
unless result = search_const(b, $1)
|
750
751
|
message = "Error: Not defined constants: #{expr.inspect}"
|
751
752
|
end
|
752
753
|
else
|
753
754
|
begin
|
754
|
-
# try to check local variables
|
755
|
-
b.local_variable_defined?(expr) or raise NameError
|
756
755
|
result = b.local_variable_get(expr)
|
757
756
|
rescue NameError
|
758
757
|
# try to check method
|
@@ -774,7 +773,7 @@ module DEBUGGER__
|
|
774
773
|
|
775
774
|
when :completions
|
776
775
|
fid, text = args
|
777
|
-
frame =
|
776
|
+
frame = get_frame(fid)
|
778
777
|
|
779
778
|
if (b = frame&.binding) && word = text&.split(/[\s\{]/)&.last
|
780
779
|
words = IRB::InputCompletor::retrieve_completion_data(word, bind: b).compact
|
@@ -788,10 +787,8 @@ module DEBUGGER__
|
|
788
787
|
end
|
789
788
|
|
790
789
|
begin
|
791
|
-
|
792
|
-
|
793
|
-
phrase += " (variable:#{DEBUGGER__.safe_inspect(v)})"
|
794
|
-
end
|
790
|
+
v = b.local_variable_get(w)
|
791
|
+
phrase += " (variable:#{DEBUGGER__.safe_inspect(v)})"
|
795
792
|
rescue NameError
|
796
793
|
end
|
797
794
|
|
@@ -807,7 +804,7 @@ module DEBUGGER__
|
|
807
804
|
end
|
808
805
|
|
809
806
|
def search_const b, expr
|
810
|
-
cs = expr.split('::')
|
807
|
+
cs = expr.delete_prefix('::').split('::')
|
811
808
|
[Object, *b.eval('Module.nesting')].reverse_each{|mod|
|
812
809
|
if cs.all?{|c|
|
813
810
|
if mod.const_defined?(c)
|