debug 1.4.0 → 1.9.2
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 +210 -6
- data/Gemfile +2 -0
- data/LICENSE.txt +0 -0
- data/README.md +161 -85
- data/Rakefile +33 -10
- data/TODO.md +8 -8
- data/debug.gemspec +9 -7
- data/exe/rdbg +23 -4
- data/ext/debug/debug.c +111 -21
- data/ext/debug/extconf.rb +23 -0
- data/ext/debug/iseq_collector.c +2 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +102 -74
- data/lib/debug/client.rb +46 -12
- data/lib/debug/color.rb +0 -0
- data/lib/debug/config.rb +129 -36
- data/lib/debug/console.rb +46 -40
- data/lib/debug/dap_custom/traceInspector.rb +336 -0
- data/lib/debug/frame_info.rb +40 -25
- data/lib/debug/irb_integration.rb +37 -0
- data/lib/debug/local.rb +17 -11
- data/lib/debug/open.rb +0 -0
- data/lib/debug/open_nonstop.rb +0 -0
- data/lib/debug/prelude.rb +3 -2
- data/lib/debug/server.rb +126 -56
- data/lib/debug/server_cdp.rb +673 -248
- data/lib/debug/server_dap.rb +497 -261
- data/lib/debug/session.rb +899 -441
- data/lib/debug/source_repository.rb +122 -49
- data/lib/debug/start.rb +1 -1
- data/lib/debug/thread_client.rb +460 -155
- data/lib/debug/tracer.rb +10 -16
- data/lib/debug/version.rb +1 -1
- data/lib/debug.rb +7 -2
- data/misc/README.md.erb +106 -56
- metadata +14 -24
- 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/bp.vim +0 -68
data/lib/debug/server.rb
CHANGED
@@ -19,8 +19,9 @@ module DEBUGGER__
|
|
19
19
|
@session = nil
|
20
20
|
end
|
21
21
|
|
22
|
-
class Terminate < StandardError
|
23
|
-
end
|
22
|
+
class Terminate < StandardError; end
|
23
|
+
class GreetingError < StandardError; end
|
24
|
+
class RetryConnection < StandardError; end
|
24
25
|
|
25
26
|
def deactivate
|
26
27
|
@reader_thread.raise Terminate
|
@@ -47,10 +48,13 @@ module DEBUGGER__
|
|
47
48
|
|
48
49
|
accept do |server, already_connected: false|
|
49
50
|
DEBUGGER__.warn "Connected."
|
51
|
+
greeting_done = false
|
52
|
+
@need_pause_at_first = true
|
50
53
|
|
51
54
|
@accept_m.synchronize{
|
52
55
|
@sock = server
|
53
56
|
greeting
|
57
|
+
greeting_done = true
|
54
58
|
|
55
59
|
@accept_cv.signal
|
56
60
|
|
@@ -65,17 +69,23 @@ module DEBUGGER__
|
|
65
69
|
} unless already_connected
|
66
70
|
|
67
71
|
setup_interrupt do
|
68
|
-
pause
|
72
|
+
pause if !already_connected && @need_pause_at_first
|
69
73
|
process
|
70
74
|
end
|
71
75
|
|
76
|
+
rescue GreetingError => e
|
77
|
+
DEBUGGER__.warn "GreetingError: #{e.message}"
|
78
|
+
next
|
72
79
|
rescue Terminate
|
73
80
|
raise # should catch at outer scope
|
81
|
+
rescue RetryConnection
|
82
|
+
next
|
74
83
|
rescue => e
|
75
84
|
DEBUGGER__.warn "ReaderThreadError: #{e}"
|
76
85
|
pp e.backtrace
|
77
86
|
ensure
|
78
|
-
|
87
|
+
DEBUGGER__.warn "Disconnected."
|
88
|
+
cleanup_reader if greeting_done
|
79
89
|
end # accept
|
80
90
|
|
81
91
|
rescue Terminate
|
@@ -84,7 +94,7 @@ module DEBUGGER__
|
|
84
94
|
end
|
85
95
|
|
86
96
|
def cleanup_reader
|
87
|
-
|
97
|
+
@sock.close if @sock
|
88
98
|
@sock = nil
|
89
99
|
@q_msg.close
|
90
100
|
@q_msg = nil
|
@@ -92,21 +102,58 @@ module DEBUGGER__
|
|
92
102
|
@q_ans = nil
|
93
103
|
end
|
94
104
|
|
105
|
+
def check_cookie c
|
106
|
+
cookie = CONFIG[:cookie]
|
107
|
+
if cookie && cookie != c
|
108
|
+
raise GreetingError, "Cookie mismatch (#{$2.inspect} was sent)"
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def parse_option params
|
113
|
+
case params.strip
|
114
|
+
when /width:\s+(\d+)/
|
115
|
+
@width = $1.to_i
|
116
|
+
parse_option $~.post_match
|
117
|
+
when /cookie:\s+(\S+)/
|
118
|
+
check_cookie $1 if $1 != '-'
|
119
|
+
parse_option $~.post_match
|
120
|
+
when /nonstop: (true|false)/
|
121
|
+
@need_pause_at_first = false if $1 == 'true'
|
122
|
+
parse_option $~.post_match
|
123
|
+
when /(.+):(.+)/
|
124
|
+
raise GreetingError, "Unkown option: #{params}"
|
125
|
+
else
|
126
|
+
# OK
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
95
130
|
def greeting
|
96
131
|
case g = @sock.gets
|
97
|
-
when /^
|
98
|
-
|
132
|
+
when /^info cookie:\s+(.*)$/
|
133
|
+
require 'etc'
|
134
|
+
|
135
|
+
check_cookie $1
|
136
|
+
@sock.puts "PID: #{Process.pid}, $0: #{$0}, session_name: #{CONFIG[:session_name]}"
|
137
|
+
@sock.puts "debug #{VERSION} on #{RUBY_DESCRIPTION}"
|
138
|
+
@sock.puts "uname: #{Etc.uname.inspect}"
|
139
|
+
@sock.close
|
140
|
+
raise GreetingError, "HEAD request"
|
141
|
+
|
142
|
+
when /^version:\s+(\S+)\s+(.+)$/
|
143
|
+
v, params = $1, $2
|
144
|
+
|
99
145
|
# TODO: protocol version
|
100
146
|
if v != VERSION
|
101
|
-
|
147
|
+
@sock.puts msg = "out DEBUGGER: Incompatible version (server:#{VERSION} and client:#{$1})"
|
148
|
+
raise GreetingError, msg
|
102
149
|
end
|
150
|
+
parse_option(params)
|
103
151
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
@width = w.to_i
|
152
|
+
session_name = CONFIG[:session_name]
|
153
|
+
session_name_str = ", session_name:#{session_name}" if session_name
|
154
|
+
puts "DEBUGGER (client): Connected. PID:#{Process.pid}, $0:#{$0}#{session_name_str}"
|
155
|
+
puts "DEBUGGER (client): Type `Ctrl-C` to enter the debug console." unless @need_pause_at_first
|
156
|
+
puts
|
110
157
|
|
111
158
|
when /^Content-Length: (\d+)/
|
112
159
|
require_relative 'server_dap'
|
@@ -114,37 +161,38 @@ module DEBUGGER__
|
|
114
161
|
raise unless @sock.read(2) == "\r\n"
|
115
162
|
self.extend(UI_DAP)
|
116
163
|
@repl = false
|
164
|
+
@need_pause_at_first = false
|
117
165
|
dap_setup @sock.read($1.to_i)
|
118
|
-
|
166
|
+
|
167
|
+
when /^GET\s\/json\sHTTP\/1.1/, /^GET\s\/json\/version\sHTTP\/1.1/, /^GET\s\/\w{8}-\w{4}-\w{4}-\w{4}-\w{12}\sHTTP\/1.1/
|
168
|
+
# The reason for not using @uuid here is @uuid is nil if users run debugger without `--open=chrome`.
|
169
|
+
|
119
170
|
require_relative 'server_cdp'
|
120
171
|
|
121
172
|
self.extend(UI_CDP)
|
122
|
-
|
123
|
-
CONFIG.set_config no_color: true
|
124
|
-
|
125
|
-
@ws_server = UI_CDP::WebSocketServer.new(@sock)
|
126
|
-
@ws_server.handshake
|
173
|
+
send_chrome_response g
|
127
174
|
else
|
128
|
-
raise "
|
175
|
+
raise GreetingError, "Unknown greeting message: #{g}"
|
129
176
|
end
|
130
177
|
end
|
131
178
|
|
132
179
|
def process
|
133
180
|
while true
|
134
|
-
DEBUGGER__.
|
135
|
-
|
136
|
-
DEBUGGER__.
|
181
|
+
DEBUGGER__.debug{ "sleep IO.select" }
|
182
|
+
_r = IO.select([@sock])
|
183
|
+
DEBUGGER__.debug{ "wakeup IO.select" }
|
137
184
|
|
138
185
|
line = @session.process_group.sync do
|
139
186
|
unless IO.select([@sock], nil, nil, 0)
|
140
|
-
DEBUGGER__.
|
187
|
+
DEBUGGER__.debug{ "UI_Server can not read" }
|
141
188
|
break :can_not_read
|
142
189
|
end
|
143
190
|
@sock.gets&.chomp.tap{|line|
|
144
|
-
DEBUGGER__.
|
191
|
+
DEBUGGER__.debug{ "UI_Server received: #{line}" }
|
145
192
|
}
|
146
193
|
end
|
147
194
|
|
195
|
+
return unless line
|
148
196
|
next if line == :can_not_read
|
149
197
|
|
150
198
|
case line
|
@@ -168,7 +216,7 @@ module DEBUGGER__
|
|
168
216
|
raise "pid:#{Process.pid} but get #{line}"
|
169
217
|
end
|
170
218
|
else
|
171
|
-
STDERR.puts "unsupported: #{line}"
|
219
|
+
STDERR.puts "unsupported: #{line.inspect}"
|
172
220
|
exit!
|
173
221
|
end
|
174
222
|
end
|
@@ -290,16 +338,18 @@ module DEBUGGER__
|
|
290
338
|
end
|
291
339
|
|
292
340
|
def readline prompt
|
293
|
-
input = (sock do |s|
|
341
|
+
input = (sock(skip: CONFIG[:skip_bp]) do |s|
|
342
|
+
next unless s
|
343
|
+
|
294
344
|
if @repl
|
295
345
|
raise "not in subsession, but received: #{line.inspect}" unless @session.in_subsession?
|
296
346
|
line = "input #{Process.pid}"
|
297
|
-
DEBUGGER__.
|
347
|
+
DEBUGGER__.debug{ "send: #{line}" }
|
298
348
|
s.puts line
|
299
349
|
end
|
300
350
|
sleep 0.01 until @q_msg
|
301
351
|
@q_msg.pop.tap{|msg|
|
302
|
-
DEBUGGER__.
|
352
|
+
DEBUGGER__.debug{ "readline: #{msg.inspect}" }
|
303
353
|
}
|
304
354
|
end || 'continue')
|
305
355
|
|
@@ -315,7 +365,7 @@ module DEBUGGER__
|
|
315
365
|
Process.kill(TRAP_SIGNAL, Process.pid)
|
316
366
|
end
|
317
367
|
|
318
|
-
def quit n
|
368
|
+
def quit n, &_b
|
319
369
|
# ignore n
|
320
370
|
sock do |s|
|
321
371
|
s.puts "quit"
|
@@ -325,20 +375,31 @@ module DEBUGGER__
|
|
325
375
|
def after_fork_parent
|
326
376
|
# do nothing
|
327
377
|
end
|
378
|
+
|
379
|
+
def vscode_setup debug_port
|
380
|
+
require_relative 'server_dap'
|
381
|
+
UI_DAP.setup debug_port
|
382
|
+
end
|
328
383
|
end
|
329
384
|
|
330
385
|
class UI_TcpServer < UI_ServerBase
|
331
386
|
def initialize host: nil, port: nil
|
332
|
-
@
|
333
|
-
@host = host || CONFIG[:host]
|
334
|
-
@
|
335
|
-
|
336
|
-
|
337
|
-
|
338
|
-
|
387
|
+
@local_addr = nil
|
388
|
+
@host = host || CONFIG[:host]
|
389
|
+
@port_save_file = nil
|
390
|
+
@port = begin
|
391
|
+
port_str = (port && port.to_s) || CONFIG[:port] || raise("Specify listening port by RUBY_DEBUG_PORT environment variable.")
|
392
|
+
case port_str
|
393
|
+
when /\A\d+\z/
|
339
394
|
port_str.to_i
|
395
|
+
when /\A(\d+):(.+)\z/
|
396
|
+
@port_save_file = $2
|
397
|
+
$1.to_i
|
398
|
+
else
|
399
|
+
raise "Specify digits for port number"
|
340
400
|
end
|
341
401
|
end
|
402
|
+
@uuid = nil # for CDP
|
342
403
|
|
343
404
|
super()
|
344
405
|
end
|
@@ -346,14 +407,14 @@ module DEBUGGER__
|
|
346
407
|
def chrome_setup
|
347
408
|
require_relative 'server_cdp'
|
348
409
|
|
349
|
-
|
350
|
-
|
351
|
-
|
352
|
-
|
353
|
-
|
354
|
-
|
355
|
-
|
356
|
-
|
410
|
+
@uuid = SecureRandom.uuid
|
411
|
+
@chrome_pid = UI_CDP.setup_chrome(@local_addr.inspect_sockaddr, @uuid)
|
412
|
+
DEBUGGER__.warn <<~EOS
|
413
|
+
With Chrome browser, type the following URL in the address-bar:
|
414
|
+
|
415
|
+
devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&noJavaScriptCompletion=true&ws=#{@local_addr.inspect_sockaddr}/#{@uuid}
|
416
|
+
|
417
|
+
EOS
|
357
418
|
end
|
358
419
|
|
359
420
|
def accept
|
@@ -362,18 +423,28 @@ module DEBUGGER__
|
|
362
423
|
|
363
424
|
begin
|
364
425
|
Socket.tcp_server_sockets @host, @port do |socks|
|
365
|
-
@
|
426
|
+
@local_addr = socks.first.local_address # Change this part if `socks` are multiple.
|
366
427
|
rdbg = File.expand_path('../../exe/rdbg', __dir__)
|
428
|
+
DEBUGGER__.warn "Debugger can attach via TCP/IP (#{@local_addr.inspect_sockaddr})"
|
429
|
+
|
430
|
+
if @port_save_file
|
431
|
+
File.write(@port_save_file, "#{socks[0].local_address.ip_port.to_s}\n")
|
432
|
+
DEBUGGER__.warn "Port is saved into #{@port_save_file}"
|
433
|
+
end
|
367
434
|
|
368
|
-
DEBUGGER__.warn "Debugger can attach via TCP/IP (#{@addr})"
|
369
435
|
DEBUGGER__.info <<~EOS
|
370
436
|
With rdbg, use the following command line:
|
371
437
|
#
|
372
|
-
# #{rdbg} --attach #{@
|
438
|
+
# #{rdbg} --attach #{@local_addr.ip_address} #{@local_addr.ip_port}
|
373
439
|
#
|
374
440
|
EOS
|
375
441
|
|
376
|
-
|
442
|
+
case CONFIG[:open]
|
443
|
+
when 'chrome'
|
444
|
+
chrome_setup
|
445
|
+
when 'vscode'
|
446
|
+
vscode_setup @local_addr.inspect_sockaddr
|
447
|
+
end
|
377
448
|
|
378
449
|
Socket.accept_loop(socks) do |sock, client|
|
379
450
|
@client_addr = client
|
@@ -397,6 +468,10 @@ module DEBUGGER__
|
|
397
468
|
end
|
398
469
|
ensure
|
399
470
|
@sock_for_fork = nil
|
471
|
+
|
472
|
+
if @port_save_file && File.exist?(@port_save_file)
|
473
|
+
File.unlink(@port_save_file)
|
474
|
+
end
|
400
475
|
end
|
401
476
|
end
|
402
477
|
|
@@ -409,11 +484,6 @@ module DEBUGGER__
|
|
409
484
|
super()
|
410
485
|
end
|
411
486
|
|
412
|
-
def vscode_setup
|
413
|
-
require_relative 'server_dap'
|
414
|
-
UI_DAP.setup @sock_path
|
415
|
-
end
|
416
|
-
|
417
487
|
def accept
|
418
488
|
super # for fork
|
419
489
|
|
@@ -426,7 +496,7 @@ module DEBUGGER__
|
|
426
496
|
end
|
427
497
|
|
428
498
|
::DEBUGGER__.warn "Debugger can attach via UNIX domain socket (#{@sock_path})"
|
429
|
-
vscode_setup if CONFIG[:
|
499
|
+
vscode_setup @sock_path if CONFIG[:open] == 'vscode'
|
430
500
|
|
431
501
|
begin
|
432
502
|
Socket.unix_server_loop @sock_path do |sock, client|
|