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