debug 1.3.3 → 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.
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,6 +72,9 @@ 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
@@ -76,11 +82,7 @@ module DEBUGGER__
76
82
  pp e.backtrace
77
83
  ensure
78
84
  DEBUGGER__.warn "Disconnected."
79
- @sock = nil
80
- @q_msg.close
81
- @q_msg = nil
82
- @q_ans.close
83
- @q_ans = nil
85
+ cleanup_reader if greeting_done
84
86
  end # accept
85
87
 
86
88
  rescue Terminate
@@ -88,19 +90,40 @@ module DEBUGGER__
88
90
  end
89
91
  end
90
92
 
93
+ def cleanup_reader
94
+ @sock.close if @sock
95
+ @sock = nil
96
+ @q_msg.close
97
+ @q_msg = nil
98
+ @q_ans.close
99
+ @q_ans = nil
100
+ end
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
+
91
109
  def greeting
92
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
+
93
119
  when /^version:\s+(.+)\s+width: (\d+) cookie:\s+(.*)$/
94
120
  v, w, c = $1, $2, $3
95
121
  # TODO: protocol version
96
122
  if v != VERSION
97
- raise "Incompatible version (#{VERSION} client:#{$1})"
123
+ raise GreetingError, "Incompatible version (server:#{VERSION} and client:#{$1})"
98
124
  end
99
125
 
100
- cookie = CONFIG[:cookie]
101
- if cookie && cookie != c
102
- raise "Cookie mismatch (#{$2.inspect} was sent)"
103
- end
126
+ check_cookie c
104
127
 
105
128
  @width = w.to_i
106
129
 
@@ -111,17 +134,17 @@ module DEBUGGER__
111
134
  self.extend(UI_DAP)
112
135
  @repl = false
113
136
  dap_setup @sock.read($1.to_i)
114
- when /^GET \/ HTTP\/1.1/
137
+ when /^GET \/.* HTTP\/1.1/
115
138
  require_relative 'server_cdp'
116
139
 
117
140
  self.extend(UI_CDP)
118
141
  @repl = false
119
142
  CONFIG.set_config no_color: true
120
143
 
121
- @web_sock = UI_CDP::WebSocket.new(@sock)
122
- @web_sock.handshake
144
+ @ws_server = UI_CDP::WebSocketServer.new(@sock)
145
+ @ws_server.handshake
123
146
  else
124
- raise "Greeting message error: #{g}"
147
+ raise GreetingError, "Unknown greeting message: #{g}"
125
148
  end
126
149
  end
127
150
 
@@ -180,7 +203,7 @@ module DEBUGGER__
180
203
 
181
204
  def sigurg_overridden? prev_handler
182
205
  case prev_handler
183
- when "SYSTEM_DEFAULT"
206
+ when "SYSTEM_DEFAULT", "DEFAULT"
184
207
  false
185
208
  when Proc
186
209
  if prev_handler.source_location[0] == __FILE__
@@ -193,10 +216,19 @@ module DEBUGGER__
193
216
  end
194
217
  end
195
218
 
219
+ begin
220
+ prev = trap(:SIGURG, nil)
221
+ trap(:SIGURG, prev)
222
+ TRAP_SIGNAL = :SIGURG
223
+ rescue ArgumentError
224
+ # maybe Windows?
225
+ TRAP_SIGNAL = :SIGINT
226
+ end
227
+
196
228
  def setup_interrupt
197
- prev_handler = trap(:SIGURG) do
229
+ prev_handler = trap(TRAP_SIGNAL) do
198
230
  # $stderr.puts "trapped SIGINT"
199
- ThreadClient.current.on_trap :SIGURG
231
+ ThreadClient.current.on_trap TRAP_SIGNAL
200
232
 
201
233
  case prev_handler
202
234
  when Proc
@@ -211,7 +243,7 @@ module DEBUGGER__
211
243
  end
212
244
  yield
213
245
  ensure
214
- trap(:SIGURG, prev_handler)
246
+ trap(TRAP_SIGNAL, prev_handler)
215
247
  end
216
248
 
217
249
  attr_reader :reader_thread
@@ -299,7 +331,7 @@ module DEBUGGER__
299
331
 
300
332
  def pause
301
333
  # $stderr.puts "DEBUG: pause request"
302
- Process.kill(:SIGURG, Process.pid)
334
+ Process.kill(TRAP_SIGNAL, Process.pid)
303
335
  end
304
336
 
305
337
  def quit n
@@ -312,47 +344,75 @@ module DEBUGGER__
312
344
  def after_fork_parent
313
345
  # do nothing
314
346
  end
347
+
348
+ def vscode_setup debug_port
349
+ require_relative 'server_dap'
350
+ UI_DAP.setup debug_port
351
+ end
315
352
  end
316
353
 
317
354
  class UI_TcpServer < UI_ServerBase
318
355
  def initialize host: nil, port: nil
319
356
  @addr = nil
320
357
  @host = host || CONFIG[:host] || '127.0.0.1'
321
- @port = port || begin
322
- port_str = CONFIG[:port] || raise("Specify listening port by RUBY_DEBUG_PORT environment variable.")
323
- if /\A\d+\z/ !~ port_str
324
- raise "Specify digits for port number"
325
- else
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/
326
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"
327
369
  end
