ed-precompiled_debug 1.11.0
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 +7 -0
- data/CONTRIBUTING.md +573 -0
- data/Gemfile +10 -0
- data/LICENSE.txt +22 -0
- data/README.md +996 -0
- data/Rakefile +57 -0
- data/TODO.md +23 -0
- data/debug.gemspec +33 -0
- data/exe/rdbg +53 -0
- data/ext/debug/debug.c +228 -0
- data/ext/debug/extconf.rb +27 -0
- data/ext/debug/iseq_collector.c +93 -0
- data/lib/debug/abbrev_command.rb +77 -0
- data/lib/debug/breakpoint.rb +556 -0
- data/lib/debug/client.rb +263 -0
- data/lib/debug/color.rb +123 -0
- data/lib/debug/config.rb +592 -0
- data/lib/debug/console.rb +224 -0
- data/lib/debug/dap_custom/traceInspector.rb +336 -0
- data/lib/debug/frame_info.rb +191 -0
- data/lib/debug/irb_integration.rb +37 -0
- data/lib/debug/local.rb +115 -0
- data/lib/debug/open.rb +13 -0
- data/lib/debug/open_nonstop.rb +15 -0
- data/lib/debug/prelude.rb +50 -0
- data/lib/debug/server.rb +534 -0
- data/lib/debug/server_cdp.rb +1348 -0
- data/lib/debug/server_dap.rb +1108 -0
- data/lib/debug/session.rb +2667 -0
- data/lib/debug/source_repository.rb +150 -0
- data/lib/debug/start.rb +5 -0
- data/lib/debug/thread_client.rb +1457 -0
- data/lib/debug/tracer.rb +241 -0
- data/lib/debug/version.rb +5 -0
- data/lib/debug.rb +9 -0
- data/misc/README.md.erb +660 -0
- metadata +110 -0
@@ -0,0 +1,191 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DEBUGGER__
|
4
|
+
FrameInfo = Struct.new(:location, :self, :binding, :iseq, :class, :frame_depth,
|
5
|
+
:has_return_value, :return_value,
|
6
|
+
:has_raised_exception, :raised_exception,
|
7
|
+
:show_line,
|
8
|
+
:_local_variables, :_callee, # for recorder
|
9
|
+
:dupped_binding,
|
10
|
+
)
|
11
|
+
|
12
|
+
# extend FrameInfo with debug.so
|
13
|
+
begin
|
14
|
+
ruby_version = /(\d+\.\d+)/.match(::RUBY_VERSION)
|
15
|
+
require "#{ruby_version}/debug/debug.so"
|
16
|
+
rescue LoadError
|
17
|
+
require 'debug/debug.so'
|
18
|
+
end
|
19
|
+
|
20
|
+
class FrameInfo
|
21
|
+
HOME = ENV['HOME'] ? (ENV['HOME'] + '/') : nil
|
22
|
+
|
23
|
+
def path
|
24
|
+
location.path
|
25
|
+
end
|
26
|
+
|
27
|
+
def realpath
|
28
|
+
location.absolute_path
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.pretty_path path
|
32
|
+
return '#<none>' unless path
|
33
|
+
use_short_path = CONFIG[:use_short_path]
|
34
|
+
|
35
|
+
case
|
36
|
+
when use_short_path && path.start_with?(dir = RbConfig::CONFIG["rubylibdir"] + '/')
|
37
|
+
path.sub(dir, '$(rubylibdir)/')
|
38
|
+
when use_short_path && Gem.path.any? do |gp|
|
39
|
+
path.start_with?(dir = gp + '/gems/')
|
40
|
+
end
|
41
|
+
path.sub(dir, '$(Gem)/')
|
42
|
+
when HOME && path.start_with?(HOME)
|
43
|
+
path.sub(HOME, '~/')
|
44
|
+
else
|
45
|
+
path
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def pretty_path
|
50
|
+
FrameInfo.pretty_path path
|
51
|
+
end
|
52
|
+
|
53
|
+
def name
|
54
|
+
# p frame_type: frame_type, self: self
|
55
|
+
case frame_type
|
56
|
+
when :block
|
57
|
+
level, block_loc = block_identifier
|
58
|
+
"block in #{block_loc}#{level}"
|
59
|
+
when :method
|
60
|
+
method_identifier
|
61
|
+
when :c
|
62
|
+
c_identifier
|
63
|
+
when :other
|
64
|
+
other_identifier
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def file_lines
|
69
|
+
SESSION.source(self.iseq)
|
70
|
+
end
|
71
|
+
|
72
|
+
def frame_type
|
73
|
+
if self.local_variables && iseq
|
74
|
+
if iseq.type == :block
|
75
|
+
:block
|
76
|
+
elsif callee
|
77
|
+
:method
|
78
|
+
else
|
79
|
+
:other
|
80
|
+
end
|
81
|
+
else
|
82
|
+
:c
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
BLOCK_LABL_REGEXP = /\Ablock( \(\d+ levels\))* in (.+)\z/
|
87
|
+
|
88
|
+
def block_identifier
|
89
|
+
return unless frame_type == :block
|
90
|
+
re_match = location.label.match(BLOCK_LABL_REGEXP)
|
91
|
+
_, level, block_loc = re_match ? re_match.to_a : [nil, nil, location.label]
|
92
|
+
|
93
|
+
[level || "", block_loc]
|
94
|
+
end
|
95
|
+
|
96
|
+
def method_identifier
|
97
|
+
return unless frame_type == :method
|
98
|
+
"#{klass_sig}#{callee}"
|
99
|
+
end
|
100
|
+
|
101
|
+
def c_identifier
|
102
|
+
return unless frame_type == :c
|
103
|
+
"[C] #{klass_sig}#{location.base_label}"
|
104
|
+
end
|
105
|
+
|
106
|
+
def other_identifier
|
107
|
+
return unless frame_type == :other
|
108
|
+
location.label
|
109
|
+
end
|
110
|
+
|
111
|
+
def callee
|
112
|
+
self._callee ||= begin
|
113
|
+
self.binding&.eval('__callee__')
|
114
|
+
rescue NameError # BasicObject
|
115
|
+
nil
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def return_str
|
120
|
+
if self.binding && iseq && has_return_value
|
121
|
+
DEBUGGER__.safe_inspect(return_value, short: true)
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
def matchable_location
|
126
|
+
# realpath can sometimes be nil so we can't use it here
|
127
|
+
"#{path}:#{location.lineno}"
|
128
|
+
end
|
129
|
+
|
130
|
+
def location_str
|
131
|
+
"#{pretty_path}:#{location.lineno}"
|
132
|
+
end
|
133
|
+
|
134
|
+
def eval_binding
|
135
|
+
if b = self.dupped_binding
|
136
|
+
b
|
137
|
+
else
|
138
|
+
b = self.binding || TOPLEVEL_BINDING
|
139
|
+
self.dupped_binding = b.dup
|
140
|
+
end
|
141
|
+
end
|
142
|
+
|
143
|
+
def local_variables
|
144
|
+
if lvars = self._local_variables
|
145
|
+
lvars
|
146
|
+
elsif b = self.binding
|
147
|
+
b.local_variables.map{|var|
|
148
|
+
[var, b.local_variable_get(var)]
|
149
|
+
}.to_h
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
def iseq_parameters_info
|
154
|
+
case frame_type
|
155
|
+
when :block, :method
|
156
|
+
parameters_info
|
157
|
+
else
|
158
|
+
nil
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
def parameters_info
|
163
|
+
vars = iseq.parameters_symbols
|
164
|
+
vars.map{|var|
|
165
|
+
begin
|
166
|
+
{ name: var, value: DEBUGGER__.safe_inspect(local_variable_get(var), short: true) }
|
167
|
+
rescue NameError, TypeError
|
168
|
+
nil
|
169
|
+
end
|
170
|
+
}.compact
|
171
|
+
end
|
172
|
+
|
173
|
+
private def get_singleton_class obj
|
174
|
+
obj.singleton_class # TODO: don't use it
|
175
|
+
rescue TypeError
|
176
|
+
nil
|
177
|
+
end
|
178
|
+
|
179
|
+
private def local_variable_get var
|
180
|
+
local_variables[var]
|
181
|
+
end
|
182
|
+
|
183
|
+
private def klass_sig
|
184
|
+
if self.class == get_singleton_class(self.self)
|
185
|
+
"#{self.self}."
|
186
|
+
else
|
187
|
+
"#{self.class}#"
|
188
|
+
end
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'irb'
|
4
|
+
|
5
|
+
module DEBUGGER__
|
6
|
+
module IrbPatch
|
7
|
+
def evaluate(line, line_no)
|
8
|
+
SESSION.send(:restart_all_threads)
|
9
|
+
super
|
10
|
+
# This is to communicate with the test framework so it can feed the next input
|
11
|
+
puts "INTERNAL_INFO: {}" if ENV['RUBY_DEBUG_TEST_UI'] == 'terminal'
|
12
|
+
ensure
|
13
|
+
SESSION.send(:stop_all_threads)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
class ThreadClient
|
18
|
+
def activate_irb_integration
|
19
|
+
IRB.setup(location, argv: [])
|
20
|
+
workspace = IRB::WorkSpace.new(current_frame&.binding || TOPLEVEL_BINDING)
|
21
|
+
irb = IRB::Irb.new(workspace)
|
22
|
+
IRB.conf[:MAIN_CONTEXT] = irb.context
|
23
|
+
IRB::Debug.setup(irb)
|
24
|
+
IRB::Context.prepend(IrbPatch)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
class Session
|
29
|
+
def deactivate_irb_integration
|
30
|
+
Reline.completion_proc = nil
|
31
|
+
Reline.output_modifier_proc = nil
|
32
|
+
Reline.autocompletion = false
|
33
|
+
Reline.dig_perfect_match_proc = nil
|
34
|
+
reset_ui UI_LocalConsole.new
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
data/lib/debug/local.rb
ADDED
@@ -0,0 +1,115 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'io/console/size'
|
4
|
+
require_relative 'console'
|
5
|
+
|
6
|
+
module DEBUGGER__
|
7
|
+
class UI_LocalConsole < UI_Base
|
8
|
+
def initialize
|
9
|
+
@console = Console.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def remote?
|
13
|
+
false
|
14
|
+
end
|
15
|
+
|
16
|
+
def activate_sigint
|
17
|
+
prev_handler = trap(:SIGINT){
|
18
|
+
if SESSION.active?
|
19
|
+
ThreadClient.current.on_trap :SIGINT
|
20
|
+
end
|
21
|
+
}
|
22
|
+
SESSION.intercept_trap_sigint_start prev_handler
|
23
|
+
end
|
24
|
+
|
25
|
+
def deactivate_sigint
|
26
|
+
if SESSION.intercept_trap_sigint?
|
27
|
+
prev = SESSION.intercept_trap_sigint_end
|
28
|
+
trap(:SIGINT, prev)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def activate session, on_fork: false
|
33
|
+
activate_sigint unless CONFIG[:no_sigint_hook]
|
34
|
+
end
|
35
|
+
|
36
|
+
def deactivate
|
37
|
+
deactivate_sigint
|
38
|
+
@console.deactivate
|
39
|
+
end
|
40
|
+
|
41
|
+
def width
|
42
|
+
if (w = IO.console_size[1]) == 0 # for tests PTY
|
43
|
+
80
|
44
|
+
else
|
45
|
+
w
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def quit n
|
50
|
+
yield
|
51
|
+
exit n
|
52
|
+
end
|
53
|
+
|
54
|
+
def ask prompt
|
55
|
+
setup_interrupt do
|
56
|
+
print prompt
|
57
|
+
($stdin.gets || '').strip
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def puts str = nil
|
62
|
+
case str
|
63
|
+
when Array
|
64
|
+
str.each{|line|
|
65
|
+
$stdout.puts line.chomp
|
66
|
+
}
|
67
|
+
when String
|
68
|
+
str.each_line{|line|
|
69
|
+
$stdout.puts line.chomp
|
70
|
+
}
|
71
|
+
when nil
|
72
|
+
$stdout.puts
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
def readline prompt = '(rdbg)'
|
77
|
+
setup_interrupt do
|
78
|
+
(@console.readline(prompt) || 'quit').strip
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def setup_interrupt
|
83
|
+
SESSION.intercept_trap_sigint false do
|
84
|
+
current_thread = Thread.current # should be session_server thread
|
85
|
+
|
86
|
+
prev_handler = trap(:INT){
|
87
|
+
current_thread.raise Interrupt
|
88
|
+
}
|
89
|
+
|
90
|
+
yield
|
91
|
+
ensure
|
92
|
+
trap(:INT, prev_handler)
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
def after_fork_parent
|
97
|
+
parent_pid = Process.pid
|
98
|
+
|
99
|
+
at_exit{
|
100
|
+
SESSION.intercept_trap_sigint_end
|
101
|
+
trap(:SIGINT, :IGNORE)
|
102
|
+
|
103
|
+
if Process.pid == parent_pid
|
104
|
+
# only check child process from its parent
|
105
|
+
begin
|
106
|
+
# wait for all child processes to keep terminal
|
107
|
+
Process.waitpid
|
108
|
+
rescue Errno::ESRCH, Errno::ECHILD
|
109
|
+
end
|
110
|
+
end
|
111
|
+
}
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
data/lib/debug/open.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Open the door for the debugger to connect.
|
4
|
+
# Users can connect to debuggee program with "rdbg --attach" option.
|
5
|
+
#
|
6
|
+
# If RUBY_DEBUG_PORT envval is provided (digits), open TCP/IP port.
|
7
|
+
# Otherwise, UNIX domain socket is used.
|
8
|
+
#
|
9
|
+
|
10
|
+
require_relative 'session'
|
11
|
+
return unless defined?(DEBUGGER__)
|
12
|
+
|
13
|
+
DEBUGGER__.open
|
@@ -0,0 +1,15 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Open the door for the debugger to connect.
|
4
|
+
# Unlike debug/open, it does not stop at the beginning of the program.
|
5
|
+
# Users can connect to debuggee program with "rdbg --attach" option or
|
6
|
+
# VSCode attach type.
|
7
|
+
#
|
8
|
+
# If RUBY_DEBUG_PORT envval is provided (digits), open TCP/IP port.
|
9
|
+
# Otherwise, UNIX domain socket is used.
|
10
|
+
#
|
11
|
+
|
12
|
+
require_relative 'session'
|
13
|
+
return unless defined?(DEBUGGER__)
|
14
|
+
|
15
|
+
DEBUGGER__.open(nonstop: true)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
return if ENV['RUBY_DEBUG_ENABLE'] == '0'
|
4
|
+
return if defined?(::DEBUGGER__::Session)
|
5
|
+
|
6
|
+
# Put the following line in your login script (e.g. ~/.bash_profile) with modified path:
|
7
|
+
#
|
8
|
+
# export RUBYOPT="-r /path/to/debug/prelude ${RUBYOPT}"
|
9
|
+
#
|
10
|
+
module Kernel
|
11
|
+
def debugger(*a, up_level: 0, **kw)
|
12
|
+
begin
|
13
|
+
require_relative 'version'
|
14
|
+
cur_version = ::DEBUGGER__::VERSION
|
15
|
+
require_relative 'frame_info'
|
16
|
+
|
17
|
+
if !defined?(::DEBUGGER__::SO_VERSION) || ::DEBUGGER__::VERSION != ::DEBUGGER__::SO_VERSION
|
18
|
+
::Object.send(:remove_const, :DEBUGGER__)
|
19
|
+
raise LoadError
|
20
|
+
end
|
21
|
+
require_relative 'session'
|
22
|
+
up_level += 1
|
23
|
+
rescue LoadError
|
24
|
+
$LOADED_FEATURES.delete_if{|e|
|
25
|
+
e.start_with?(__dir__) || e.end_with?('debug/debug.so')
|
26
|
+
}
|
27
|
+
require 'debug/session'
|
28
|
+
require 'debug/version'
|
29
|
+
::DEBUGGER__.info "Can not activate debug #{cur_version} specified by debug/prelude.rb. Activate debug #{DEBUGGER__::VERSION} instead."
|
30
|
+
up_level += 1
|
31
|
+
end
|
32
|
+
|
33
|
+
::DEBUGGER__::start no_sigint_hook: true, nonstop: true
|
34
|
+
|
35
|
+
begin
|
36
|
+
debugger(*a, up_level: up_level, **kw)
|
37
|
+
self
|
38
|
+
rescue ArgumentError # for 1.2.4 and earlier
|
39
|
+
debugger(*a, **kw)
|
40
|
+
self
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
alias bb debugger if ENV['RUBY_DEBUG_BB']
|
45
|
+
end
|
46
|
+
|
47
|
+
class Binding
|
48
|
+
alias break debugger
|
49
|
+
alias b debugger
|
50
|
+
end
|