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
data/lib/debug/config.rb
ADDED
@@ -0,0 +1,592 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DEBUGGER__
|
4
|
+
LOG_LEVELS = {
|
5
|
+
UNKNOWN: 0,
|
6
|
+
FATAL: 1,
|
7
|
+
ERROR: 2,
|
8
|
+
WARN: 3,
|
9
|
+
INFO: 4,
|
10
|
+
DEBUG: 5
|
11
|
+
}.freeze
|
12
|
+
|
13
|
+
CONFIG_SET = {
|
14
|
+
# UI setting
|
15
|
+
log_level: ['RUBY_DEBUG_LOG_LEVEL', "UI: Log level same as Logger", :loglevel, "WARN"],
|
16
|
+
show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint", :int, "10"],
|
17
|
+
show_src_lines_frame:['RUBY_DEBUG_SHOW_SRC_LINES_FRAME', "UI: Show n lines source code on frame operations", :int, "1"],
|
18
|
+
show_evaledsrc: ['RUBY_DEBUG_SHOW_EVALEDSRC', "UI: Show actually evaluated source", :bool, "false"],
|
19
|
+
show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint", :int, "2"],
|
20
|
+
use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shorten PATH (like $(Gem)/foo.rb)", :bool, "false"],
|
21
|
+
no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize", :bool, "false"],
|
22
|
+
no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT", :bool, "false"],
|
23
|
+
no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library", :bool, "false"],
|
24
|
+
no_hint: ['RUBY_DEBUG_NO_HINT', "UI: Do not show the hint on the REPL", :bool, "false"],
|
25
|
+
no_lineno: ['RUBY_DEBUG_NO_LINENO', "UI: Do not show line numbers", :bool, "false"],
|
26
|
+
no_repeat: ['RUBY_DEBUG_NO_REPEAT', "UI: Do not repeat last line when empty line",:bool, "false"],
|
27
|
+
irb_console: ["RUBY_DEBUG_IRB_CONSOLE", "UI: Use IRB as the console", :bool, "false"],
|
28
|
+
|
29
|
+
# control setting
|
30
|
+
skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths", :path],
|
31
|
+
skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "CONTROL: Skip on no source code lines", :bool, "false"],
|
32
|
+
keep_alloc_site: ['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it", :bool, "false"],
|
33
|
+
postmortem: ['RUBY_DEBUG_POSTMORTEM', "CONTROL: Enable postmortem debug", :bool, "false"],
|
34
|
+
fork_mode: ['RUBY_DEBUG_FORK_MODE', "CONTROL: Control which process activates a debugger after fork (both/parent/child)", :forkmode, "both"],
|
35
|
+
sigdump_sig: ['RUBY_DEBUG_SIGDUMP_SIG', "CONTROL: Sigdump signal", :bool, "false"],
|
36
|
+
|
37
|
+
# boot setting
|
38
|
+
nonstop: ['RUBY_DEBUG_NONSTOP', "BOOT: Nonstop mode", :bool, "false"],
|
39
|
+
stop_at_load: ['RUBY_DEBUG_STOP_AT_LOAD',"BOOT: Stop at just loading location", :bool, "false"],
|
40
|
+
init_script: ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
|
41
|
+
commands: ['RUBY_DEBUG_COMMANDS', "BOOT: debug commands invoked at first stop. Commands should be separated by `;;`"],
|
42
|
+
no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool, "false"],
|
43
|
+
history_file: ['RUBY_DEBUG_HISTORY_FILE',"BOOT: history file (default: ${XDG_STATE_HOME-~/.local/state}/rdbg/history)", :string, nil],
|
44
|
+
save_history: ['RUBY_DEBUG_SAVE_HISTORY',"BOOT: maximum save history lines", :int, "10000"],
|
45
|
+
|
46
|
+
# remote setting
|
47
|
+
open: ['RUBY_DEBUG_OPEN', "REMOTE: Open remote port (same as `rdbg --open` option)"],
|
48
|
+
port: ['RUBY_DEBUG_PORT', "REMOTE: TCP/IP remote debugging: port"],
|
49
|
+
port_range: ['RUBY_DEBUG_PORT_RANGE', "REMOTE: TCP/IP remote debugging: length of port range"],
|
50
|
+
host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host", :string, "127.0.0.1"],
|
51
|
+
sock_path: ['RUBY_DEBUG_SOCK_PATH', "REMOTE: UNIX Domain Socket remote debugging: socket path"],
|
52
|
+
sock_dir: ['RUBY_DEBUG_SOCK_DIR', "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
|
53
|
+
local_fs_map: ['RUBY_DEBUG_LOCAL_FS_MAP', "REMOTE: Specify local fs map", :path_map],
|
54
|
+
skip_bp: ['RUBY_DEBUG_SKIP_BP', "REMOTE: Skip breakpoints if no clients are attached", :bool, 'false'],
|
55
|
+
cookie: ['RUBY_DEBUG_COOKIE', "REMOTE: Cookie for negotiation"],
|
56
|
+
session_name: ['RUBY_DEBUG_SESSION_NAME', "REMOTE: Session name for differentiating multiple sessions"],
|
57
|
+
chrome_path: ['RUBY_DEBUG_CHROME_PATH', "REMOTE: Platform dependent path of Chrome (For more information, See [here](https://github.com/ruby/debug/pull/334/files#diff-5fc3d0a901379a95bc111b86cf0090b03f857edfd0b99a0c1537e26735698453R55-R64))"],
|
58
|
+
|
59
|
+
# obsolete
|
60
|
+
parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "OBSOLETE: Keep debugging parent process on fork", :bool, "false"],
|
61
|
+
}.freeze
|
62
|
+
|
63
|
+
CONFIG_MAP = CONFIG_SET.map{|k, (ev, _)| [k, ev]}.to_h.freeze
|
64
|
+
|
65
|
+
class Config
|
66
|
+
@config = nil
|
67
|
+
|
68
|
+
def self.config
|
69
|
+
@config
|
70
|
+
end
|
71
|
+
|
72
|
+
def initialize argv
|
73
|
+
if self.class.config
|
74
|
+
raise 'Can not make multiple configurations in one process'
|
75
|
+
end
|
76
|
+
|
77
|
+
config = self.class.parse_argv(argv)
|
78
|
+
|
79
|
+
# apply defaults
|
80
|
+
CONFIG_SET.each do |k, config_detail|
|
81
|
+
unless config.key?(k)
|
82
|
+
default_value = config_detail[3]
|
83
|
+
config[k] = parse_config_value(k, default_value)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
update config
|
88
|
+
end
|
89
|
+
|
90
|
+
def inspect
|
91
|
+
config.inspect
|
92
|
+
end
|
93
|
+
|
94
|
+
def [](key)
|
95
|
+
config[key]
|
96
|
+
end
|
97
|
+
|
98
|
+
def []=(key, val)
|
99
|
+
set_config(key => val)
|
100
|
+
end
|
101
|
+
|
102
|
+
def set_config(**kw)
|
103
|
+
conf = config.dup
|
104
|
+
kw.each{|k, v|
|
105
|
+
if CONFIG_MAP[k]
|
106
|
+
conf[k] = parse_config_value(k, v) # TODO: ractor support
|
107
|
+
else
|
108
|
+
raise "Unknown configuration: #{k}"
|
109
|
+
end
|
110
|
+
}
|
111
|
+
|
112
|
+
update conf
|
113
|
+
end
|
114
|
+
|
115
|
+
def append_config key, val
|
116
|
+
conf = config.dup
|
117
|
+
|
118
|
+
if CONFIG_SET[key]
|
119
|
+
if CONFIG_SET[key][2] == :path
|
120
|
+
conf[key] = [*conf[key], *parse_config_value(key, val)];
|
121
|
+
else
|
122
|
+
raise "not an Array type: #{key}"
|
123
|
+
end
|
124
|
+
else
|
125
|
+
raise "Unknown configuration: #{key}"
|
126
|
+
end
|
127
|
+
|
128
|
+
update conf
|
129
|
+
end
|
130
|
+
|
131
|
+
def update conf
|
132
|
+
old_conf = self.class.instance_variable_get(:@config) || {}
|
133
|
+
|
134
|
+
# TODO: Use Ractor.make_shareable(conf)
|
135
|
+
self.class.instance_variable_set(:@config, conf.freeze)
|
136
|
+
|
137
|
+
# Post process
|
138
|
+
if_updated old_conf, conf, :keep_alloc_site do |old, new|
|
139
|
+
if new
|
140
|
+
require 'objspace'
|
141
|
+
ObjectSpace.trace_object_allocations_start
|
142
|
+
end
|
143
|
+
|
144
|
+
if old && !new
|
145
|
+
ObjectSpace.trace_object_allocations_stop
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
if_updated old_conf, conf, :postmortem do |_, new_p|
|
150
|
+
if defined?(SESSION)
|
151
|
+
SESSION.postmortem = new_p
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
if_updated old_conf, conf, :sigdump_sig do |old_sig, new_sig|
|
156
|
+
setup_sigdump old_sig, new_sig
|
157
|
+
end
|
158
|
+
|
159
|
+
if_updated old_conf, conf, :no_sigint_hook do |old, new|
|
160
|
+
if defined?(SESSION)
|
161
|
+
SESSION.set_no_sigint_hook old, new
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
if_updated old_conf, conf, :irb_console do |old, new|
|
166
|
+
if defined?(SESSION) && SESSION.active?
|
167
|
+
# irb_console is switched from true to false
|
168
|
+
if old
|
169
|
+
SESSION.deactivate_irb_integration
|
170
|
+
# irb_console is switched from false to true
|
171
|
+
else
|
172
|
+
if CONFIG[:open]
|
173
|
+
SESSION.instance_variable_get(:@ui).puts "\nIRB is not supported on the remote console."
|
174
|
+
else
|
175
|
+
SESSION.activate_irb_integration
|
176
|
+
end
|
177
|
+
end
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
private def if_updated old_conf, new_conf, key
|
183
|
+
old, new = old_conf[key], new_conf[key]
|
184
|
+
yield old, new if old != new
|
185
|
+
end
|
186
|
+
|
187
|
+
private def enable_sigdump sig
|
188
|
+
@sigdump_sig_prev = trap(sig) do
|
189
|
+
str = []
|
190
|
+
str << "Simple sigdump on #{Process.pid}"
|
191
|
+
Thread.list.each{|th|
|
192
|
+
str << "Thread: #{th}"
|
193
|
+
th.backtrace.each{|loc|
|
194
|
+
str << " #{loc}"
|
195
|
+
}
|
196
|
+
str << ''
|
197
|
+
}
|
198
|
+
|
199
|
+
STDERR.puts str
|
200
|
+
end
|
201
|
+
end
|
202
|
+
|
203
|
+
private def disable_sigdump old_sig
|
204
|
+
trap(old_sig, @sigdump_sig_prev)
|
205
|
+
@sigdump_sig_prev = nil
|
206
|
+
end
|
207
|
+
|
208
|
+
# emergency simple sigdump.
|
209
|
+
# Use `sigdump` gem for more rich features.
|
210
|
+
private def setup_sigdump old_sig = nil, sig = CONFIG[:sigdump_sig]
|
211
|
+
if !old_sig && sig
|
212
|
+
enable_sigdump sig
|
213
|
+
elsif old_sig && !sig
|
214
|
+
disable_sigdump old_sig
|
215
|
+
elsif old_sig && sig
|
216
|
+
disable_sigdump old_sig
|
217
|
+
enable_sigdump sig
|
218
|
+
end
|
219
|
+
end
|
220
|
+
|
221
|
+
private def config
|
222
|
+
self.class.config
|
223
|
+
end
|
224
|
+
|
225
|
+
private def parse_config_value name, valstr
|
226
|
+
self.class.parse_config_value name, valstr
|
227
|
+
end
|
228
|
+
|
229
|
+
def self.parse_config_value name, valstr
|
230
|
+
return valstr unless valstr.kind_of? String
|
231
|
+
|
232
|
+
case CONFIG_SET[name][2]
|
233
|
+
when :bool
|
234
|
+
case valstr
|
235
|
+
when '1', 'true', 'TRUE', 'T'
|
236
|
+
true
|
237
|
+
else
|
238
|
+
false
|
239
|
+
end
|
240
|
+
when :int
|
241
|
+
valstr.to_i
|
242
|
+
when :loglevel
|
243
|
+
if DEBUGGER__::LOG_LEVELS[s = valstr.to_sym]
|
244
|
+
s
|
245
|
+
else
|
246
|
+
raise "Unknown loglevel: #{valstr}"
|
247
|
+
end
|
248
|
+
when :forkmode
|
249
|
+
case sym = valstr.to_sym
|
250
|
+
when :parent, :child, :both, nil
|
251
|
+
sym
|
252
|
+
else
|
253
|
+
raise "unknown fork mode: #{sym}"
|
254
|
+
end
|
255
|
+
when :path # array of String
|
256
|
+
valstr.split(/:/).map{|e|
|
257
|
+
if /\A\/(.+)\/\z/ =~ e
|
258
|
+
Regexp.compile $1
|
259
|
+
else
|
260
|
+
e
|
261
|
+
end
|
262
|
+
}
|
263
|
+
when :path_map
|
264
|
+
valstr.split(',').map{|e| e.split(':')}
|
265
|
+
else
|
266
|
+
valstr
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
def self.parse_argv argv
|
271
|
+
config = {
|
272
|
+
mode: :start,
|
273
|
+
no_color: (nc = ENV['NO_COLOR']) && !nc.empty?,
|
274
|
+
}
|
275
|
+
CONFIG_MAP.each{|key, evname|
|
276
|
+
if val = ENV[evname]
|
277
|
+
config[key] = parse_config_value(key, val)
|
278
|
+
end
|
279
|
+
}
|
280
|
+
return config if !argv || argv.empty?
|
281
|
+
|
282
|
+
if argv.kind_of? String
|
283
|
+
require 'shellwords'
|
284
|
+
argv = Shellwords.split(argv)
|
285
|
+
end
|
286
|
+
|
287
|
+
require 'optparse'
|
288
|
+
require_relative 'version'
|
289
|
+
|
290
|
+
have_shown_version = false
|
291
|
+
|
292
|
+
opt = OptionParser.new do |o|
|
293
|
+
o.banner = "#{$0} [options] -- [debuggee options]"
|
294
|
+
o.separator ''
|
295
|
+
o.version = ::DEBUGGER__::VERSION
|
296
|
+
|
297
|
+
o.separator 'Debug console mode:'
|
298
|
+
o.on('-n', '--nonstop', 'Do not stop at the beginning of the script.') do
|
299
|
+
config[:nonstop] = '1'
|
300
|
+
end
|
301
|
+
|
302
|
+
o.on('-e DEBUG_COMMAND', 'Execute debug command at the beginning of the script.') do |cmd|
|
303
|
+
config[:commands] ||= ''
|
304
|
+
config[:commands] += cmd + ';;'
|
305
|
+
end
|
306
|
+
|
307
|
+
o.on('-x FILE', '--init-script=FILE', 'Execute debug command in the FILE.') do |file|
|
308
|
+
config[:init_script] = file
|
309
|
+
end
|
310
|
+
o.on('--no-rc', 'Ignore ~/.rdbgrc') do
|
311
|
+
config[:no_rc] = true
|
312
|
+
end
|
313
|
+
o.on('--no-color', 'Disable colorize') do
|
314
|
+
config[:no_color] = true
|
315
|
+
end
|
316
|
+
o.on('--no-sigint-hook', 'Disable to trap SIGINT') do
|
317
|
+
config[:no_sigint_hook] = true
|
318
|
+
end
|
319
|
+
|
320
|
+
o.on('-c', '--command', 'Enable command mode.',
|
321
|
+
'The first argument should be a command name in $PATH.',
|
322
|
+
'Example: \'rdbg -c bundle exec rake test\'') do
|
323
|
+
config[:command] = true
|
324
|
+
end
|
325
|
+
|
326
|
+
o.separator ''
|
327
|
+
|
328
|
+
o.on('-O', '--open=[FRONTEND]', 'Start remote debugging with opening the network port.',
|
329
|
+
'If TCP/IP options are not given, a UNIX domain socket will be used.',
|
330
|
+
'If FRONTEND is given, prepare for the FRONTEND.',
|
331
|
+
'Now rdbg, vscode and chrome is supported.') do |f|
|
332
|
+
|
333
|
+
case f # some format patterns are not documented yet
|
334
|
+
when nil
|
335
|
+
config[:open] = true
|
336
|
+
when /\A\d\z/
|
337
|
+
config[:open] = true
|
338
|
+
config[:port] = f.to_i
|
339
|
+
when /\A(\S+):(\d+)\z/
|
340
|
+
config[:open] = true
|
341
|
+
config[:host] = $1
|
342
|
+
config[:port] = $2.to_i
|
343
|
+
when 'tcp'
|
344
|
+
config[:open] = true
|
345
|
+
config[:port] ||= 0
|
346
|
+
when 'vscode', 'chrome', 'cdp'
|
347
|
+
config[:open] = f&.downcase
|
348
|
+
else
|
349
|
+
raise "Unknown option for --open: #{f}"
|
350
|
+
end
|
351
|
+
end
|
352
|
+
o.on('--sock-path=SOCK_PATH', 'UNIX Domain socket path') do |path|
|
353
|
+
config[:sock_path] = path
|
354
|
+
end
|
355
|
+
o.on('--port=PORT', 'Listening TCP/IP port') do |port|
|
356
|
+
config[:port] = port
|
357
|
+
end
|
358
|
+
o.on('--port-range=PORT_RANGE', 'Number of ports to try to connect to') do |port_range|
|
359
|
+
config[:port_range] = port_range
|
360
|
+
end
|
361
|
+
o.on('--host=HOST', 'Listening TCP/IP host') do |host|
|
362
|
+
config[:host] = host
|
363
|
+
end
|
364
|
+
o.on('--cookie=COOKIE', 'Set a cookie for connection') do |c|
|
365
|
+
config[:cookie] = c
|
366
|
+
end
|
367
|
+
o.on('--session-name=NAME', 'Session name') do |name|
|
368
|
+
config[:session_name] = name
|
369
|
+
end
|
370
|
+
|
371
|
+
rdbg = 'rdbg'
|
372
|
+
|
373
|
+
o.separator ''
|
374
|
+
o.separator ' Debug console mode runs Ruby program with the debug console.'
|
375
|
+
o.separator ''
|
376
|
+
o.separator " '#{rdbg} target.rb foo bar' starts like 'ruby target.rb foo bar'."
|
377
|
+
o.separator " '#{rdbg} -- -r foo -e bar' starts like 'ruby -r foo -e bar'."
|
378
|
+
o.separator " '#{rdbg} -c rake test' starts like 'rake test'."
|
379
|
+
o.separator " '#{rdbg} -c -- rake test -t' starts like 'rake test -t'."
|
380
|
+
o.separator " '#{rdbg} -c bundle exec rake test' starts like 'bundle exec rake test'."
|
381
|
+
o.separator " '#{rdbg} -O target.rb foo bar' starts and accepts attaching with UNIX domain socket."
|
382
|
+
o.separator " '#{rdbg} -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234."
|
383
|
+
o.separator " '#{rdbg} -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234."
|
384
|
+
o.separator " '#{rdbg} target.rb -O chrome --port 1234' starts and accepts connecting from Chrome Devtools with localhost:1234."
|
385
|
+
|
386
|
+
o.separator ''
|
387
|
+
o.separator 'Attach mode:'
|
388
|
+
o.on('-A', '--attach', 'Attach to debuggee process.') do
|
389
|
+
config[:mode] = :attach
|
390
|
+
end
|
391
|
+
|
392
|
+
o.separator ''
|
393
|
+
o.separator ' Attach mode attaches the remote debug console to the debuggee process.'
|
394
|
+
o.separator ''
|
395
|
+
o.separator " '#{rdbg} -A' tries to connect via UNIX domain socket."
|
396
|
+
o.separator " #{' ' * rdbg.size} If there are multiple processes are waiting for the"
|
397
|
+
o.separator " #{' ' * rdbg.size} debugger connection, list possible debuggee names."
|
398
|
+
o.separator " '#{rdbg} -A path' tries to connect via UNIX domain socket with given path name."
|
399
|
+
o.separator " '#{rdbg} -A port' tries to connect to localhost:port via TCP/IP."
|
400
|
+
o.separator " '#{rdbg} -A host port' tries to connect to host:port via TCP/IP."
|
401
|
+
|
402
|
+
o.separator ''
|
403
|
+
o.separator 'Other options:'
|
404
|
+
|
405
|
+
o.on('-v', 'Show version number') do
|
406
|
+
puts o.ver
|
407
|
+
have_shown_version = true
|
408
|
+
end
|
409
|
+
|
410
|
+
o.on('--version', 'Show version number and exit') do
|
411
|
+
puts o.ver
|
412
|
+
exit
|
413
|
+
end
|
414
|
+
|
415
|
+
o.on("-h", "--help", "Print help") do
|
416
|
+
puts o
|
417
|
+
exit
|
418
|
+
end
|
419
|
+
|
420
|
+
o.on('--util=NAME', 'Utility mode (used by tools)') do |name|
|
421
|
+
require_relative 'client'
|
422
|
+
Client.util(name)
|
423
|
+
exit
|
424
|
+
end
|
425
|
+
|
426
|
+
o.on('--stop-at-load', 'Stop immediately when the debugging feature is loaded.') do
|
427
|
+
config[:stop_at_load] = true
|
428
|
+
end
|
429
|
+
|
430
|
+
o.separator ''
|
431
|
+
o.separator 'NOTE'
|
432
|
+
o.separator ' All messages communicated between a debugger and a debuggee are *NOT* encrypted.'
|
433
|
+
o.separator ' Please use the remote debugging feature carefully.'
|
434
|
+
end
|
435
|
+
|
436
|
+
opt.parse!(argv)
|
437
|
+
|
438
|
+
if argv.empty?
|
439
|
+
case
|
440
|
+
when have_shown_version && config[:mode] == :start
|
441
|
+
exit
|
442
|
+
end
|
443
|
+
end
|
444
|
+
|
445
|
+
config
|
446
|
+
end
|
447
|
+
|
448
|
+
def self.config_to_env_hash config
|
449
|
+
CONFIG_MAP.each_with_object({}){|(key, evname), env|
|
450
|
+
unless config[key].nil?
|
451
|
+
case CONFIG_SET[key][2]
|
452
|
+
when :path
|
453
|
+
valstr = config[key].map{|e| e.kind_of?(Regexp) ? e.inspect : e}.join(':')
|
454
|
+
when :path_map
|
455
|
+
valstr = config[key].map{|e| e.join(':')}.join(',')
|
456
|
+
else
|
457
|
+
valstr = config[key].to_s
|
458
|
+
end
|
459
|
+
env[evname] = valstr
|
460
|
+
end
|
461
|
+
}
|
462
|
+
end
|
463
|
+
end
|
464
|
+
|
465
|
+
CONFIG = Config.new ENV['RUBY_DEBUG_OPT']
|
466
|
+
|
467
|
+
## Unix domain socket configuration
|
468
|
+
|
469
|
+
def self.check_dir_authority path
|
470
|
+
fs = File.stat(path)
|
471
|
+
|
472
|
+
unless (dir_uid = fs.uid) == (uid = Process.uid)
|
473
|
+
raise "#{path} uid is #{dir_uid}, but Process.uid is #{uid}"
|
474
|
+
end
|
475
|
+
|
476
|
+
if fs.world_writable? && !fs.sticky?
|
477
|
+
raise "#{path} is world writable but not sticky"
|
478
|
+
end
|
479
|
+
|
480
|
+
path
|
481
|
+
end
|
482
|
+
|
483
|
+
def self.unix_domain_socket_tmpdir
|
484
|
+
require 'tmpdir'
|
485
|
+
|
486
|
+
if tmpdir = Dir.tmpdir
|
487
|
+
path = File.join(tmpdir, "rdbg-#{Process.uid}")
|
488
|
+
|
489
|
+
unless File.exist?(path)
|
490
|
+
d = Dir.mktmpdir
|
491
|
+
File.rename(d, path)
|
492
|
+
end
|
493
|
+
|
494
|
+
check_dir_authority(path)
|
495
|
+
end
|
496
|
+
end
|
497
|
+
|
498
|
+
def self.unix_domain_socket_homedir
|
499
|
+
if home = ENV['HOME']
|
500
|
+
path = File.join(home, '.rdbg-sock')
|
501
|
+
|
502
|
+
unless File.exist?(path)
|
503
|
+
Dir.mkdir(path, 0700)
|
504
|
+
end
|
505
|
+
|
506
|
+
check_dir_authority(path)
|
507
|
+
end
|
508
|
+
end
|
509
|
+
|
510
|
+
def self.unix_domain_socket_dir
|
511
|
+
case
|
512
|
+
when path = CONFIG[:sock_dir]
|
513
|
+
when path = ENV['XDG_RUNTIME_DIR']
|
514
|
+
when path = unix_domain_socket_tmpdir
|
515
|
+
when path = unix_domain_socket_homedir
|
516
|
+
else
|
517
|
+
raise 'specify RUBY_DEBUG_SOCK_DIR environment variable.'
|
518
|
+
end
|
519
|
+
|
520
|
+
path
|
521
|
+
end
|
522
|
+
|
523
|
+
def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir)
|
524
|
+
File.join(base_dir, "rdbg")
|
525
|
+
end
|
526
|
+
|
527
|
+
def self.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir)
|
528
|
+
suffix = "-#{Process.pid}"
|
529
|
+
name = CONFIG[:session_name]
|
530
|
+
suffix << "-#{name}" if name
|
531
|
+
create_unix_domain_socket_name_prefix(base_dir) + suffix
|
532
|
+
end
|
533
|
+
|
534
|
+
## Help
|
535
|
+
|
536
|
+
def self.parse_help
|
537
|
+
helps = Hash.new{|h, k| h[k] = []}
|
538
|
+
desc = cat = nil
|
539
|
+
cmds = Hash.new
|
540
|
+
|
541
|
+
File.read(File.join(__dir__, 'session.rb'), encoding: Encoding::UTF_8).each_line do |line|
|
542
|
+
case line
|
543
|
+
when /\A\s*### (.+)/
|
544
|
+
cat = $1
|
545
|
+
break if $1 == 'END'
|
546
|
+
when /\A register_command (.+)/
|
547
|
+
next unless cat
|
548
|
+
next unless desc
|
549
|
+
|
550
|
+
ws = []
|
551
|
+
$1.gsub(/'([a-z]+)'/){|w|
|
552
|
+
ws << $1
|
553
|
+
}
|
554
|
+
helps[cat] << [ws, desc]
|
555
|
+
desc = nil
|
556
|
+
max_w = ws.max_by{|w| w.length}
|
557
|
+
ws.each{|w|
|
558
|
+
cmds[w] = max_w
|
559
|
+
}
|
560
|
+
when /\A\s+# (\s*\*.+)/
|
561
|
+
if desc
|
562
|
+
desc << "\n" + $1
|
563
|
+
else
|
564
|
+
desc = $1
|
565
|
+
end
|
566
|
+
end
|
567
|
+
end
|
568
|
+
@commands = cmds
|
569
|
+
@helps = helps
|
570
|
+
end
|
571
|
+
|
572
|
+
def self.helps
|
573
|
+
(defined?(@helps) && @helps) || parse_help
|
574
|
+
end
|
575
|
+
|
576
|
+
def self.commands
|
577
|
+
(defined?(@commands) && @commands) || (parse_help; @commands)
|
578
|
+
end
|
579
|
+
|
580
|
+
def self.help
|
581
|
+
r = []
|
582
|
+
self.helps.each{|cat, cmds|
|
583
|
+
r << "### #{cat}"
|
584
|
+
r << ''
|
585
|
+
cmds.each{|_, desc|
|
586
|
+
r << desc
|
587
|
+
}
|
588
|
+
r << ''
|
589
|
+
}
|
590
|
+
r.join("\n")
|
591
|
+
end
|
592
|
+
end
|