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.
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
- Reline.prompt_proc = nil
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 commands.keys.include?(c = c.strip)
64
- # [:DIM, :CYAN, :BLUE, :CLEAR, :UNDERLINE, :REVERSE, :RED, :GREEN, :MAGENTA, :BOLD, :YELLOW]
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 = (rest ? colorize_code(rest) : '') + rprompt
74
- cmd + rest
75
- when !rest && /\A\s*[a-z]*\z/ =~ c
94
+ rest = rest ? colorize_code(rest) : ''
95
+ cmd + rest + rprompt
96
+ when nil
76
97
  buff
77
- else
78
- colorize_code(buff.chomp) + colorize(" # ruby", [:DIM])
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.chomp
116
+ line.strip
96
117
  end
97
118
  end
98
119
 
@@ -83,14 +83,14 @@ module DEBUGGER__
83
83
 
84
84
  def block_identifier
85
85
  return unless frame_type == :block
86
- args = parameters_info(iseq.argc)
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(iseq.argc)
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(argc)
155
- vars = iseq.locals[0...argc]
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
@@ -98,7 +98,7 @@ module DEBUGGER__
98
98
  # only check child process from its parent
99
99
  begin
100
100
  # wait for all child processes to keep terminal
101
- loop{ Process.waitpid }
101
+ Process.waitpid
102
102
  rescue Errno::ESRCH, Errno::ECHILD
103
103
  end
104
104
  end
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
- cleanup_reader
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
- DEBUGGER__.warn "Disconnected."
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 (#{VERSION} client:#{$1})"
123
+ raise GreetingError, "Incompatible version (server:#{VERSION} and client:#{$1})"
102
124
  end
103
125
 
104
- cookie = CONFIG[:cookie]
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 \/ HTTP\/1.1/
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 "Greeting message error: #{g}"
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
- @port = port || begin
335
- port_str = CONFIG[:port] || raise("Specify listening port by RUBY_DEBUG_PORT environment variable.")
336
- if /\A\d+\z/ !~ port_str
337
- raise "Specify digits for port number"
338
- 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/
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 if CONFIG[:open_frontend] == 'chrome'
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
- chrome_setup if CONFIG[:open_frontend] == 'chrome'
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|