debug 1.4.0 → 1.5.0

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