debug 1.7.1 → 1.7.2
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.
- 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
|