328
370
  end
329
371
 
330
372
  super()
331
373
  end
332
374
 
375
+ def chrome_setup
376
+ require_relative 'server_cdp'
377
+
378
+ unless @chrome_pid = UI_CDP.setup_chrome(@addr)
379
+ DEBUGGER__.warn <<~EOS
380
+ With Chrome browser, type the following URL in the address-bar:
381
+
382
+ devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@addr}/#{SecureRandom.uuid}
383
+
384
+ EOS
385
+ end
386
+ end
387
+
333
388
  def accept
334
389
  retry_cnt = 0
335
390
  super # for fork
336
391
 
337
392
  begin
338
393
  Socket.tcp_server_sockets @host, @port do |socks|
339
- addr = socks[0].local_address.inspect_sockaddr # Change this part if `socks` are multiple.
394
+ @addr = socks[0].local_address.inspect_sockaddr # Change this part if `socks` are multiple.
340
395
  rdbg = File.expand_path('../../exe/rdbg', __dir__)
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
341
402
 
342
- DEBUGGER__.warn "Debugger can attach via TCP/IP (#{addr})"
343
403
  DEBUGGER__.info <<~EOS
344
404
  With rdbg, use the following command line:
345
405
  #
346
- # #{rdbg} --attach #{addr.split(':').join(' ')}
406
+ # #{rdbg} --attach #{@addr.split(':').join(' ')}
347
407
  #
348
408
  EOS
349
409
 
350
- DEBUGGER__.warn <<~EOS if CONFIG[:open_frontend] == 'chrome'
351
- With Chrome browser, type the following URL in the address-bar:
352
-
353
- devtools://devtools/bundled/inspector.html?ws=#{addr}
354
-
355
- EOS
410
+ case CONFIG[:open_frontend]
411
+ when 'chrome'
412
+ chrome_setup
413
+ when 'vscode'
414
+ vscode_setup @addr
415
+ end
356
416
 
357
417
  Socket.accept_loop(socks) do |sock, client|
358
418
  @client_addr = client
@@ -376,6 +436,10 @@ module DEBUGGER__
376
436
  end
377
437
  ensure
378
438
  @sock_for_fork = nil
439
+
440
+ if @port_save_file && File.exist?(@port_save_file)
441
+ File.unlink(@port_save_file)
442
+ end
379
443
  end
380
444
  end
381
445
 
@@ -388,64 +452,6 @@ module DEBUGGER__
388
452
  super()
389
453
  end
390
454
 
391
- def vscode_setup
392
- require 'tmpdir'
393
- require 'json'
394
- require 'fileutils'
395
-
396
- dir = Dir.mktmpdir("ruby-debug-vscode-")
397
- at_exit{
398
- FileUtils.rm_rf dir
399
- }
400
- Dir.chdir(dir) do
401
- Dir.mkdir('.vscode')
402
- open('README.rb', 'w'){|f|
403
- f.puts <<~MSG
404
- # Wait for starting the attaching to the Ruby process
405
- # This file will be removed at the end of the debuggee process.
406
- #
407
- # Note that vscode-rdbg extension is needed. Please install if you don't have.
408
- MSG
409
- }
410
- open('.vscode/launch.json', 'w'){|f|
411
- f.puts JSON.pretty_generate({
412
- version: '0.2.0',
413
- configurations: [
414
- {
415
- type: "rdbg",
416
- name: "Attach with rdbg",
417
- request: "attach",
418
- rdbgPath: File.expand_path('../../exe/rdbg', __dir__),
419
- debugPort: @sock_path,
420
- autoAttach: true,
421
- }
422
- ]
423
- })
424
- }
425
- end
426
-
427
- cmds = ['code', "#{dir}/", "#{dir}/README.rb"]
428
- cmdline = cmds.join(' ')
429
- ssh_cmdline = "code --remote ssh-remote+[SSH hostname] #{dir}/ #{dir}/README.rb"
430
-
431
- STDERR.puts "Launching: #{cmdline}"
432
- env = ENV.delete_if{|k, h| /RUBY/ =~ k}.to_h
433
-
434
- unless system(env, *cmds)
435
- DEBUGGER__.warn <<~MESSAGE
436
- Can not invoke the command.
437
- Use the command-line on your terminal (with modification if you need).
438
-
439
- #{cmdline}
440
-
441
- If your application is running on a SSH remote host, please try:
442
-
443
- #{ssh_cmdline}
444
-
445
- MESSAGE
446
- end
447
- end
448
-
449
455
  def accept
450
456
  super # for fork
451
457
 
@@ -458,7 +464,7 @@ module DEBUGGER__
458
464
  end
459
465
 
460
466
  ::DEBUGGER__.warn "Debugger can attach via UNIX domain socket (#{@sock_path})"
461
- vscode_setup if CONFIG[:open_frontend] == 'vscode'
467
+ vscode_setup @sock_path if CONFIG[:open_frontend] == 'vscode'
462
468
 
463
469
  begin
464
470
  Socket.unix_server_loop @sock_path do |sock, client|