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/console.rb
CHANGED
@@ -30,6 +30,18 @@ module DEBUGGER__
|
|
30
30
|
prepend m
|
31
31
|
end if SIGWINCH_SUPPORTED
|
32
32
|
|
33
|
+
def parse_input buff, commands
|
34
|
+
c, rest = get_command buff
|
35
|
+
case
|
36
|
+
when commands.keys.include?(c)
|
37
|
+
:command
|
38
|
+
when !rest && /\A\s*[a-z]*\z/ =~ c
|
39
|
+
nil
|
40
|
+
else
|
41
|
+
:ruby
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
33
45
|
def readline_setup prompt
|
34
46
|
load_history_if_not_loaded
|
35
47
|
commands = DEBUGGER__.commands
|
@@ -38,7 +50,17 @@ module DEBUGGER__
|
|
38
50
|
prev_output_modifier_proc = Reline.output_modifier_proc
|
39
51
|
prev_prompt_proc = Reline.prompt_proc
|
40
52
|
|
41
|
-
|
53
|
+
# prompt state
|
54
|
+
state = nil # :command, :ruby, nil (unknown)
|
55
|
+
|
56
|
+
Reline.prompt_proc = -> args, *kw do
|
57
|
+
case state = parse_input(args.first, commands)
|
58
|
+
when nil, :command
|
59
|
+
[prompt, prompt]
|
60
|
+
when :ruby
|
61
|
+
[prompt.sub('rdbg'){colorize('ruby', [:RED])}] * 2
|
62
|
+
end
|
63
|
+
end
|
42
64
|
|
43
65
|
Reline.completion_proc = -> given do
|
44
66
|
buff = Reline.line_buffer
|
@@ -52,17 +74,16 @@ module DEBUGGER__
|
|
52
74
|
end
|
53
75
|
files
|
54
76
|
else
|
55
|
-
commands.keys.grep(/\A#{given}/)
|
77
|
+
commands.keys.grep(/\A#{Regexp.escape(given)}/)
|
56
78
|
end
|
57
79
|
end
|
58
80
|
|
59
81
|
Reline.output_modifier_proc = -> buff, **kw do
|
60
82
|
c, rest = get_command buff
|
61
83
|
|
62
|
-
case
|
63
|
-
when
|
64
|
-
|
65
|
-
cmd = colorize(c.strip, [:CYAN, :UNDERLINE])
|
84
|
+
case state
|
85
|
+
when :command
|
86
|
+
cmd = colorize(c, [:CYAN, :UNDERLINE])
|
66
87
|
|
67
88
|
if commands[c] == c
|
68
89
|
rprompt = colorize(" # command", [:DIM])
|
@@ -70,12 +91,12 @@ module DEBUGGER__
|
|
70
91
|
rprompt = colorize(" # #{commands[c]} command", [:DIM])
|
71
92
|
end
|
72
93
|
|
73
|
-
rest =
|
74
|
-
cmd + rest
|
75
|
-
when
|
94
|
+
rest = rest ? colorize_code(rest) : ''
|
95
|
+
cmd + rest + rprompt
|
96
|
+
when nil
|
76
97
|
buff
|
77
|
-
|
78
|
-
colorize_code(buff.chomp)
|
98
|
+
when :ruby
|
99
|
+
colorize_code(buff.chomp)
|
79
100
|
end
|
80
101
|
end
|
81
102
|
|
@@ -90,9 +111,9 @@ module DEBUGGER__
|
|
90
111
|
private def get_command line
|
91
112
|
case line.chomp
|
92
113
|
when /\A(\s*[a-z]+)(\s.*)?\z$/
|
93
|
-
return $1, $2
|
114
|
+
return $1.strip, $2
|
94
115
|
else
|
95
|
-
line.
|
116
|
+
line.strip
|
96
117
|
end
|
97
118
|
end
|
98
119
|
|
data/lib/debug/frame_info.rb
CHANGED
@@ -83,14 +83,14 @@ module DEBUGGER__
|
|
83
83
|
|
84
84
|
def block_identifier
|
85
85
|
return unless frame_type == :block
|
86
|
-
args = parameters_info
|
86
|
+
args = parameters_info
|
87
87
|
_, level, block_loc = location.label.match(BLOCK_LABL_REGEXP).to_a
|
88
88
|
[level || "", block_loc, args]
|
89
89
|
end
|
90
90
|
|
91
91
|
def method_identifier
|
92
92
|
return unless frame_type == :method
|
93
|
-
args = parameters_info
|
93
|
+
args = parameters_info
|
94
94
|
ci = "#{klass_sig}#{callee}"
|
95
95
|
[ci, args]
|
96
96
|
end
|
@@ -123,7 +123,6 @@ module DEBUGGER__
|
|
123
123
|
if b = self.dupped_binding
|
124
124
|
b
|
125
125
|
else
|
126
|
-
b = TOPLEVEL_BINDING unless b = self.binding
|
127
126
|
b = self.binding || TOPLEVEL_BINDING
|
128
127
|
self.dupped_binding = b.dup
|
129
128
|
end
|
@@ -151,8 +150,8 @@ module DEBUGGER__
|
|
151
150
|
local_variables[var]
|
152
151
|
end
|
153
152
|
|
154
|
-
def parameters_info
|
155
|
-
vars = iseq.
|
153
|
+
def parameters_info
|
154
|
+
vars = iseq.parameters_symbols
|
156
155
|
vars.map{|var|
|
157
156
|
begin
|
158
157
|
{ name: var, value: DEBUGGER__.safe_inspect(local_variable_get(var), short: true) }
|
data/lib/debug/local.rb
CHANGED
data/lib/debug/server.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'socket'
|
4
|
+
require 'etc'
|
4
5
|
require_relative 'config'
|
5
6
|
require_relative 'version'
|
6
7
|
|
@@ -19,8 +20,8 @@ module DEBUGGER__
|
|
19
20
|
@session = nil
|
20
21
|
end
|
21
22
|
|
22
|
-
class Terminate < StandardError
|
23
|
-
end
|
23
|
+
class Terminate < StandardError; end
|
24
|
+
class GreetingError < StandardError; end
|
24
25
|
|
25
26
|
def deactivate
|
26
27
|
@reader_thread.raise Terminate
|
@@ -47,10 +48,12 @@ module DEBUGGER__
|
|
47
48
|
|
48
49
|
accept do |server, already_connected: false|
|
49
50
|
DEBUGGER__.warn "Connected."
|
51
|
+
greeting_done = false
|
50
52
|
|
51
53
|
@accept_m.synchronize{
|
52
54
|
@sock = server
|
53
55
|
greeting
|
56
|
+
greeting_done = true
|
54
57
|
|
55
58
|
@accept_cv.signal
|
56
59
|
|
@@ -69,13 +72,17 @@ module DEBUGGER__
|
|
69
72
|
process
|
70
73
|
end
|
71
74
|
|
75
|
+
rescue GreetingError => e
|
76
|
+
DEBUGGER__.warn "GreetingError: #{e.message}"
|
77
|
+
next
|
72
78
|
rescue Terminate
|
73
79
|
raise # should catch at outer scope
|
74
80
|
rescue => e
|
75
81
|
DEBUGGER__.warn "ReaderThreadError: #{e}"
|
76
82
|
pp e.backtrace
|
77
83
|
ensure
|
78
|
-
|
84
|
+
DEBUGGER__.warn "Disconnected."
|
85
|
+
cleanup_reader if greeting_done
|
79
86
|
end # accept
|
80
87
|
|
81
88
|
rescue Terminate
|
@@ -84,7 +91,7 @@ module DEBUGGER__
|
|
84
91
|
end
|
85
92
|
|
86
93
|
def cleanup_reader
|
87
|
-
|
94
|
+
@sock.close if @sock
|
88
95
|
@sock = nil
|
89
96
|
@q_msg.close
|
90
97
|
@q_msg = nil
|
@@ -92,19 +99,31 @@ module DEBUGGER__
|
|
92
99
|
@q_ans = nil
|
93
100
|
end
|
94
101
|
|
102
|
+
def check_cookie c
|
103
|
+
cookie = CONFIG[:cookie]
|
104
|
+
if cookie && cookie != c
|
105
|
+
raise GreetingError, "Cookie mismatch (#{$2.inspect} was sent)"
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
95
109
|
def greeting
|
96
110
|
case g = @sock.gets
|
111
|
+
when /^info cookie:\s+(.*)$/
|
112
|
+
check_cookie $1
|
113
|
+
@sock.puts "PID: #{Process.pid}, $0: #{$0}"
|
114
|
+
@sock.puts "debug #{VERSION} on #{RUBY_DESCRIPTION}"
|
115
|
+
@sock.puts "uname: #{Etc.uname.inspect}"
|
116
|
+
@sock.close
|
117
|
+
raise GreetingError, "HEAD request"
|
118
|
+
|
97
119
|
when /^version:\s+(.+)\s+width: (\d+) cookie:\s+(.*)$/
|
98
120
|
v, w, c = $1, $2, $3
|
99
121
|
# TODO: protocol version
|
100
122
|
if v != VERSION
|
101
|
-
raise "Incompatible version (
|
123
|
+
raise GreetingError, "Incompatible version (server:#{VERSION} and client:#{$1})"
|
102
124
|
end
|
103
125
|
|
104
|
-
|
105
|
-
if cookie && cookie != c
|
106
|
-
raise "Cookie mismatch (#{$2.inspect} was sent)"
|
107
|
-
end
|
126
|
+
check_cookie c
|
108
127
|
|
109
128
|
@width = w.to_i
|
110
129
|
|
@@ -115,7 +134,7 @@ module DEBUGGER__
|
|
115
134
|
self.extend(UI_DAP)
|
116
135
|
@repl = false
|
117
136
|
dap_setup @sock.read($1.to_i)
|
118
|
-
when /^GET
|
137
|
+
when /^GET \/.* HTTP\/1.1/
|
119
138
|
require_relative 'server_cdp'
|
120
139
|
|
121
140
|
self.extend(UI_CDP)
|
@@ -125,7 +144,7 @@ module DEBUGGER__
|
|
125
144
|
@ws_server = UI_CDP::WebSocketServer.new(@sock)
|
126
145
|
@ws_server.handshake
|
127
146
|
else
|
128
|
-
raise "
|
147
|
+
raise GreetingError, "Unknown greeting message: #{g}"
|
129
148
|
end
|
130
149
|
end
|
131
150
|
|
@@ -325,18 +344,28 @@ module DEBUGGER__
|
|
325
344
|
def after_fork_parent
|
326
345
|
# do nothing
|
327
346
|
end
|
347
|
+
|
348
|
+
def vscode_setup debug_port
|
349
|
+
require_relative 'server_dap'
|
350
|
+
UI_DAP.setup debug_port
|
351
|
+
end
|
328
352
|
end
|
329
353
|
|
330
354
|
class UI_TcpServer < UI_ServerBase
|
331
355
|
def initialize host: nil, port: nil
|
332
356
|
@addr = nil
|
333
357
|
@host = host || CONFIG[:host] || '127.0.0.1'
|
334
|
-
@
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
358
|
+
@port_save_file = nil
|
359
|
+
@port = begin
|
360
|
+
port_str = (port && port.to_s) || CONFIG[:port] || raise("Specify listening port by RUBY_DEBUG_PORT environment variable.")
|
361
|
+
case port_str
|
362
|
+
when /\A\d+\z/
|
339
363
|
port_str.to_i
|
364
|
+
when /\A(\d+):(.+)\z/
|
365
|
+
@port_save_file = $2
|
366
|
+
$1.to_i
|
367
|
+
else
|
368
|
+
raise "Specify digits for port number"
|
340
369
|
end
|
341
370
|
end
|
342
371
|
|
@@ -347,10 +376,10 @@ module DEBUGGER__
|
|
347
376
|
require_relative 'server_cdp'
|
348
377
|
|
349
378
|
unless @chrome_pid = UI_CDP.setup_chrome(@addr)
|
350
|
-
DEBUGGER__.warn <<~EOS
|
379
|
+
DEBUGGER__.warn <<~EOS
|
351
380
|
With Chrome browser, type the following URL in the address-bar:
|
352
|
-
|
353
|
-
devtools://devtools/bundled/inspector.html?ws=#{@addr}
|
381
|
+
|
382
|
+
devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@addr}/#{SecureRandom.uuid}
|
354
383
|
|
355
384
|
EOS
|
356
385
|
end
|
@@ -364,8 +393,13 @@ module DEBUGGER__
|
|
364
393
|
Socket.tcp_server_sockets @host, @port do |socks|
|
365
394
|
@addr = socks[0].local_address.inspect_sockaddr # Change this part if `socks` are multiple.
|
366
395
|
rdbg = File.expand_path('../../exe/rdbg', __dir__)
|
367
|
-
|
368
396
|
DEBUGGER__.warn "Debugger can attach via TCP/IP (#{@addr})"
|
397
|
+
|
398
|
+
if @port_save_file
|
399
|
+
File.write(@port_save_file, "#{socks[0].local_address.ip_port.to_s}\n")
|
400
|
+
DEBUGGER__.warn "Port is saved into #{@port_save_file}"
|
401
|
+
end
|
402
|
+
|
369
403
|
DEBUGGER__.info <<~EOS
|
370
404
|
With rdbg, use the following command line:
|
371
405
|
#
|
@@ -373,7 +407,12 @@ module DEBUGGER__
|
|
373
407
|
#
|
374
408
|
EOS
|
375
409
|
|
376
|
-
|
410
|
+
case CONFIG[:open_frontend]
|
411
|
+
when 'chrome'
|
412
|
+
chrome_setup
|
413
|
+
when 'vscode'
|
414
|
+
vscode_setup @addr
|
415
|
+
end
|
377
416
|
|
378
417
|
Socket.accept_loop(socks) do |sock, client|
|
379
418
|
@client_addr = client
|
@@ -397,6 +436,10 @@ module DEBUGGER__
|
|
397
436
|
end
|
398
437
|
ensure
|
399
438
|
@sock_for_fork = nil
|
439
|
+
|
440
|
+
if @port_save_file && File.exist?(@port_save_file)
|
441
|
+
File.unlink(@port_save_file)
|
442
|
+
end
|
400
443
|
end
|
401
444
|
end
|
402
445
|
|
@@ -409,11 +452,6 @@ module DEBUGGER__
|
|
409
452
|
super()
|
410
453
|
end
|
411
454
|
|
412
|
-
def vscode_setup
|
413
|
-
require_relative 'server_dap'
|
414
|
-
UI_DAP.setup @sock_path
|
415
|
-
end
|
416
|
-
|
417
455
|
def accept
|
418
456
|
super # for fork
|
419
457
|
|
@@ -426,7 +464,7 @@ module DEBUGGER__
|
|
426
464
|
end
|
427
465
|
|
428
466
|
::DEBUGGER__.warn "Debugger can attach via UNIX domain socket (#{@sock_path})"
|
429
|
-
vscode_setup if CONFIG[:open_frontend] == 'vscode'
|
467
|
+
vscode_setup @sock_path if CONFIG[:open_frontend] == 'vscode'
|
430
468
|
|
431
469
|
begin
|
432
470
|
Socket.unix_server_loop @sock_path do |sock, client|
|