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