debug 1.7.1 → 1.7.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 +2 -2
- data/README.md +3 -3
- data/Rakefile +8 -3
- data/lib/debug/breakpoint.rb +6 -8
- data/lib/debug/config.rb +23 -2
- data/lib/debug/server.rb +5 -6
- data/lib/debug/server_cdp.rb +60 -59
- data/lib/debug/server_dap.rb +43 -26
- data/lib/debug/session.rb +55 -42
- data/lib/debug/source_repository.rb +2 -2
- data/lib/debug/thread_client.rb +9 -1
- data/lib/debug/tracer.rb +4 -5
- data/lib/debug/version.rb +1 -1
- data/misc/README.md.erb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 1bac91980fb350e0e37a0639612064bba25a8e441661a6d797e0d9f996ce0205
|
4
|
+
data.tar.gz: 3294757150ec0731ccadd0c26a57c8e920c0479e4a967b4900e999dae4afcf9c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7a0259c1f8fa904d9a425be5035e2642de89e69cd81cc61bd81bae2e9e7b17dbd18a78addce9e5ed8e3c7723417c0de1113a26922131760e490e77e91bb8493d
|
7
|
+
data.tar.gz: fd55333ccf690b4338729a2b84d55a21464ba2de71bdf5edcb0b82f309594477333224eb3be0a89c6b7c477042c4f904f8b743f74bba3931f6db4de7a9ff7dd9
|
data/CONTRIBUTING.md
CHANGED
@@ -149,10 +149,10 @@ If the file already exists, **only method** will be added to it.
|
|
149
149
|
```ruby
|
150
150
|
# frozen_string_literal: true
|
151
151
|
|
152
|
-
require_relative '../support/
|
152
|
+
require_relative '../support/console_test_case'
|
153
153
|
|
154
154
|
module DEBUGGER__
|
155
|
-
class FooTest <
|
155
|
+
class FooTest < ConsoleTestCase
|
156
156
|
def program
|
157
157
|
<<~RUBY
|
158
158
|
1| module Foo
|
data/README.md
CHANGED
@@ -26,7 +26,7 @@ New debug.rb has several advantages:
|
|
26
26
|
* Support threads (almost done) and ractors (TODO).
|
27
27
|
* Support suspending and entering to the console debugging with `Ctrl-C` at most of timing.
|
28
28
|
* Show parameters on backtrace command.
|
29
|
-
* Support recording &
|
29
|
+
* Support recording & replay debugging.
|
30
30
|
|
31
31
|
# Installation
|
32
32
|
|
@@ -563,9 +563,9 @@ The `<...>` notation means the argument.
|
|
563
563
|
* `u[ntil]`
|
564
564
|
* Similar to `next` command, but only stop later lines or the end of the current frame.
|
565
565
|
* Similar to gdb's `advance` command.
|
566
|
-
* `u[ntil] <[file:]line
|
566
|
+
* `u[ntil] <[file:]line>`
|
567
567
|
* Run til the program reaches given location or the end of the current frame.
|
568
|
-
* `u[ntil] <name
|
568
|
+
* `u[ntil] <name>`
|
569
569
|
* Run til the program invokes a method `<name>`. `<name>` can be a regexp with `/name/`.
|
570
570
|
* `c` or `cont` or `continue`
|
571
571
|
* Resume the program.
|
data/Rakefile
CHANGED
@@ -35,9 +35,14 @@ task :check_readme do
|
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
+
desc "Run debug.gem test-framework tests"
|
39
|
+
Rake::TestTask.new(:test_test) do |t|
|
40
|
+
t.test_files = FileList["test/support/*_test.rb"]
|
41
|
+
end
|
42
|
+
|
38
43
|
desc "Run all debugger console related tests"
|
39
44
|
Rake::TestTask.new(:test_console) do |t|
|
40
|
-
t.test_files = FileList["test/console/*_test.rb"
|
45
|
+
t.test_files = FileList["test/console/*_test.rb"]
|
41
46
|
end
|
42
47
|
|
43
48
|
desc "Run all debugger protocols (CAP & DAP) related tests"
|
@@ -46,7 +51,7 @@ Rake::TestTask.new(:test_protocol) do |t|
|
|
46
51
|
end
|
47
52
|
|
48
53
|
task test: 'test_console' do
|
49
|
-
warn '`rake test` doesn\'t run protocol tests. Use `rake
|
54
|
+
warn '`rake test` doesn\'t run protocol tests. Use `rake test_all` to test all.'
|
50
55
|
end
|
51
56
|
|
52
|
-
task test_all: [:test_console, :test_protocol]
|
57
|
+
task test_all: [:test_test, :test_console, :test_protocol]
|
data/lib/debug/breakpoint.rb
CHANGED
@@ -101,10 +101,6 @@ module DEBUGGER__
|
|
101
101
|
def generate_label(name)
|
102
102
|
colorize(" BP - #{name} ", [:YELLOW, :BOLD, :REVERSE])
|
103
103
|
end
|
104
|
-
|
105
|
-
def pending_until_load?
|
106
|
-
false
|
107
|
-
end
|
108
104
|
end
|
109
105
|
|
110
106
|
if RUBY_VERSION.to_f <= 2.7
|
@@ -163,10 +159,6 @@ module DEBUGGER__
|
|
163
159
|
@pending = !@iseq
|
164
160
|
end
|
165
161
|
|
166
|
-
def pending_until_load?
|
167
|
-
@pending
|
168
|
-
end
|
169
|
-
|
170
162
|
def setup
|
171
163
|
return unless @type
|
172
164
|
|
@@ -207,6 +199,8 @@ module DEBUGGER__
|
|
207
199
|
if @pending && !@oneshot
|
208
200
|
DEBUGGER__.info "#{self} is activated."
|
209
201
|
end
|
202
|
+
|
203
|
+
@pending = false
|
210
204
|
end
|
211
205
|
|
212
206
|
def activate_exact iseq, events, line
|
@@ -299,6 +293,10 @@ module DEBUGGER__
|
|
299
293
|
def inspect
|
300
294
|
"<#{self.class.name} #{self.to_s}>"
|
301
295
|
end
|
296
|
+
|
297
|
+
def path_is? path
|
298
|
+
DEBUGGER__.compare_path(@path, path)
|
299
|
+
end
|
302
300
|
end
|
303
301
|
|
304
302
|
class CatchBreakpoint < Breakpoint
|
data/lib/debug/config.rb
CHANGED
@@ -265,6 +265,8 @@ module DEBUGGER__
|
|
265
265
|
require 'optparse'
|
266
266
|
require_relative 'version'
|
267
267
|
|
268
|
+
have_shown_version = false
|
269
|
+
|
268
270
|
opt = OptionParser.new do |o|
|
269
271
|
o.banner = "#{$0} [options] -- [debuggee options]"
|
270
272
|
o.separator ''
|
@@ -372,6 +374,16 @@ module DEBUGGER__
|
|
372
374
|
o.separator ''
|
373
375
|
o.separator 'Other options:'
|
374
376
|
|
377
|
+
o.on('-v', 'Show version number') do
|
378
|
+
puts o.ver
|
379
|
+
have_shown_version = true
|
380
|
+
end
|
381
|
+
|
382
|
+
o.on('--version', 'Show version number and exit') do
|
383
|
+
puts o.ver
|
384
|
+
exit
|
385
|
+
end
|
386
|
+
|
375
387
|
o.on("-h", "--help", "Print help") do
|
376
388
|
puts o
|
377
389
|
exit
|
@@ -395,6 +407,14 @@ module DEBUGGER__
|
|
395
407
|
|
396
408
|
opt.parse!(argv)
|
397
409
|
|
410
|
+
if argv.empty?
|
411
|
+
case
|
412
|
+
when have_shown_version && config[:mode] == :start
|
413
|
+
pp config
|
414
|
+
exit
|
415
|
+
end
|
416
|
+
end
|
417
|
+
|
398
418
|
config
|
399
419
|
end
|
400
420
|
|
@@ -425,8 +445,9 @@ module DEBUGGER__
|
|
425
445
|
unless (dir_uid = fs.uid) == (uid = Process.uid)
|
426
446
|
raise "#{path} uid is #{dir_uid}, but Process.uid is #{uid}"
|
427
447
|
end
|
428
|
-
|
429
|
-
|
448
|
+
|
449
|
+
if fs.world_writable? && !fs.sticky?
|
450
|
+
raise "#{path} is world writable but not sticky"
|
430
451
|
end
|
431
452
|
|
432
453
|
path
|
data/lib/debug/server.rb
CHANGED
@@ -406,14 +406,13 @@ module DEBUGGER__
|
|
406
406
|
require_relative 'server_cdp'
|
407
407
|
|
408
408
|
@uuid = SecureRandom.uuid
|
409
|
-
|
410
|
-
|
411
|
-
|
409
|
+
@chrome_pid = UI_CDP.setup_chrome(@local_addr.inspect_sockaddr, @uuid)
|
410
|
+
DEBUGGER__.warn <<~EOS
|
411
|
+
With Chrome browser, type the following URL in the address-bar:
|
412
412
|
|
413
|
-
|
413
|
+
devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{@local_addr.inspect_sockaddr}/#{@uuid}
|
414
414
|
|
415
|
-
|
416
|
-
end
|
415
|
+
EOS
|
417
416
|
end
|
418
417
|
|
419
418
|
def accept
|
data/lib/debug/server_cdp.rb
CHANGED
@@ -98,7 +98,6 @@ module DEBUGGER__
|
|
98
98
|
candidates = ['C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe']
|
99
99
|
path = get_chrome_path candidates
|
100
100
|
end
|
101
|
-
uuid = SecureRandom.uuid
|
102
101
|
# The path is based on https://github.com/sindresorhus/open/blob/v8.4.0/index.js#L128.
|
103
102
|
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{ENV['SystemRoot']}\\System32\\WindowsPowerShell\\v1.0\\powershell")
|
104
103
|
tf = Tempfile.create(['debug-', '.txt'])
|
@@ -450,11 +449,7 @@ module DEBUGGER__
|
|
450
449
|
end
|
451
450
|
|
452
451
|
def send_response req, **res
|
453
|
-
|
454
|
-
@ws_server.send id: req['id'], result: {}
|
455
|
-
else
|
456
|
-
@ws_server.send id: req['id'], result: res
|
457
|
-
end
|
452
|
+
@ws_server.send id: req['id'], result: res
|
458
453
|
end
|
459
454
|
|
460
455
|
def send_fail_response req, **res
|
@@ -462,11 +457,7 @@ module DEBUGGER__
|
|
462
457
|
end
|
463
458
|
|
464
459
|
def send_event method, **params
|
465
|
-
|
466
|
-
@ws_server.send method: method, params: {}
|
467
|
-
else
|
468
|
-
@ws_server.send method: method, params: params
|
469
|
-
end
|
460
|
+
@ws_server.send method: method, params: params
|
470
461
|
end
|
471
462
|
|
472
463
|
INVALID_REQUEST = -32600
|
@@ -557,6 +548,9 @@ module DEBUGGER__
|
|
557
548
|
activate_bp bps
|
558
549
|
end
|
559
550
|
send_response req
|
551
|
+
when 'Debugger.pause'
|
552
|
+
send_response req
|
553
|
+
Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
|
560
554
|
|
561
555
|
# breakpoint
|
562
556
|
when 'Debugger.getPossibleBreakpoints'
|
@@ -564,35 +558,31 @@ module DEBUGGER__
|
|
564
558
|
when 'Debugger.setBreakpointByUrl'
|
565
559
|
line = req.dig('params', 'lineNumber')
|
566
560
|
if regexp = req.dig('params', 'urlRegex')
|
567
|
-
path = regexp.match(/(.*)\|/)[1].gsub("\\", "")
|
568
|
-
cond = req.dig('params', 'condition')
|
569
|
-
src = get_source_code path
|
570
|
-
end_line = src.lines.count
|
571
|
-
line = end_line if line > end_line
|
572
561
|
b_id = "1:#{line}:#{regexp}"
|
573
|
-
if cond != ''
|
574
|
-
SESSION.add_line_breakpoint(path, line + 1, cond: cond)
|
575
|
-
else
|
576
|
-
SESSION.add_line_breakpoint(path, line + 1)
|
577
|
-
end
|
578
562
|
bps[b_id] = bps.size
|
579
|
-
|
580
|
-
req
|
581
|
-
req['params']['lineNumber'] = line
|
582
|
-
req['params']['breakpointId'] = b_id
|
583
|
-
@q_msg << req
|
563
|
+
path = regexp.match(/(.*)\|/)[1].gsub("\\", "")
|
564
|
+
add_line_breakpoint(req, b_id, path)
|
584
565
|
elsif url = req.dig('params', 'url')
|
585
566
|
b_id = "#{line}:#{url}"
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
591
|
-
|
592
|
-
|
593
|
-
|
567
|
+
# When breakpoints are set in Script snippet, non-existent path such as "snippet:///Script%20snippet%20%231" sent.
|
568
|
+
# That's why we need to check it here.
|
569
|
+
if File.exist? url
|
570
|
+
bps[b_id] = bps.size
|
571
|
+
add_line_breakpoint(req, b_id, url)
|
572
|
+
else
|
573
|
+
send_response req,
|
574
|
+
breakpointId: b_id,
|
575
|
+
locations: []
|
576
|
+
end
|
594
577
|
else
|
595
|
-
|
578
|
+
if hash = req.dig('params', 'scriptHash')
|
579
|
+
b_id = "#{line}:#{hash}"
|
580
|
+
send_response req,
|
581
|
+
breakpointId: b_id,
|
582
|
+
locations: []
|
583
|
+
else
|
584
|
+
raise 'Unsupported'
|
585
|
+
end
|
596
586
|
end
|
597
587
|
when 'Debugger.removeBreakpoint'
|
598
588
|
b_id = req.dig('params', 'breakpointId')
|
@@ -631,6 +621,24 @@ module DEBUGGER__
|
|
631
621
|
@q_msg << 'continue'
|
632
622
|
end
|
633
623
|
|
624
|
+
def add_line_breakpoint req, b_id, path
|
625
|
+
cond = req.dig('params', 'condition')
|
626
|
+
line = req.dig('params', 'lineNumber')
|
627
|
+
src = get_source_code path
|
628
|
+
end_line = src.lines.count
|
629
|
+
line = end_line if line > end_line
|
630
|
+
if cond != ''
|
631
|
+
SESSION.add_line_breakpoint(path, line + 1, cond: cond)
|
632
|
+
else
|
633
|
+
SESSION.add_line_breakpoint(path, line + 1)
|
634
|
+
end
|
635
|
+
# Because we need to return scriptId, responses are returned in SESSION thread.
|
636
|
+
req['params']['scriptId'] = path
|
637
|
+
req['params']['lineNumber'] = line
|
638
|
+
req['params']['breakpointId'] = b_id
|
639
|
+
@q_msg << req
|
640
|
+
end
|
641
|
+
|
634
642
|
def del_bp bps, k
|
635
643
|
return bps unless idx = bps[k]
|
636
644
|
|
@@ -668,31 +676,20 @@ module DEBUGGER__
|
|
668
676
|
def cleanup_reader
|
669
677
|
super
|
670
678
|
Process.kill :KILL, @chrome_pid if @chrome_pid
|
679
|
+
rescue Errno::ESRCH # continue if @chrome_pid process is not found
|
671
680
|
end
|
672
681
|
|
673
682
|
## Called by the SESSION thread
|
674
683
|
|
675
|
-
|
676
|
-
|
677
|
-
|
678
|
-
|
679
|
-
def respond_fail req, **result
|
680
|
-
send_fail_response req, **result
|
681
|
-
end
|
682
|
-
|
683
|
-
def fire_event event, **result
|
684
|
-
if result.empty?
|
685
|
-
send_event event
|
686
|
-
else
|
687
|
-
send_event event, **result
|
688
|
-
end
|
689
|
-
end
|
684
|
+
alias respond send_response
|
685
|
+
alias respond_fail send_fail_response
|
686
|
+
alias fire_event send_event
|
690
687
|
|
691
688
|
def sock skip: false
|
692
689
|
yield $stderr
|
693
690
|
end
|
694
691
|
|
695
|
-
def puts result
|
692
|
+
def puts result=''
|
696
693
|
# STDERR.puts "puts: #{result}"
|
697
694
|
# send_event 'output', category: 'stderr', output: "PUTS!!: " + result.to_s
|
698
695
|
end
|
@@ -756,7 +753,11 @@ module DEBUGGER__
|
|
756
753
|
request_tc [:cdp, :scope, req, fid]
|
757
754
|
when 'global'
|
758
755
|
vars = safe_global_variables.sort.map do |name|
|
759
|
-
|
756
|
+
begin
|
757
|
+
gv = eval(name.to_s)
|
758
|
+
rescue Errno::ENOENT
|
759
|
+
gv = nil
|
760
|
+
end
|
760
761
|
prop = {
|
761
762
|
name: name,
|
762
763
|
value: {
|
@@ -836,7 +837,7 @@ module DEBUGGER__
|
|
836
837
|
end
|
837
838
|
end
|
838
839
|
|
839
|
-
def
|
840
|
+
def process_protocol_result args
|
840
841
|
type, req, result = args
|
841
842
|
|
842
843
|
case type
|
@@ -1026,7 +1027,7 @@ module DEBUGGER__
|
|
1026
1027
|
result[:data] = evaluate_result exception
|
1027
1028
|
result[:reason] = 'exception'
|
1028
1029
|
end
|
1029
|
-
event! :
|
1030
|
+
event! :protocol_result, :backtrace, req, result
|
1030
1031
|
when :evaluate
|
1031
1032
|
res = {}
|
1032
1033
|
fid, expr, group = args
|
@@ -1071,7 +1072,7 @@ module DEBUGGER__
|
|
1071
1072
|
begin
|
1072
1073
|
orig_stdout = $stdout
|
1073
1074
|
$stdout = StringIO.new
|
1074
|
-
result =
|
1075
|
+
result = b.eval(expr.to_s, '(DEBUG CONSOLE)')
|
1075
1076
|
rescue Exception => e
|
1076
1077
|
result = e
|
1077
1078
|
res[:exceptionDetails] = exceptionDetails(e, 'Uncaught')
|
@@ -1087,7 +1088,7 @@ module DEBUGGER__
|
|
1087
1088
|
end
|
1088
1089
|
|
1089
1090
|
res[:result] = evaluate_result(result)
|
1090
|
-
event! :
|
1091
|
+
event! :protocol_result, :evaluate, req, message: message, response: res, output: output
|
1091
1092
|
when :scope
|
1092
1093
|
fid = args.shift
|
1093
1094
|
frame = @target_frames[fid]
|
@@ -1110,7 +1111,7 @@ module DEBUGGER__
|
|
1110
1111
|
vars.unshift variable(name, val)
|
1111
1112
|
end
|
1112
1113
|
end
|
1113
|
-
event! :
|
1114
|
+
event! :protocol_result, :scope, req, vars
|
1114
1115
|
when :properties
|
1115
1116
|
oid = args.shift
|
1116
1117
|
result = []
|
@@ -1152,14 +1153,14 @@ module DEBUGGER__
|
|
1152
1153
|
}
|
1153
1154
|
prop += [internalProperty('#class', M_CLASS.bind_call(obj))]
|
1154
1155
|
end
|
1155
|
-
event! :
|
1156
|
+
event! :protocol_result, :properties, req, result: result, internalProperties: prop
|
1156
1157
|
when :exception
|
1157
1158
|
oid = args.shift
|
1158
1159
|
exc = nil
|
1159
1160
|
if obj = @obj_map[oid]
|
1160
1161
|
exc = exceptionDetails obj, obj.to_s
|
1161
1162
|
end
|
1162
|
-
event! :
|
1163
|
+
event! :protocol_result, :exception, req, exceptionDetails: exc
|
1163
1164
|
end
|
1164
1165
|
end
|
1165
1166
|
|
data/lib/debug/server_dap.rb
CHANGED
@@ -125,6 +125,7 @@ module DEBUGGER__
|
|
125
125
|
def dap_setup bytes
|
126
126
|
CONFIG.set_config no_color: true
|
127
127
|
@seq = 0
|
128
|
+
@send_lock = Mutex.new
|
128
129
|
|
129
130
|
case self
|
130
131
|
when UI_UnixDomainServer
|
@@ -212,9 +213,13 @@ module DEBUGGER__
|
|
212
213
|
if sock = @sock
|
213
214
|
kw[:seq] = @seq += 1
|
214
215
|
str = JSON.dump(kw)
|
215
|
-
|
216
|
+
@send_lock.synchronize do
|
217
|
+
sock.write "Content-Length: #{str.bytesize}\r\n\r\n#{str}"
|
218
|
+
end
|
216
219
|
show_protocol '<', str
|
217
220
|
end
|
221
|
+
rescue Errno::EPIPE => e
|
222
|
+
$stderr.puts "#{e.inspect} rescued during sending message"
|
218
223
|
end
|
219
224
|
|
220
225
|
def send_response req, success: true, message: nil, **kw
|
@@ -246,17 +251,17 @@ module DEBUGGER__
|
|
246
251
|
end
|
247
252
|
|
248
253
|
def recv_request
|
249
|
-
|
254
|
+
IO.select([@sock])
|
250
255
|
|
251
256
|
@session.process_group.sync do
|
252
257
|
raise RetryBecauseCantRead unless IO.select([@sock], nil, nil, 0)
|
253
258
|
|
254
|
-
case
|
259
|
+
case @sock.gets
|
255
260
|
when /Content-Length: (\d+)/
|
256
261
|
b = @sock.read(2)
|
257
262
|
raise b.inspect unless b == "\r\n"
|
258
263
|
|
259
|
-
l = @sock.read(
|
264
|
+
l = @sock.read($1.to_i)
|
260
265
|
show_protocol :>, l
|
261
266
|
JSON.load(l)
|
262
267
|
when nil
|
@@ -309,7 +314,6 @@ module DEBUGGER__
|
|
309
314
|
when 'setBreakpoints'
|
310
315
|
req_path = args.dig('source', 'path')
|
311
316
|
path = UI_DAP.local_to_remote_path(req_path)
|
312
|
-
|
313
317
|
if path
|
314
318
|
SESSION.clear_line_breakpoints path
|
315
319
|
|
@@ -436,11 +440,12 @@ module DEBUGGER__
|
|
436
440
|
when 'evaluate'
|
437
441
|
expr = req.dig('arguments', 'expression')
|
438
442
|
if /\A\s*,(.+)\z/ =~ expr
|
439
|
-
dbg_expr = $1
|
443
|
+
dbg_expr = $1.strip
|
444
|
+
dbg_expr.split(';;') { |cmd| @q_msg << cmd }
|
445
|
+
|
440
446
|
send_response req,
|
441
|
-
result: "",
|
447
|
+
result: "(rdbg:command) #{dbg_expr}",
|
442
448
|
variablesReference: 0
|
443
|
-
debugger do: dbg_expr
|
444
449
|
else
|
445
450
|
@q_msg << req
|
446
451
|
end
|
@@ -452,8 +457,8 @@ module DEBUGGER__
|
|
452
457
|
@q_msg << req
|
453
458
|
|
454
459
|
else
|
455
|
-
if respond_to? mid = "
|
456
|
-
|
460
|
+
if respond_to? mid = "custom_dap_request_#{req['command']}"
|
461
|
+
__send__ mid, req
|
457
462
|
else
|
458
463
|
raise "Unknown request: #{req.inspect}"
|
459
464
|
end
|
@@ -546,7 +551,7 @@ module DEBUGGER__
|
|
546
551
|
when 'stackTrace'
|
547
552
|
tid = req.dig('arguments', 'threadId')
|
548
553
|
|
549
|
-
if
|
554
|
+
if find_waiting_tc(tid)
|
550
555
|
request_tc [:dap, :backtrace, req]
|
551
556
|
else
|
552
557
|
fail_response req
|
@@ -555,7 +560,7 @@ module DEBUGGER__
|
|
555
560
|
frame_id = req.dig('arguments', 'frameId')
|
556
561
|
if @frame_map[frame_id]
|
557
562
|
tid, fid = @frame_map[frame_id]
|
558
|
-
if
|
563
|
+
if find_waiting_tc(tid)
|
559
564
|
request_tc [:dap, :scopes, req, fid]
|
560
565
|
else
|
561
566
|
fail_response req
|
@@ -591,7 +596,7 @@ module DEBUGGER__
|
|
591
596
|
frame_id = ref[1]
|
592
597
|
tid, fid = @frame_map[frame_id]
|
593
598
|
|
594
|
-
if
|
599
|
+
if find_waiting_tc(tid)
|
595
600
|
request_tc [:dap, :scope, req, fid]
|
596
601
|
else
|
597
602
|
fail_response req
|
@@ -600,7 +605,7 @@ module DEBUGGER__
|
|
600
605
|
when :variable
|
601
606
|
tid, vid = ref[1], ref[2]
|
602
607
|
|
603
|
-
if
|
608
|
+
if find_waiting_tc(tid)
|
604
609
|
request_tc [:dap, :variable, req, vid]
|
605
610
|
else
|
606
611
|
fail_response req
|
@@ -619,7 +624,7 @@ module DEBUGGER__
|
|
619
624
|
tid, fid = @frame_map[frame_id]
|
620
625
|
expr = req.dig('arguments', 'expression')
|
621
626
|
|
622
|
-
if
|
627
|
+
if find_waiting_tc(tid)
|
623
628
|
request_tc [:dap, :evaluate, req, fid, expr, context]
|
624
629
|
else
|
625
630
|
fail_response req
|
@@ -640,7 +645,7 @@ module DEBUGGER__
|
|
640
645
|
frame_id = req.dig('arguments', 'frameId')
|
641
646
|
tid, fid = @frame_map[frame_id]
|
642
647
|
|
643
|
-
if
|
648
|
+
if find_waiting_tc(tid)
|
644
649
|
text = req.dig('arguments', 'text')
|
645
650
|
line = req.dig('arguments', 'line')
|
646
651
|
if col = req.dig('arguments', 'column')
|
@@ -651,11 +656,15 @@ module DEBUGGER__
|
|
651
656
|
fail_response req
|
652
657
|
end
|
653
658
|
else
|
654
|
-
|
659
|
+
if respond_to? mid = "custom_dap_request_#{req['command']}"
|
660
|
+
__send__ mid, req
|
661
|
+
else
|
662
|
+
raise "Unknown request: #{req.inspect}"
|
663
|
+
end
|
655
664
|
end
|
656
665
|
end
|
657
666
|
|
658
|
-
def
|
667
|
+
def process_protocol_result args
|
659
668
|
# puts({dap_event: args}.inspect)
|
660
669
|
type, req, result = args
|
661
670
|
|
@@ -703,7 +712,11 @@ module DEBUGGER__
|
|
703
712
|
when :completions
|
704
713
|
@ui.respond req, result
|
705
714
|
else
|
706
|
-
|
715
|
+
if respond_to? mid = "custom_dap_request_event_#{type}"
|
716
|
+
__send__ mid, req, result
|
717
|
+
else
|
718
|
+
raise "unsupported: #{args.inspect}"
|
719
|
+
end
|
707
720
|
end
|
708
721
|
end
|
709
722
|
|
@@ -789,7 +802,7 @@ module DEBUGGER__
|
|
789
802
|
}
|
790
803
|
end
|
791
804
|
|
792
|
-
event! :
|
805
|
+
event! :protocol_result, :backtrace, req, {
|
793
806
|
stackFrames: frames,
|
794
807
|
totalFrames: @target_frames.size,
|
795
808
|
}
|
@@ -806,7 +819,7 @@ module DEBUGGER__
|
|
806
819
|
0
|
807
820
|
end
|
808
821
|
|
809
|
-
event! :
|
822
|
+
event! :protocol_result, :scopes, req, scopes: [{
|
810
823
|
name: 'Local variables',
|
811
824
|
presentationHint: 'locals',
|
812
825
|
# variablesReference: N, # filled by SESSION
|
@@ -828,7 +841,7 @@ module DEBUGGER__
|
|
828
841
|
variable(var, val)
|
829
842
|
end
|
830
843
|
|
831
|
-
event! :
|
844
|
+
event! :protocol_result, :scope, req, variables: vars, tid: self.id
|
832
845
|
when :variable
|
833
846
|
vid = args.shift
|
834
847
|
obj = @var_map[vid]
|
@@ -876,7 +889,7 @@ module DEBUGGER__
|
|
876
889
|
end
|
877
890
|
end
|
878
891
|
end
|
879
|
-
event! :
|
892
|
+
event! :protocol_result, :variable, req, variables: (vars || []), tid: self.id
|
880
893
|
|
881
894
|
when :evaluate
|
882
895
|
fid, expr, context = args
|
@@ -931,7 +944,7 @@ module DEBUGGER__
|
|
931
944
|
result = 'Error: Can not evaluate on this frame'
|
932
945
|
end
|
933
946
|
|
934
|
-
event! :
|
947
|
+
event! :protocol_result, :evaluate, req, message: message, tid: self.id, **evaluate_result(result)
|
935
948
|
|
936
949
|
when :completions
|
937
950
|
fid, text = args
|
@@ -941,7 +954,7 @@ module DEBUGGER__
|
|
941
954
|
words = IRB::InputCompletor::retrieve_completion_data(word, bind: b).compact
|
942
955
|
end
|
943
956
|
|
944
|
-
event! :
|
957
|
+
event! :protocol_result, :completions, req, targets: (words || []).map{|phrase|
|
945
958
|
detail = nil
|
946
959
|
|
947
960
|
if /\b([_a-zA-Z]\w*[!\?]?)\z/ =~ phrase
|
@@ -964,7 +977,11 @@ module DEBUGGER__
|
|
964
977
|
}
|
965
978
|
|
966
979
|
else
|
967
|
-
|
980
|
+
if respond_to? mid = "custom_dap_request_#{type}"
|
981
|
+
__send__ mid, req
|
982
|
+
else
|
983
|
+
raise "Unknown request: #{args.inspect}"
|
984
|
+
end
|
968
985
|
end
|
969
986
|
end
|
970
987
|
|
data/lib/debug/session.rb
CHANGED
@@ -128,16 +128,16 @@ module DEBUGGER__
|
|
128
128
|
@obj_map = {} # { object_id => ... } for CDP
|
129
129
|
|
130
130
|
@tp_thread_begin = nil
|
131
|
+
@tp_thread_end = nil
|
132
|
+
|
131
133
|
@commands = {}
|
132
134
|
@unsafe_context = false
|
133
135
|
|
134
|
-
has_keep_script_lines = RubyVM.respond_to? :keep_script_lines
|
136
|
+
@has_keep_script_lines = RubyVM.respond_to? :keep_script_lines
|
135
137
|
|
136
138
|
@tp_load_script = TracePoint.new(:script_compiled){|tp|
|
137
|
-
|
138
|
-
|
139
|
-
ThreadClient.current.on_load tp.instruction_sequence, eval_script
|
140
|
-
end
|
139
|
+
eval_script = tp.eval_script unless @has_keep_script_lines
|
140
|
+
ThreadClient.current.on_load tp.instruction_sequence, eval_script
|
141
141
|
}
|
142
142
|
@tp_load_script.enable
|
143
143
|
|
@@ -169,12 +169,17 @@ module DEBUGGER__
|
|
169
169
|
@ui = ui if ui
|
170
170
|
|
171
171
|
@tp_thread_begin&.disable
|
172
|
+
@tp_thread_end&.disable
|
172
173
|
@tp_thread_begin = nil
|
173
|
-
|
174
|
+
@tp_thread_end = nil
|
174
175
|
@ui.activate self, on_fork: on_fork
|
175
176
|
|
176
177
|
q = Queue.new
|
178
|
+
first_q = Queue.new
|
177
179
|
@session_server = Thread.new do
|
180
|
+
# make sure `@session_server` is assigned
|
181
|
+
first_q.pop; first_q = nil
|
182
|
+
|
178
183
|
Thread.current.name = 'DEBUGGER__::SESSION@server'
|
179
184
|
Thread.current.abort_on_exception = true
|
180
185
|
|
@@ -192,10 +197,16 @@ module DEBUGGER__
|
|
192
197
|
end
|
193
198
|
@tp_thread_begin.enable
|
194
199
|
|
200
|
+
@tp_thread_end = TracePoint.new(:thread_end) do |tp|
|
201
|
+
@th_clients.delete(Thread.current)
|
202
|
+
end
|
203
|
+
@tp_thread_end.enable
|
204
|
+
|
195
205
|
# session start
|
196
206
|
q << true
|
197
207
|
session_server_main
|
198
208
|
end
|
209
|
+
first_q << :ok
|
199
210
|
|
200
211
|
q.pop
|
201
212
|
end
|
@@ -205,6 +216,7 @@ module DEBUGGER__
|
|
205
216
|
@thread_stopper.disable
|
206
217
|
@tp_load_script.disable
|
207
218
|
@tp_thread_begin.disable
|
219
|
+
@tp_thread_end.disable
|
208
220
|
@bps.each_value{|bp| bp.disable}
|
209
221
|
@th_clients.each_value{|thc| thc.close}
|
210
222
|
@tracers.values.each{|t| t.disable}
|
@@ -219,11 +231,13 @@ module DEBUGGER__
|
|
219
231
|
|
220
232
|
# activate new ui
|
221
233
|
@tp_thread_begin.disable
|
234
|
+
@tp_thread_end.disable
|
222
235
|
@ui.activate self
|
223
236
|
if @ui.respond_to?(:reader_thread) && thc = get_thread_client(@ui.reader_thread)
|
224
237
|
thc.mark_as_management
|
225
238
|
end
|
226
239
|
@tp_thread_begin.enable
|
240
|
+
@tp_thread_end.enable
|
227
241
|
end
|
228
242
|
|
229
243
|
def pop_event
|
@@ -329,16 +343,13 @@ module DEBUGGER__
|
|
329
343
|
opt = ev_args[3]
|
330
344
|
add_tracer ObjectTracer.new(@ui, obj_id, obj_inspect, **opt)
|
331
345
|
else
|
332
|
-
|
346
|
+
stop_all_threads
|
333
347
|
end
|
334
348
|
|
335
349
|
wait_command_loop
|
336
350
|
|
337
|
-
when :
|
338
|
-
|
339
|
-
wait_command_loop
|
340
|
-
when :cdp_result
|
341
|
-
cdp_event ev_args
|
351
|
+
when :protocol_result
|
352
|
+
process_protocol_result ev_args
|
342
353
|
wait_command_loop
|
343
354
|
end
|
344
355
|
end
|
@@ -480,9 +491,9 @@ module DEBUGGER__
|
|
480
491
|
# * `u[ntil]`
|
481
492
|
# * Similar to `next` command, but only stop later lines or the end of the current frame.
|
482
493
|
# * Similar to gdb's `advance` command.
|
483
|
-
# * `u[ntil] <[file:]line
|
494
|
+
# * `u[ntil] <[file:]line>`
|
484
495
|
# * Run til the program reaches given location or the end of the current frame.
|
485
|
-
# * `u[ntil] <name
|
496
|
+
# * `u[ntil] <name>`
|
486
497
|
# * Run til the program invokes a method `<name>`. `<name>` can be a regexp with `/name/`.
|
487
498
|
register_command 'u', 'until',
|
488
499
|
repeat: true,
|
@@ -889,13 +900,13 @@ module DEBUGGER__
|
|
889
900
|
# * `p <expr>`
|
890
901
|
# * Evaluate like `p <expr>` on the current frame.
|
891
902
|
register_command 'p' do |arg|
|
892
|
-
|
903
|
+
request_eval :p, arg.to_s
|
893
904
|
end
|
894
905
|
|
895
906
|
# * `pp <expr>`
|
896
907
|
# * Evaluate like `pp <expr>` on the current frame.
|
897
908
|
register_command 'pp' do |arg|
|
898
|
-
|
909
|
+
request_eval :pp, arg.to_s
|
899
910
|
end
|
900
911
|
|
901
912
|
# * `eval <expr>`
|
@@ -906,7 +917,7 @@ module DEBUGGER__
|
|
906
917
|
@ui.puts "\nTo evaluate the variable `#{cmd}`, use `pp #{cmd}` instead."
|
907
918
|
:retry
|
908
919
|
else
|
909
|
-
|
920
|
+
request_eval :call, arg
|
910
921
|
end
|
911
922
|
end
|
912
923
|
|
@@ -917,7 +928,7 @@ module DEBUGGER__
|
|
917
928
|
@ui.puts "not supported on the remote console."
|
918
929
|
:retry
|
919
930
|
end
|
920
|
-
|
931
|
+
request_eval :irb, nil
|
921
932
|
end
|
922
933
|
|
923
934
|
### Trace
|
@@ -1137,7 +1148,7 @@ module DEBUGGER__
|
|
1137
1148
|
@repl_prev_line = nil
|
1138
1149
|
check_unsafe
|
1139
1150
|
|
1140
|
-
|
1151
|
+
request_eval :pp, line
|
1141
1152
|
end
|
1142
1153
|
|
1143
1154
|
rescue Interrupt
|
@@ -1153,6 +1164,11 @@ module DEBUGGER__
|
|
1153
1164
|
return :retry
|
1154
1165
|
end
|
1155
1166
|
|
1167
|
+
def request_eval type, src
|
1168
|
+
restart_all_threads
|
1169
|
+
request_tc [:eval, type, src]
|
1170
|
+
end
|
1171
|
+
|
1156
1172
|
def step_command type, arg
|
1157
1173
|
if type == :until
|
1158
1174
|
leave_subsession [:step, type, arg]
|
@@ -1321,10 +1337,6 @@ module DEBUGGER__
|
|
1321
1337
|
|
1322
1338
|
# breakpoint management
|
1323
1339
|
|
1324
|
-
def bps_pending_until_load?
|
1325
|
-
@bps.any?{|key, bp| bp.pending_until_load?}
|
1326
|
-
end
|
1327
|
-
|
1328
1340
|
def iterate_bps
|
1329
1341
|
deleted_bps = []
|
1330
1342
|
i = 0
|
@@ -1500,7 +1512,7 @@ module DEBUGGER__
|
|
1500
1512
|
def clear_line_breakpoints path
|
1501
1513
|
path = resolve_path(path)
|
1502
1514
|
clear_breakpoints do |k, bp|
|
1503
|
-
bp.is_a?(LineBreakpoint) &&
|
1515
|
+
bp.is_a?(LineBreakpoint) && bp.path_is?(path)
|
1504
1516
|
end
|
1505
1517
|
rescue Errno::ENOENT
|
1506
1518
|
# just ignore
|
@@ -1524,7 +1536,7 @@ module DEBUGGER__
|
|
1524
1536
|
# tracers
|
1525
1537
|
|
1526
1538
|
def add_tracer tracer
|
1527
|
-
if @tracers
|
1539
|
+
if @tracers[tracer.key]&.enabled?
|
1528
1540
|
tracer.disable
|
1529
1541
|
@ui.puts "Duplicated tracer: #{tracer}"
|
1530
1542
|
else
|
@@ -1724,25 +1736,26 @@ module DEBUGGER__
|
|
1724
1736
|
file_path, reloaded = @sr.add(iseq, src)
|
1725
1737
|
@ui.event :load, file_path, reloaded
|
1726
1738
|
|
1727
|
-
|
1728
|
-
|
1729
|
-
|
1730
|
-
|
1731
|
-
pending_line_breakpoints.each do |_key, bp|
|
1732
|
-
if DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
|
1733
|
-
bp.try_activate iseq
|
1734
|
-
end
|
1735
|
-
end
|
1736
|
-
|
1737
|
-
if reloaded
|
1738
|
-
@bps.find_all do |key, bp|
|
1739
|
-
LineBreakpoint === bp && DEBUGGER__.compare_path(bp.path, file_path)
|
1739
|
+
# check breakpoints
|
1740
|
+
if file_path
|
1741
|
+
@bps.find_all do |_key, bp|
|
1742
|
+
LineBreakpoint === bp && bp.path_is?(file_path)
|
1740
1743
|
end.each do |_key, bp|
|
1741
|
-
|
1742
|
-
|
1743
|
-
|
1744
|
+
if !bp.iseq
|
1745
|
+
bp.try_activate iseq
|
1746
|
+
elsif reloaded
|
1747
|
+
@bps.delete bp.key # to allow duplicate
|
1748
|
+
if nbp = LineBreakpoint.copy(bp, iseq)
|
1749
|
+
add_bp nbp
|
1750
|
+
end
|
1744
1751
|
end
|
1745
1752
|
end
|
1753
|
+
else # !file_path => file_path is not existing
|
1754
|
+
@bps.find_all do |_key, bp|
|
1755
|
+
LineBreakpoint === bp && !bp.iseq && DEBUGGER__.compare_path(bp.path, (iseq.absolute_path || iseq.path))
|
1756
|
+
end.each do |_key, bp|
|
1757
|
+
bp.try_activate iseq
|
1758
|
+
end
|
1746
1759
|
end
|
1747
1760
|
end
|
1748
1761
|
|
@@ -2438,7 +2451,7 @@ module DEBUGGER__
|
|
2438
2451
|
end
|
2439
2452
|
|
2440
2453
|
module DaemonInterceptor
|
2441
|
-
def daemon
|
2454
|
+
def daemon(*args)
|
2442
2455
|
return super unless defined?(SESSION) && SESSION.active?
|
2443
2456
|
|
2444
2457
|
_, child_hook = __fork_setup_for_debugger(:child)
|
@@ -34,7 +34,7 @@ module DEBUGGER__
|
|
34
34
|
end
|
35
35
|
|
36
36
|
def add iseq, src
|
37
|
-
#
|
37
|
+
# only manage loaded file names
|
38
38
|
if (path = (iseq.absolute_path || iseq.path)) && File.exist?(path)
|
39
39
|
if @loaded_file_map.has_key? path
|
40
40
|
return path, true # reloaded
|
@@ -49,7 +49,7 @@ module DEBUGGER__
|
|
49
49
|
lines = iseq.script_lines&.map(&:chomp)
|
50
50
|
line = iseq.first_line
|
51
51
|
if line > 1
|
52
|
-
|
52
|
+
[*([''] * (line - 1)), *lines]
|
53
53
|
else
|
54
54
|
lines
|
55
55
|
end
|
data/lib/debug/thread_client.rb
CHANGED
@@ -1230,7 +1230,15 @@ module DEBUGGER__
|
|
1230
1230
|
rescue SuspendReplay, SystemExit, Interrupt
|
1231
1231
|
raise
|
1232
1232
|
rescue Exception => e
|
1233
|
-
|
1233
|
+
STDERR.puts e.cause.inspect
|
1234
|
+
STDERR.puts e.inspect
|
1235
|
+
Thread.list.each{|th|
|
1236
|
+
STDERR.puts "@@@ #{th}"
|
1237
|
+
th.backtrace.each{|b|
|
1238
|
+
STDERR.puts " > #{b}"
|
1239
|
+
}
|
1240
|
+
}
|
1241
|
+
p ["DEBUGGER Exception: #{__FILE__}:#{__LINE__}", e, e.backtrace]
|
1234
1242
|
raise
|
1235
1243
|
ensure
|
1236
1244
|
@returning = false
|
data/lib/debug/tracer.rb
CHANGED
@@ -54,6 +54,10 @@ module DEBUGGER__
|
|
54
54
|
@tracer.disable
|
55
55
|
end
|
56
56
|
|
57
|
+
def enabled?
|
58
|
+
@tracer.enabled?
|
59
|
+
end
|
60
|
+
|
57
61
|
def description
|
58
62
|
nil
|
59
63
|
end
|
@@ -85,11 +89,6 @@ module DEBUGGER__
|
|
85
89
|
end
|
86
90
|
end
|
87
91
|
|
88
|
-
def puts msg
|
89
|
-
@output.puts msg
|
90
|
-
@output.flush
|
91
|
-
end
|
92
|
-
|
93
92
|
def minfo tp
|
94
93
|
return "block{}" if tp.event == :b_call
|
95
94
|
|
data/lib/debug/version.rb
CHANGED
data/misc/README.md.erb
CHANGED
@@ -26,7 +26,7 @@ New debug.rb has several advantages:
|
|
26
26
|
* Support threads (almost done) and ractors (TODO).
|
27
27
|
* Support suspending and entering to the console debugging with `Ctrl-C` at most of timing.
|
28
28
|
* Show parameters on backtrace command.
|
29
|
-
* Support recording &
|
29
|
+
* Support recording & replay debugging.
|
30
30
|
|
31
31
|
# Installation
|
32
32
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: debug
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.7.
|
4
|
+
version: 1.7.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Koichi Sasada
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2023-03-28 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: irb
|