debug 1.7.0 → 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/TODO.md +8 -8
- data/lib/debug/breakpoint.rb +6 -8
- data/lib/debug/config.rb +23 -2
- data/lib/debug/prelude.rb +1 -1
- data/lib/debug/server.rb +5 -6
- data/lib/debug/server_cdp.rb +82 -76
- data/lib/debug/server_dap.rb +69 -39
- data/lib/debug/session.rb +55 -42
- data/lib/debug/source_repository.rb +2 -2
- data/lib/debug/thread_client.rb +19 -4
- 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/TODO.md
CHANGED
@@ -2,22 +2,22 @@
|
|
2
2
|
|
3
3
|
## Basic functionality
|
4
4
|
|
5
|
-
* Support Ractors
|
6
|
-
* Signal (SIGINT) trap handling
|
5
|
+
* Support Fibers and Ractors
|
7
6
|
|
8
7
|
## UI
|
9
8
|
|
9
|
+
* Multi-line support
|
10
10
|
* Completion for Ruby's code
|
11
11
|
* Interactive breakpoint setting
|
12
12
|
* Interactive record & play debugging
|
13
13
|
* irb integration
|
14
|
-
* Web browser integrated UI
|
15
|
-
* History file
|
16
14
|
|
17
15
|
## Debug command
|
18
16
|
|
19
|
-
* Breakpoints
|
20
|
-
* Lightweight pending method break points with Ruby 3.1 feature (TP:method_added)
|
21
17
|
* Watch points
|
22
|
-
* Lightweight watchpoints for instance variables with Ruby 3.
|
23
|
-
*
|
18
|
+
* Lightweight watchpoints for instance variables with Ruby 3.3 features (TP:ivar_set)
|
19
|
+
* Alias
|
20
|
+
|
21
|
+
## Debug port
|
22
|
+
|
23
|
+
* Debug port for monitoring
|
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/prelude.rb
CHANGED
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
@@ -34,25 +34,26 @@ module DEBUGGER__
|
|
34
34
|
|
35
35
|
loop do
|
36
36
|
res = ws_client.extract_data
|
37
|
-
case
|
38
|
-
when
|
37
|
+
case res['id']
|
38
|
+
when 1
|
39
|
+
target_info = res.dig('result', 'targetInfos')
|
39
40
|
page = target_info.find{|t| t['type'] == 'page'}
|
40
41
|
ws_client.send id: 2, method: 'Target.attachToTarget',
|
41
42
|
params: {
|
42
43
|
targetId: page['targetId'],
|
43
44
|
flatten: true
|
44
45
|
}
|
45
|
-
when
|
46
|
+
when 2
|
46
47
|
s_id = res.dig('result', 'sessionId')
|
47
48
|
# TODO: change id
|
48
49
|
ws_client.send sessionId: s_id, id: 100, method: 'Network.enable'
|
49
50
|
ws_client.send sessionId: s_id, id: 3,
|
50
51
|
method: 'Page.enable'
|
51
|
-
when
|
52
|
+
when 3
|
52
53
|
s_id = res['sessionId']
|
53
54
|
ws_client.send sessionId: s_id, id: 4,
|
54
55
|
method: 'Page.getFrameTree'
|
55
|
-
when
|
56
|
+
when 4
|
56
57
|
s_id = res['sessionId']
|
57
58
|
f_id = res.dig('result', 'frameTree', 'frame', 'id')
|
58
59
|
ws_client.send sessionId: s_id, id: 5,
|
@@ -61,17 +62,19 @@ module DEBUGGER__
|
|
61
62
|
url: "devtools://devtools/bundled/inspector.html?v8only=true&panel=sources&ws=#{addr}/#{uuid}",
|
62
63
|
frameId: f_id
|
63
64
|
}
|
64
|
-
when
|
65
|
-
s_id = res['sessionId']
|
66
|
-
# Display the console by entering ESC key
|
67
|
-
ws_client.send sessionId: s_id, id: 101, # TODO: change id
|
68
|
-
method:"Input.dispatchKeyEvent",
|
69
|
-
params: {
|
70
|
-
type:"keyDown",
|
71
|
-
windowsVirtualKeyCode:27 # ESC key
|
72
|
-
}
|
73
|
-
when res['id'] == 101
|
65
|
+
when 101
|
74
66
|
break
|
67
|
+
else
|
68
|
+
if res['method'] == 'Network.webSocketWillSendHandshakeRequest'
|
69
|
+
s_id = res['sessionId']
|
70
|
+
# Display the console by entering ESC key
|
71
|
+
ws_client.send sessionId: s_id, id: 101, # TODO: change id
|
72
|
+
method:"Input.dispatchKeyEvent",
|
73
|
+
params: {
|
74
|
+
type:"keyDown",
|
75
|
+
windowsVirtualKeyCode:27 # ESC key
|
76
|
+
}
|
77
|
+
end
|
75
78
|
end
|
76
79
|
end
|
77
80
|
pid
|
@@ -95,7 +98,6 @@ module DEBUGGER__
|
|
95
98
|
candidates = ['C:\\Program Files (x86)\\Google\\Chrome\\Application\\chrome.exe', 'C:\\Program Files\\Google\\Chrome\\Application\\chrome.exe']
|
96
99
|
path = get_chrome_path candidates
|
97
100
|
end
|
98
|
-
uuid = SecureRandom.uuid
|
99
101
|
# The path is based on https://github.com/sindresorhus/open/blob/v8.4.0/index.js#L128.
|
100
102
|
stdin, stdout, stderr, wait_thr = *Open3.popen3("#{ENV['SystemRoot']}\\System32\\WindowsPowerShell\\v1.0\\powershell")
|
101
103
|
tf = Tempfile.create(['debug-', '.txt'])
|
@@ -447,11 +449,7 @@ module DEBUGGER__
|
|
447
449
|
end
|
448
450
|
|
449
451
|
def send_response req, **res
|
450
|
-
|
451
|
-
@ws_server.send id: req['id'], result: {}
|
452
|
-
else
|
453
|
-
@ws_server.send id: req['id'], result: res
|
454
|
-
end
|
452
|
+
@ws_server.send id: req['id'], result: res
|
455
453
|
end
|
456
454
|
|
457
455
|
def send_fail_response req, **res
|
@@ -459,11 +457,7 @@ module DEBUGGER__
|
|
459
457
|
end
|
460
458
|
|
461
459
|
def send_event method, **params
|
462
|
-
|
463
|
-
@ws_server.send method: method, params: {}
|
464
|
-
else
|
465
|
-
@ws_server.send method: method, params: params
|
466
|
-
end
|
460
|
+
@ws_server.send method: method, params: params
|
467
461
|
end
|
468
462
|
|
469
463
|
INVALID_REQUEST = -32600
|
@@ -554,6 +548,9 @@ module DEBUGGER__
|
|
554
548
|
activate_bp bps
|
555
549
|
end
|
556
550
|
send_response req
|
551
|
+
when 'Debugger.pause'
|
552
|
+
send_response req
|
553
|
+
Process.kill(UI_ServerBase::TRAP_SIGNAL, Process.pid)
|
557
554
|
|
558
555
|
# breakpoint
|
559
556
|
when 'Debugger.getPossibleBreakpoints'
|
@@ -561,35 +558,31 @@ module DEBUGGER__
|
|
561
558
|
when 'Debugger.setBreakpointByUrl'
|
562
559
|
line = req.dig('params', 'lineNumber')
|
563
560
|
if regexp = req.dig('params', 'urlRegex')
|
564
|
-
path = regexp.match(/(.*)\|/)[1].gsub("\\", "")
|
565
|
-
cond = req.dig('params', 'condition')
|
566
|
-
src = get_source_code path
|
567
|
-
end_line = src.lines.count
|
568
|
-
line = end_line if line > end_line
|
569
561
|
b_id = "1:#{line}:#{regexp}"
|
570
|
-
if cond != ''
|
571
|
-
SESSION.add_line_breakpoint(path, line + 1, cond: cond)
|
572
|
-
else
|
573
|
-
SESSION.add_line_breakpoint(path, line + 1)
|
574
|
-
end
|
575
562
|
bps[b_id] = bps.size
|
576
|
-
|
577
|
-
req
|
578
|
-
req['params']['lineNumber'] = line
|
579
|
-
req['params']['breakpointId'] = b_id
|
580
|
-
@q_msg << req
|
563
|
+
path = regexp.match(/(.*)\|/)[1].gsub("\\", "")
|
564
|
+
add_line_breakpoint(req, b_id, path)
|
581
565
|
elsif url = req.dig('params', 'url')
|
582
566
|
b_id = "#{line}:#{url}"
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
|
588
|
-
|
589
|
-
|
590
|
-
|
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
|
591
577
|
else
|
592
|
-
|
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
|
593
586
|
end
|
594
587
|
when 'Debugger.removeBreakpoint'
|
595
588
|
b_id = req.dig('params', 'breakpointId')
|
@@ -628,6 +621,24 @@ module DEBUGGER__
|
|
628
621
|
@q_msg << 'continue'
|
629
622
|
end
|
630
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
|
+
|
631
642
|
def del_bp bps, k
|
632
643
|
return bps unless idx = bps[k]
|
633
644
|
|
@@ -665,37 +676,28 @@ module DEBUGGER__
|
|
665
676
|
def cleanup_reader
|
666
677
|
super
|
667
678
|
Process.kill :KILL, @chrome_pid if @chrome_pid
|
679
|
+
rescue Errno::ESRCH # continue if @chrome_pid process is not found
|
668
680
|
end
|
669
681
|
|
670
682
|
## Called by the SESSION thread
|
671
683
|
|
672
|
-
|
673
|
-
|
674
|
-
|
675
|
-
|
676
|
-
def respond_fail req, **result
|
677
|
-
send_fail_response req, **result
|
678
|
-
end
|
679
|
-
|
680
|
-
def fire_event event, **result
|
681
|
-
if result.empty?
|
682
|
-
send_event event
|
683
|
-
else
|
684
|
-
send_event event, **result
|
685
|
-
end
|
686
|
-
end
|
684
|
+
alias respond send_response
|
685
|
+
alias respond_fail send_fail_response
|
686
|
+
alias fire_event send_event
|
687
687
|
|
688
688
|
def sock skip: false
|
689
689
|
yield $stderr
|
690
690
|
end
|
691
691
|
|
692
|
-
def puts result
|
692
|
+
def puts result=''
|
693
693
|
# STDERR.puts "puts: #{result}"
|
694
694
|
# send_event 'output', category: 'stderr', output: "PUTS!!: " + result.to_s
|
695
695
|
end
|
696
696
|
end
|
697
697
|
|
698
698
|
class Session
|
699
|
+
include GlobalVariablesHelper
|
700
|
+
|
699
701
|
# FIXME: unify this method with ThreadClient#propertyDescriptor.
|
700
702
|
def get_type obj
|
701
703
|
case obj
|
@@ -750,8 +752,12 @@ module DEBUGGER__
|
|
750
752
|
fid = @frame_map[frame_id]
|
751
753
|
request_tc [:cdp, :scope, req, fid]
|
752
754
|
when 'global'
|
753
|
-
vars =
|
754
|
-
|
755
|
+
vars = safe_global_variables.sort.map do |name|
|
756
|
+
begin
|
757
|
+
gv = eval(name.to_s)
|
758
|
+
rescue Errno::ENOENT
|
759
|
+
gv = nil
|
760
|
+
end
|
755
761
|
prop = {
|
756
762
|
name: name,
|
757
763
|
value: {
|
@@ -831,7 +837,7 @@ module DEBUGGER__
|
|
831
837
|
end
|
832
838
|
end
|
833
839
|
|
834
|
-
def
|
840
|
+
def process_protocol_result args
|
835
841
|
type, req, result = args
|
836
842
|
|
837
843
|
case type
|
@@ -1021,7 +1027,7 @@ module DEBUGGER__
|
|
1021
1027
|
result[:data] = evaluate_result exception
|
1022
1028
|
result[:reason] = 'exception'
|
1023
1029
|
end
|
1024
|
-
event! :
|
1030
|
+
event! :protocol_result, :backtrace, req, result
|
1025
1031
|
when :evaluate
|
1026
1032
|
res = {}
|
1027
1033
|
fid, expr, group = args
|
@@ -1040,7 +1046,7 @@ module DEBUGGER__
|
|
1040
1046
|
case expr
|
1041
1047
|
# Chrome doesn't read instance variables
|
1042
1048
|
when /\A\$\S/
|
1043
|
-
|
1049
|
+
safe_global_variables.each{|gvar|
|
1044
1050
|
if gvar.to_s == expr
|
1045
1051
|
result = eval(gvar.to_s)
|
1046
1052
|
break false
|
@@ -1066,7 +1072,7 @@ module DEBUGGER__
|
|
1066
1072
|
begin
|
1067
1073
|
orig_stdout = $stdout
|
1068
1074
|
$stdout = StringIO.new
|
1069
|
-
result =
|
1075
|
+
result = b.eval(expr.to_s, '(DEBUG CONSOLE)')
|
1070
1076
|
rescue Exception => e
|
1071
1077
|
result = e
|
1072
1078
|
res[:exceptionDetails] = exceptionDetails(e, 'Uncaught')
|
@@ -1082,7 +1088,7 @@ module DEBUGGER__
|
|
1082
1088
|
end
|
1083
1089
|
|
1084
1090
|
res[:result] = evaluate_result(result)
|
1085
|
-
event! :
|
1091
|
+
event! :protocol_result, :evaluate, req, message: message, response: res, output: output
|
1086
1092
|
when :scope
|
1087
1093
|
fid = args.shift
|
1088
1094
|
frame = @target_frames[fid]
|
@@ -1105,7 +1111,7 @@ module DEBUGGER__
|
|
1105
1111
|
vars.unshift variable(name, val)
|
1106
1112
|
end
|
1107
1113
|
end
|
1108
|
-
event! :
|
1114
|
+
event! :protocol_result, :scope, req, vars
|
1109
1115
|
when :properties
|
1110
1116
|
oid = args.shift
|
1111
1117
|
result = []
|
@@ -1147,14 +1153,14 @@ module DEBUGGER__
|
|
1147
1153
|
}
|
1148
1154
|
prop += [internalProperty('#class', M_CLASS.bind_call(obj))]
|
1149
1155
|
end
|
1150
|
-
event! :
|
1156
|
+
event! :protocol_result, :properties, req, result: result, internalProperties: prop
|
1151
1157
|
when :exception
|
1152
1158
|
oid = args.shift
|
1153
1159
|
exc = nil
|
1154
1160
|
if obj = @obj_map[oid]
|
1155
1161
|
exc = exceptionDetails obj, obj.to_s
|
1156
1162
|
end
|
1157
|
-
event! :
|
1163
|
+
event! :protocol_result, :exception, req, exceptionDetails: exc
|
1158
1164
|
end
|
1159
1165
|
end
|
1160
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,7 +457,11 @@ module DEBUGGER__
|
|
452
457
|
@q_msg << req
|
453
458
|
|
454
459
|
else
|
455
|
-
|
460
|
+
if respond_to? mid = "custom_dap_request_#{req['command']}"
|
461
|
+
__send__ mid, req
|
462
|
+
else
|
463
|
+
raise "Unknown request: #{req.inspect}"
|
464
|
+
end
|
456
465
|
end
|
457
466
|
end
|
458
467
|
ensure
|
@@ -516,6 +525,8 @@ module DEBUGGER__
|
|
516
525
|
end
|
517
526
|
|
518
527
|
class Session
|
528
|
+
include GlobalVariablesHelper
|
529
|
+
|
519
530
|
def find_waiting_tc id
|
520
531
|
@th_clients.each{|th, tc|
|
521
532
|
return tc if tc.id == id && tc.waiting?
|
@@ -540,7 +551,7 @@ module DEBUGGER__
|
|
540
551
|
when 'stackTrace'
|
541
552
|
tid = req.dig('arguments', 'threadId')
|
542
553
|
|
543
|
-
if
|
554
|
+
if find_waiting_tc(tid)
|
544
555
|
request_tc [:dap, :backtrace, req]
|
545
556
|
else
|
546
557
|
fail_response req
|
@@ -549,7 +560,7 @@ module DEBUGGER__
|
|
549
560
|
frame_id = req.dig('arguments', 'frameId')
|
550
561
|
if @frame_map[frame_id]
|
551
562
|
tid, fid = @frame_map[frame_id]
|
552
|
-
if
|
563
|
+
if find_waiting_tc(tid)
|
553
564
|
request_tc [:dap, :scopes, req, fid]
|
554
565
|
else
|
555
566
|
fail_response req
|
@@ -562,8 +573,12 @@ module DEBUGGER__
|
|
562
573
|
if ref = @var_map[varid]
|
563
574
|
case ref[0]
|
564
575
|
when :globals
|
565
|
-
vars =
|
566
|
-
|
576
|
+
vars = safe_global_variables.sort.map do |name|
|
577
|
+
begin
|
578
|
+
gv = eval(name.to_s)
|
579
|
+
rescue Exception => e
|
580
|
+
gv = e.inspect
|
581
|
+
end
|
567
582
|
{
|
568
583
|
name: name,
|
569
584
|
value: gv.inspect,
|
@@ -581,7 +596,7 @@ module DEBUGGER__
|
|
581
596
|
frame_id = ref[1]
|
582
597
|
tid, fid = @frame_map[frame_id]
|
583
598
|
|
584
|
-
if
|
599
|
+
if find_waiting_tc(tid)
|
585
600
|
request_tc [:dap, :scope, req, fid]
|
586
601
|
else
|
587
602
|
fail_response req
|
@@ -590,7 +605,7 @@ module DEBUGGER__
|
|
590
605
|
when :variable
|
591
606
|
tid, vid = ref[1], ref[2]
|
592
607
|
|
593
|
-
if
|
608
|
+
if find_waiting_tc(tid)
|
594
609
|
request_tc [:dap, :variable, req, vid]
|
595
610
|
else
|
596
611
|
fail_response req
|
@@ -609,7 +624,7 @@ module DEBUGGER__
|
|
609
624
|
tid, fid = @frame_map[frame_id]
|
610
625
|
expr = req.dig('arguments', 'expression')
|
611
626
|
|
612
|
-
if
|
627
|
+
if find_waiting_tc(tid)
|
613
628
|
request_tc [:dap, :evaluate, req, fid, expr, context]
|
614
629
|
else
|
615
630
|
fail_response req
|
@@ -630,7 +645,7 @@ module DEBUGGER__
|
|
630
645
|
frame_id = req.dig('arguments', 'frameId')
|
631
646
|
tid, fid = @frame_map[frame_id]
|
632
647
|
|
633
|
-
if
|
648
|
+
if find_waiting_tc(tid)
|
634
649
|
text = req.dig('arguments', 'text')
|
635
650
|
line = req.dig('arguments', 'line')
|
636
651
|
if col = req.dig('arguments', 'column')
|
@@ -641,11 +656,15 @@ module DEBUGGER__
|
|
641
656
|
fail_response req
|
642
657
|
end
|
643
658
|
else
|
644
|
-
|
659
|
+
if respond_to? mid = "custom_dap_request_#{req['command']}"
|
660
|
+
__send__ mid, req
|
661
|
+
else
|
662
|
+
raise "Unknown request: #{req.inspect}"
|
663
|
+
end
|
645
664
|
end
|
646
665
|
end
|
647
666
|
|
648
|
-
def
|
667
|
+
def process_protocol_result args
|
649
668
|
# puts({dap_event: args}.inspect)
|
650
669
|
type, req, result = args
|
651
670
|
|
@@ -693,7 +712,11 @@ module DEBUGGER__
|
|
693
712
|
when :completions
|
694
713
|
@ui.respond req, result
|
695
714
|
else
|
696
|
-
|
715
|
+
if respond_to? mid = "custom_dap_request_event_#{type}"
|
716
|
+
__send__ mid, req, result
|
717
|
+
else
|
718
|
+
raise "unsupported: #{args.inspect}"
|
719
|
+
end
|
697
720
|
end
|
698
721
|
end
|
699
722
|
|
@@ -721,9 +744,8 @@ module DEBUGGER__
|
|
721
744
|
end
|
722
745
|
|
723
746
|
class ThreadClient
|
724
|
-
|
725
747
|
MAX_LENGTH = 180
|
726
|
-
|
748
|
+
|
727
749
|
def value_inspect obj, short: true
|
728
750
|
# TODO: max length should be configuarable?
|
729
751
|
str = DEBUGGER__.safe_inspect obj, short: short, max_length: MAX_LENGTH
|
@@ -735,6 +757,14 @@ module DEBUGGER__
|
|
735
757
|
end
|
736
758
|
end
|
737
759
|
|
760
|
+
def dap_eval b, expr, _context, prompt: '(repl_eval)'
|
761
|
+
begin
|
762
|
+
b.eval(expr.to_s, prompt)
|
763
|
+
rescue Exception => e
|
764
|
+
e
|
765
|
+
end
|
766
|
+
end
|
767
|
+
|
738
768
|
def process_dap args
|
739
769
|
# pp tc: self, args: args
|
740
770
|
type = args.shift
|
@@ -772,7 +802,7 @@ module DEBUGGER__
|
|
772
802
|
}
|
773
803
|
end
|
774
804
|
|
775
|
-
event! :
|
805
|
+
event! :protocol_result, :backtrace, req, {
|
776
806
|
stackFrames: frames,
|
777
807
|
totalFrames: @target_frames.size,
|
778
808
|
}
|
@@ -789,7 +819,7 @@ module DEBUGGER__
|
|
789
819
|
0
|
790
820
|
end
|
791
821
|
|
792
|
-
event! :
|
822
|
+
event! :protocol_result, :scopes, req, scopes: [{
|
793
823
|
name: 'Local variables',
|
794
824
|
presentationHint: 'locals',
|
795
825
|
# variablesReference: N, # filled by SESSION
|
@@ -800,7 +830,7 @@ module DEBUGGER__
|
|
800
830
|
name: 'Global variables',
|
801
831
|
presentationHint: 'globals',
|
802
832
|
variablesReference: 1, # GLOBAL
|
803
|
-
namedVariables:
|
833
|
+
namedVariables: safe_global_variables.size,
|
804
834
|
indexedVariables: 0,
|
805
835
|
expensive: false,
|
806
836
|
}]
|
@@ -811,7 +841,7 @@ module DEBUGGER__
|
|
811
841
|
variable(var, val)
|
812
842
|
end
|
813
843
|
|
814
|
-
event! :
|
844
|
+
event! :protocol_result, :scope, req, variables: vars, tid: self.id
|
815
845
|
when :variable
|
816
846
|
vid = args.shift
|
817
847
|
obj = @var_map[vid]
|
@@ -837,10 +867,11 @@ module DEBUGGER__
|
|
837
867
|
}
|
838
868
|
when String
|
839
869
|
vars = [
|
840
|
-
variable('#
|
870
|
+
variable('#lengthddsfsd', obj.length),
|
841
871
|
variable('#encoding', obj.encoding),
|
842
872
|
]
|
843
|
-
|
873
|
+
printed_str = value_inspect(obj)
|
874
|
+
vars << variable('#dump', NaiveString.new(obj)) if printed_str.end_with?('...')
|
844
875
|
when Class, Module
|
845
876
|
vars << variable('%ancestors', obj.ancestors[1..])
|
846
877
|
when Range
|
@@ -858,7 +889,7 @@ module DEBUGGER__
|
|
858
889
|
end
|
859
890
|
end
|
860
891
|
end
|
861
|
-
event! :
|
892
|
+
event! :protocol_result, :variable, req, variables: (vars || []), tid: self.id
|
862
893
|
|
863
894
|
when :evaluate
|
864
895
|
fid, expr, context = args
|
@@ -872,12 +903,7 @@ module DEBUGGER__
|
|
872
903
|
|
873
904
|
case context
|
874
905
|
when 'repl', 'watch'
|
875
|
-
|
876
|
-
result = b.eval(expr.to_s, '(DEBUG CONSOLE)')
|
877
|
-
rescue Exception => e
|
878
|
-
result = e
|
879
|
-
end
|
880
|
-
|
906
|
+
result = dap_eval b, expr, context, prompt: '(DEBUG CONSOLE)'
|
881
907
|
when 'hover'
|
882
908
|
case expr
|
883
909
|
when /\A\@\S/
|
@@ -887,7 +913,7 @@ module DEBUGGER__
|
|
887
913
|
message = "Error: Not defined instance variable: #{expr.inspect}"
|
888
914
|
end
|
889
915
|
when /\A\$\S/
|
890
|
-
|
916
|
+
safe_global_variables.each{|gvar|
|
891
917
|
if gvar.to_s == expr
|
892
918
|
result = eval(gvar.to_s)
|
893
919
|
break false
|
@@ -918,7 +944,7 @@ module DEBUGGER__
|
|
918
944
|
result = 'Error: Can not evaluate on this frame'
|
919
945
|
end
|
920
946
|
|
921
|
-
event! :
|
947
|
+
event! :protocol_result, :evaluate, req, message: message, tid: self.id, **evaluate_result(result)
|
922
948
|
|
923
949
|
when :completions
|
924
950
|
fid, text = args
|
@@ -928,7 +954,7 @@ module DEBUGGER__
|
|
928
954
|
words = IRB::InputCompletor::retrieve_completion_data(word, bind: b).compact
|
929
955
|
end
|
930
956
|
|
931
|
-
event! :
|
957
|
+
event! :protocol_result, :completions, req, targets: (words || []).map{|phrase|
|
932
958
|
detail = nil
|
933
959
|
|
934
960
|
if /\b([_a-zA-Z]\w*[!\?]?)\z/ =~ phrase
|
@@ -951,7 +977,11 @@ module DEBUGGER__
|
|
951
977
|
}
|
952
978
|
|
953
979
|
else
|
954
|
-
|
980
|
+
if respond_to? mid = "custom_dap_request_#{type}"
|
981
|
+
__send__ mid, req
|
982
|
+
else
|
983
|
+
raise "Unknown request: #{args.inspect}"
|
984
|
+
end
|
955
985
|
end
|
956
986
|
end
|
957
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
@@ -29,7 +29,7 @@ module DEBUGGER__
|
|
29
29
|
end
|
30
30
|
|
31
31
|
def skip_internal_path?(path)
|
32
|
-
path.start_with?(__dir__) || path.start_with?('<internal:')
|
32
|
+
path.start_with?(__dir__) || path.delete_prefix('!eval:').start_with?('<internal:')
|
33
33
|
end
|
34
34
|
|
35
35
|
def skip_location?(loc)
|
@@ -38,6 +38,13 @@ module DEBUGGER__
|
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
+
module GlobalVariablesHelper
|
42
|
+
SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
|
43
|
+
def safe_global_variables
|
44
|
+
global_variables.reject{|name| SKIP_GLOBAL_LIST.include? name }
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
41
48
|
class ThreadClient
|
42
49
|
def self.current
|
43
50
|
if thc = Thread.current[:DEBUGGER__ThreadClient]
|
@@ -50,6 +57,7 @@ module DEBUGGER__
|
|
50
57
|
|
51
58
|
include Color
|
52
59
|
include SkipPathHelper
|
60
|
+
include GlobalVariablesHelper
|
53
61
|
|
54
62
|
attr_reader :thread, :id, :recorder, :check_bp_fulfillment_map
|
55
63
|
|
@@ -636,9 +644,8 @@ module DEBUGGER__
|
|
636
644
|
end
|
637
645
|
end
|
638
646
|
|
639
|
-
SKIP_GLOBAL_LIST = %i[$= $KCODE $-K $SAFE].freeze
|
640
647
|
def show_globals pat
|
641
|
-
|
648
|
+
safe_global_variables.sort.each{|name|
|
642
649
|
next if SKIP_GLOBAL_LIST.include? name
|
643
650
|
|
644
651
|
value = eval(name.to_s)
|
@@ -1223,7 +1230,15 @@ module DEBUGGER__
|
|
1223
1230
|
rescue SuspendReplay, SystemExit, Interrupt
|
1224
1231
|
raise
|
1225
1232
|
rescue Exception => e
|
1226
|
-
|
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]
|
1227
1242
|
raise
|
1228
1243
|
ensure
|
1229
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
|