debug 1.7.0 → 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/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
|