debug 1.0.0.beta5 → 1.0.0.rc1
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 +213 -20
- data/Gemfile +1 -0
- data/README.md +460 -226
- data/Rakefile +2 -1
- data/TODO.md +0 -6
- data/bin/gentest +22 -0
- data/debug.gemspec +1 -0
- data/exe/rdbg +11 -18
- data/ext/debug/debug.c +11 -1
- data/lib/debug/breakpoint.rb +106 -62
- data/lib/debug/client.rb +11 -17
- data/lib/debug/color.rb +28 -7
- data/lib/debug/config.rb +378 -144
- data/lib/debug/console.rb +79 -57
- data/lib/debug/frame_info.rb +42 -8
- data/lib/debug/local.rb +91 -0
- data/lib/debug/open.rb +2 -1
- data/lib/debug/open_nonstop.rb +15 -0
- data/lib/debug/server.rb +96 -43
- data/lib/debug/server_dap.rb +34 -7
- data/lib/debug/session.rb +827 -341
- data/lib/debug/source_repository.rb +2 -0
- data/lib/debug/start.rb +5 -0
- data/lib/debug/thread_client.rb +691 -184
- data/lib/debug/tracer.rb +242 -0
- data/lib/debug/version.rb +3 -1
- data/lib/debug.rb +3 -0
- data/misc/README.md.erb +341 -216
- metadata +21 -4
- data/lib/debug/run.rb +0 -4
- data/lib/debug/test_console.rb +0 -0
data/lib/debug/client.rb
CHANGED
@@ -1,8 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'socket'
|
2
4
|
require 'io/console/size'
|
3
5
|
|
4
6
|
require_relative 'config'
|
5
7
|
require_relative 'version'
|
8
|
+
require_relative 'console'
|
6
9
|
|
7
10
|
# $VERBOSE = true
|
8
11
|
|
@@ -10,21 +13,11 @@ module DEBUGGER__
|
|
10
13
|
class CommandLineOptionError < Exception; end
|
11
14
|
|
12
15
|
class Client
|
13
|
-
begin
|
14
|
-
require 'readline'
|
15
|
-
def readline
|
16
|
-
Readline.readline("\n(rdb) ", true)
|
17
|
-
end
|
18
|
-
rescue LoadError
|
19
|
-
def readline
|
20
|
-
print "\n(rdb) "
|
21
|
-
gets
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
16
|
def initialize argv
|
26
17
|
return util(argv) if String === argv
|
27
18
|
|
19
|
+
@console = Console.new
|
20
|
+
|
28
21
|
case argv.size
|
29
22
|
when 0
|
30
23
|
connect_unix
|
@@ -45,11 +38,16 @@ module DEBUGGER__
|
|
45
38
|
end
|
46
39
|
|
47
40
|
@width = IO.console_size[1]
|
41
|
+
@width = 80 if @width == 0
|
48
42
|
@width_changed = false
|
49
43
|
|
50
44
|
send "version: #{VERSION} width: #{@width} cookie: #{CONFIG[:cookie]}"
|
51
45
|
end
|
52
46
|
|
47
|
+
def readline
|
48
|
+
@console.readline "(rdbg:remote) "
|
49
|
+
end
|
50
|
+
|
53
51
|
def util name
|
54
52
|
case name
|
55
53
|
when 'gen-sockpath'
|
@@ -162,10 +160,6 @@ module DEBUGGER__
|
|
162
160
|
end
|
163
161
|
end
|
164
162
|
|
165
|
-
def connect argv = ARGV
|
166
|
-
DEBUGGER__::Client.new(argv).connect
|
167
|
-
end
|
168
|
-
|
169
163
|
if __FILE__ == $0
|
170
|
-
connect
|
164
|
+
DEBUGGER__::Client.new(argv).connect
|
171
165
|
end
|
data/lib/debug/color.rb
CHANGED
@@ -1,5 +1,14 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
begin
|
2
4
|
require 'irb/color'
|
5
|
+
|
6
|
+
module IRB
|
7
|
+
module Color
|
8
|
+
DIM = 2 unless defined? DIM
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
3
12
|
require "irb/color_printer"
|
4
13
|
rescue LoadError
|
5
14
|
warn "DEBUGGER: can not load newer irb for coloring. Write 'gem \"debug\" in your Gemfile."
|
@@ -9,7 +18,7 @@ module DEBUGGER__
|
|
9
18
|
module Color
|
10
19
|
if defined? IRB::Color.colorize
|
11
20
|
def colorize str, color
|
12
|
-
if CONFIG[:
|
21
|
+
if !CONFIG[:no_color]
|
13
22
|
IRB::Color.colorize str, color
|
14
23
|
else
|
15
24
|
str
|
@@ -22,18 +31,22 @@ module DEBUGGER__
|
|
22
31
|
end
|
23
32
|
|
24
33
|
if defined? IRB::ColorPrinter.pp
|
25
|
-
def color_pp obj
|
26
|
-
|
34
|
+
def color_pp obj, width
|
35
|
+
if !CONFIG[:no_color]
|
36
|
+
IRB::ColorPrinter.pp(obj, "".dup, width)
|
37
|
+
else
|
38
|
+
obj.pretty_inspect
|
39
|
+
end
|
27
40
|
end
|
28
41
|
else
|
29
|
-
def color_pp obj
|
42
|
+
def color_pp obj, width
|
30
43
|
obj.pretty_inspect
|
31
44
|
end
|
32
45
|
end
|
33
46
|
|
34
|
-
def colored_inspect obj
|
35
|
-
if
|
36
|
-
color_pp obj
|
47
|
+
def colored_inspect obj, width: SESSION.width, no_color: false
|
48
|
+
if !no_color
|
49
|
+
color_pp obj, width
|
37
50
|
else
|
38
51
|
obj.pretty_inspect
|
39
52
|
end
|
@@ -66,5 +79,13 @@ module DEBUGGER__
|
|
66
79
|
def colorize_blue(str)
|
67
80
|
colorize(str, [:BLUE, :BOLD])
|
68
81
|
end
|
82
|
+
|
83
|
+
def colorize_magenta(str)
|
84
|
+
colorize(str, [:MAGENTA, :BOLD])
|
85
|
+
end
|
86
|
+
|
87
|
+
def colorize_dim(str)
|
88
|
+
colorize(str, [:DIM])
|
89
|
+
end
|
69
90
|
end
|
70
91
|
end
|
data/lib/debug/config.rb
CHANGED
@@ -1,188 +1,422 @@
|
|
1
|
+
# frozen_string_literal: true
|
1
2
|
|
2
3
|
module DEBUGGER__
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
4
|
+
CONFIG_SET = {
|
5
|
+
# UI setting
|
6
|
+
log_level: ['RUBY_DEBUG_LOG_LEVEL', "UI: Log level same as Logger (default: WARN)", :loglevel],
|
7
|
+
show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint (default: 10 lines)", :int],
|
8
|
+
show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint (default: 2 frames)", :int],
|
9
|
+
use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shoten PATH (like $(Gem)/foo.rb)", :bool],
|
10
|
+
no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize (default: false)", :bool],
|
11
|
+
no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT (default: false)", :bool],
|
12
|
+
no_reline: ['RUBY_DEBUG_NO_RELINE', "UI: Do not use Reline library (default: false)", :bool],
|
9
13
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
14
|
+
# control setting
|
15
|
+
skip_path: ['RUBY_DEBUG_SKIP_PATH', "CONTROL: Skip showing/entering frames for given paths (default: [])", :path],
|
16
|
+
skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "CONTROL: Skip on no source code lines (default: false)", :bool],
|
17
|
+
keep_alloc_site:['RUBY_DEBUG_KEEP_ALLOC_SITE',"CONTROL: Keep allocation site and p, pp shows it (default: false)", :bool],
|
18
|
+
postmortem: ['RUBY_DEBUG_POSTMORTEM', "CONTROL: Enable postmortem debug (default: false)", :bool],
|
19
|
+
parent_on_fork: ['RUBY_DEBUG_PARENT_ON_FORK', "CONTROL: Keep debugging parent process on fork (default: false)", :bool],
|
20
|
+
sigdump_sig: ['RUBY_DEBUG_SIGDUMP_SIG', "CONTROL: Sigdump signal (default: disabled)"],
|
21
|
+
|
22
|
+
# boot setting
|
23
|
+
nonstop: ['RUBY_DEBUG_NONSTOP', "BOOT: Nonstop mode", :bool],
|
24
|
+
init_script: ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
|
25
|
+
commands: ['RUBY_DEBUG_COMMANDS', "BOOT: debug commands invoked at first stop. commands should be separated by ';;'"],
|
26
|
+
no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool],
|
27
|
+
|
28
|
+
# remote setting
|
29
|
+
port: ['RUBY_DEBUG_PORT', "REMOTE: TCP/IP remote debugging: port"],
|
30
|
+
host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host (localhost if not given)"],
|
31
|
+
sock_path: ['RUBY_DEBUG_SOCK_PATH', "REMOTE: UNIX Domain Socket remote debugging: socket path"],
|
32
|
+
sock_dir: ['RUBY_DEBUG_SOCK_DIR', "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
|
33
|
+
cookie: ['RUBY_DEBUG_COOKIE', "REMOTE: Cookie for negotiation"],
|
34
|
+
}.freeze
|
35
|
+
|
36
|
+
CONFIG_MAP = CONFIG_SET.map{|k, (ev, desc)| [k, ev]}.to_h.freeze
|
37
|
+
|
38
|
+
class Config
|
39
|
+
def self.config
|
40
|
+
@config
|
18
41
|
end
|
19
42
|
|
20
|
-
|
21
|
-
|
43
|
+
def initialize argv
|
44
|
+
if self.class.instance_variable_defined? :@config
|
45
|
+
raise 'Can not make multiple configurations in one process'
|
46
|
+
end
|
22
47
|
|
23
|
-
|
24
|
-
|
25
|
-
File.join(base_dir, "ruby-debug-#{user}")
|
26
|
-
end
|
48
|
+
update self.class.parse_argv(argv)
|
49
|
+
end
|
27
50
|
|
28
|
-
|
29
|
-
|
30
|
-
|
51
|
+
def [](key)
|
52
|
+
config[key]
|
53
|
+
end
|
31
54
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
init_script: 'RUBY_DEBUG_INIT_SCRIPT', # debug command script path loaded at first stop
|
36
|
-
commands: 'RUBY_DEBUG_COMMANDS', # debug commands invoked at first stop. commands should be separated by ';;'
|
55
|
+
def []=(key, val)
|
56
|
+
set_config(key => val)
|
57
|
+
end
|
37
58
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
host: 'RUBY_DEBUG_HOST', # TCP/IP remote debugging: host (localhost if not given)
|
48
|
-
sock_path: 'RUBY_DEBUG_SOCK_PATH', # UNIX Domain Socket remote debugging: socket path
|
49
|
-
sock_dir: 'RUBY_DEBUG_SOCK_DIR', # UNIX Domain Socket remote debugging: socket directory
|
50
|
-
cookie: 'RUBY_DEBUG_COOKIE', # Cookie for negotiation
|
51
|
-
}.freeze
|
59
|
+
def set_config(**kw)
|
60
|
+
conf = config.dup
|
61
|
+
kw.each{|k, v|
|
62
|
+
if CONFIG_MAP[k]
|
63
|
+
conf[k] = parse_config_value(k, v) # TODO: ractor support
|
64
|
+
else
|
65
|
+
raise "Unknown configuration: #{k}"
|
66
|
+
end
|
67
|
+
}
|
52
68
|
|
53
|
-
|
54
|
-
|
55
|
-
ENV[evname] = config[key].to_s if config[key]
|
56
|
-
}
|
57
|
-
end
|
69
|
+
update conf
|
70
|
+
end
|
58
71
|
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
if val = ENV[evname]
|
66
|
-
if /_USE_/ =~ evname || /NONSTOP/ =~ evname
|
67
|
-
case val
|
68
|
-
when '1', 'true', 'TRUE', 'T'
|
69
|
-
config[key] = true
|
70
|
-
else
|
71
|
-
config[key] = false
|
72
|
-
end
|
72
|
+
def append_config key, val
|
73
|
+
conf = self.config.dup
|
74
|
+
|
75
|
+
if CONFIG_SET[key]
|
76
|
+
if CONFIG_SET[key][2] == :path
|
77
|
+
conf[key] = [*conf[key], *parse_config_value(key, val)];
|
73
78
|
else
|
74
|
-
|
79
|
+
raise "not an Array type: #{key}"
|
75
80
|
end
|
81
|
+
else
|
82
|
+
raise "Unknown configuration: #{key}"
|
76
83
|
end
|
77
|
-
}
|
78
|
-
return config if !argv || argv.empty?
|
79
84
|
|
80
|
-
|
81
|
-
|
85
|
+
update conf
|
86
|
+
end
|
82
87
|
|
83
|
-
|
84
|
-
|
85
|
-
o.separator ''
|
86
|
-
o.version = ::DEBUGGER__::VERSION
|
88
|
+
def update conf
|
89
|
+
old_conf = self.class.instance_variable_get(:@config) || {}
|
87
90
|
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
+
# TODO: Use Ractor.make_shareable(conf)
|
92
|
+
self.class.instance_variable_set(:@config, conf.freeze)
|
93
|
+
|
94
|
+
# Post process
|
95
|
+
if_updated old_conf, conf, :keep_alloc_site do |_, new|
|
96
|
+
if new
|
97
|
+
require 'objspace'
|
98
|
+
ObjectSpace.trace_object_allocations_start
|
99
|
+
else
|
100
|
+
ObjectSpace.trace_object_allocations_stop
|
101
|
+
end
|
91
102
|
end
|
92
103
|
|
93
|
-
|
94
|
-
|
95
|
-
config[:commands] << cmd + ';;'
|
104
|
+
if_updated old_conf, conf, :postmortem do |_, new_p|
|
105
|
+
SESSION.postmortem = new_p
|
96
106
|
end
|
97
107
|
|
98
|
-
|
99
|
-
|
108
|
+
if_updated old_conf, conf, :sigdump_sig do |old_sig, new_sig|
|
109
|
+
setup_sigdump old_sig, new_sig
|
100
110
|
end
|
111
|
+
end
|
101
112
|
|
102
|
-
|
113
|
+
private def if_updated old_conf, new_conf, key
|
114
|
+
old, new = old_conf[key], new_conf[key]
|
115
|
+
yield old, new if old != new
|
116
|
+
end
|
103
117
|
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
end
|
118
|
-
o.on('--cookie=COOKIE', 'Set a cookie for connection') do |c|
|
119
|
-
config[:cookie] = c
|
118
|
+
private def enable_sigdump sig
|
119
|
+
@sigdump_sig_prev = trap(sig) do
|
120
|
+
str = []
|
121
|
+
str << "Simple sigdump on #{Process.pid}"
|
122
|
+
Thread.list.each{|th|
|
123
|
+
str << "Thread: #{th}"
|
124
|
+
th.backtrace.each{|loc|
|
125
|
+
str << " #{loc}"
|
126
|
+
}
|
127
|
+
str << ''
|
128
|
+
}
|
129
|
+
|
130
|
+
STDERR.puts str
|
120
131
|
end
|
132
|
+
end
|
121
133
|
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
134
|
+
private def disable_sigdump old_sig
|
135
|
+
trap(old_sig, @sigdump_sig_prev)
|
136
|
+
@sigdump_sig_prev = nil
|
137
|
+
end
|
138
|
+
|
139
|
+
# emergency simple sigdump.
|
140
|
+
# Use `sigdump` gem for more rich features.
|
141
|
+
private def setup_sigdump old_sig = nil, sig = CONFIG[:sigdump_sig]
|
142
|
+
if !old_sig && sig
|
143
|
+
enable_sigdump sig
|
144
|
+
elsif old_sig && !sig
|
145
|
+
disable_sigdump old_sig
|
146
|
+
elsif old_sig && sig
|
147
|
+
disable_sigdump old_sig
|
148
|
+
enable_sigdump sig
|
137
149
|
end
|
150
|
+
end
|
151
|
+
|
152
|
+
private def config
|
153
|
+
self.class.config
|
154
|
+
end
|
155
|
+
|
156
|
+
private def parse_config_value name, valstr
|
157
|
+
self.class.parse_config_value name, valstr
|
158
|
+
end
|
159
|
+
|
160
|
+
def self.parse_config_value name, valstr
|
161
|
+
return valstr unless valstr.kind_of? String
|
138
162
|
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
163
|
+
case CONFIG_SET[name][2]
|
164
|
+
when :bool
|
165
|
+
case valstr
|
166
|
+
when '1', 'true', 'TRUE', 'T'
|
167
|
+
true
|
168
|
+
else
|
169
|
+
false
|
170
|
+
end
|
171
|
+
when :int
|
172
|
+
valstr.to_i
|
173
|
+
when :loglevel
|
174
|
+
if DEBUGGER__::LOG_LEVELS[s = valstr.to_sym]
|
175
|
+
s
|
176
|
+
else
|
177
|
+
raise "Unknown loglevel: #{valstr}"
|
178
|
+
end
|
179
|
+
when :path # array of String
|
180
|
+
valstr.split(/:/).map{|e|
|
181
|
+
if /\A\/(.+)\/\z/ =~ e
|
182
|
+
Regexp.compile $1
|
183
|
+
else
|
184
|
+
e
|
185
|
+
end
|
186
|
+
}
|
187
|
+
else
|
188
|
+
valstr
|
155
189
|
end
|
190
|
+
end
|
191
|
+
|
192
|
+
def self.parse_argv argv
|
193
|
+
config = {
|
194
|
+
mode: :start,
|
195
|
+
}
|
196
|
+
CONFIG_MAP.each{|key, evname|
|
197
|
+
if val = ENV[evname]
|
198
|
+
config[key] = parse_config_value(key, val)
|
199
|
+
end
|
200
|
+
}
|
201
|
+
return config if !argv || argv.empty?
|
156
202
|
|
157
|
-
|
158
|
-
|
203
|
+
if argv.kind_of? String
|
204
|
+
require 'shellwords'
|
205
|
+
argv = Shellwords.split(argv)
|
159
206
|
end
|
160
207
|
|
161
|
-
|
162
|
-
|
163
|
-
|
208
|
+
require 'optparse'
|
209
|
+
require_relative 'version'
|
210
|
+
|
211
|
+
opt = OptionParser.new do |o|
|
212
|
+
o.banner = "#{$0} [options] -- [debuggee options]"
|
213
|
+
o.separator ''
|
214
|
+
o.version = ::DEBUGGER__::VERSION
|
215
|
+
|
216
|
+
o.separator 'Debug console mode:'
|
217
|
+
o.on('-n', '--nonstop', 'Do not stop at the beginning of the script.') do
|
218
|
+
config[:nonstop] = '1'
|
219
|
+
end
|
220
|
+
|
221
|
+
o.on('-e DEBUG_COMMAND', 'Execute debug command at the beginning of the script.') do |cmd|
|
222
|
+
config[:commands] ||= ''
|
223
|
+
config[:commands] += cmd + ';;'
|
224
|
+
end
|
225
|
+
|
226
|
+
o.on('-x FILE', '--init-script=FILE', 'Execute debug command in the FILE.') do |file|
|
227
|
+
config[:init_script] = file
|
228
|
+
end
|
229
|
+
o.on('--no-rc', 'Ignore ~/.rdbgrc') do
|
230
|
+
config[:no_rc] = true
|
231
|
+
end
|
232
|
+
o.on('--no-color', 'Disable colorize') do
|
233
|
+
config[:no_color] = true
|
234
|
+
end
|
235
|
+
o.on('--no-sigint-hook', 'Disable to trap SIGINT') do
|
236
|
+
config[:no_sigint_hook] = true
|
237
|
+
end
|
238
|
+
|
239
|
+
o.on('-c', '--command', 'Enable command mode.',
|
240
|
+
'The first argument should be a command name in $PATH.',
|
241
|
+
'Example: \'rdbg -c bundle exec rake test\'') do
|
242
|
+
config[:command] = true
|
243
|
+
end
|
244
|
+
|
245
|
+
o.separator ''
|
246
|
+
|
247
|
+
o.on('-O', '--open', 'Start remote debugging with opening the network port.',
|
248
|
+
'If TCP/IP options are not given,',
|
249
|
+
'a UNIX domain socket will be used.') do
|
250
|
+
config[:remote] = true
|
251
|
+
end
|
252
|
+
o.on('--sock-path=SOCK_PATH', 'UNIX Domain socket path') do |path|
|
253
|
+
config[:sock_path] = path
|
254
|
+
end
|
255
|
+
o.on('--port=PORT', 'Listening TCP/IP port') do |port|
|
256
|
+
config[:port] = port
|
257
|
+
end
|
258
|
+
o.on('--host=HOST', 'Listening TCP/IP host') do |host|
|
259
|
+
config[:host] = host
|
260
|
+
end
|
261
|
+
o.on('--cookie=COOKIE', 'Set a cookie for connection') do |c|
|
262
|
+
config[:cookie] = c
|
263
|
+
end
|
264
|
+
|
265
|
+
rdbg = 'rdbg'
|
266
|
+
|
267
|
+
o.separator ''
|
268
|
+
o.separator ' Debug console mode runs Ruby program with the debug console.'
|
269
|
+
o.separator ''
|
270
|
+
o.separator " '#{rdbg} target.rb foo bar' starts like 'ruby target.rb foo bar'."
|
271
|
+
o.separator " '#{rdbg} -- -r foo -e bar' starts like 'ruby -r foo -e bar'."
|
272
|
+
o.separator " '#{rdbg} -c rake test' starts like 'rake test'."
|
273
|
+
o.separator " '#{rdbg} -c -- rake test -t' starts like 'rake test -t'."
|
274
|
+
o.separator " '#{rdbg} -c bundle exec rake test' starts like 'bundle exec rake test'."
|
275
|
+
o.separator " '#{rdbg} -O target.rb foo bar' starts and accepts attaching with UNIX domain socket."
|
276
|
+
o.separator " '#{rdbg} -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234."
|
277
|
+
o.separator " '#{rdbg} -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234."
|
278
|
+
|
279
|
+
o.separator ''
|
280
|
+
o.separator 'Attach mode:'
|
281
|
+
o.on('-A', '--attach', 'Attach to debuggee process.') do
|
282
|
+
config[:mode] = :attach
|
283
|
+
end
|
284
|
+
|
285
|
+
o.separator ''
|
286
|
+
o.separator ' Attach mode attaches the remote debug console to the debuggee process.'
|
287
|
+
o.separator ''
|
288
|
+
o.separator " '#{rdbg} -A' tries to connect via UNIX domain socket."
|
289
|
+
o.separator " #{' ' * rdbg.size} If there are multiple processes are waiting for the"
|
290
|
+
o.separator " #{' ' * rdbg.size} debugger connection, list possible debuggee names."
|
291
|
+
o.separator " '#{rdbg} -A path' tries to connect via UNIX domain socket with given path name."
|
292
|
+
o.separator " '#{rdbg} -A port' tries to connect to localhost:port via TCP/IP."
|
293
|
+
o.separator " '#{rdbg} -A host port' tries to connect to host:port via TCP/IP."
|
294
|
+
|
295
|
+
o.separator ''
|
296
|
+
o.separator 'Other options:'
|
297
|
+
|
298
|
+
o.on("-h", "--help", "Print help") do
|
299
|
+
puts o
|
300
|
+
exit
|
301
|
+
end
|
302
|
+
|
303
|
+
o.on('--util=NAME', 'Utility mode (used by tools)') do |name|
|
304
|
+
require_relative 'client'
|
305
|
+
Client.new(name)
|
306
|
+
exit
|
307
|
+
end
|
308
|
+
|
309
|
+
o.separator ''
|
310
|
+
o.separator 'NOTE'
|
311
|
+
o.separator ' All messages communicated between a debugger and a debuggee are *NOT* encrypted.'
|
312
|
+
o.separator ' Please use the remote debugging feature carefully.'
|
164
313
|
end
|
165
314
|
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
315
|
+
opt.parse!(argv)
|
316
|
+
|
317
|
+
config
|
318
|
+
end
|
319
|
+
|
320
|
+
def self.config_to_env_hash config
|
321
|
+
CONFIG_MAP.each_with_object({}){|(key, evname), env|
|
322
|
+
unless config[key].nil?
|
323
|
+
case CONFIG_SET[key][2]
|
324
|
+
when :path
|
325
|
+
valstr = config[key].map{|e| e.kind_of?(Regexp) ? e.inspect : e}.join(':')
|
326
|
+
else
|
327
|
+
valstr = config[key].to_s
|
328
|
+
end
|
329
|
+
env[evname] = valstr
|
330
|
+
end
|
331
|
+
}
|
332
|
+
end
|
333
|
+
end
|
334
|
+
|
335
|
+
CONFIG = Config.new ENV['RUBY_DEBUG_OPT']
|
336
|
+
|
337
|
+
## Unix domain socket configuration
|
338
|
+
|
339
|
+
def self.unix_domain_socket_dir
|
340
|
+
case
|
341
|
+
when path = CONFIG[:sock_dir]
|
342
|
+
when path = ENV['XDG_RUNTIME_DIR']
|
343
|
+
when home = ENV['HOME']
|
344
|
+
path = File.join(home, '.ruby-debug-sock')
|
345
|
+
|
346
|
+
case
|
347
|
+
when !File.exist?(path)
|
348
|
+
Dir.mkdir(path, 0700)
|
349
|
+
when !File.directory?(path)
|
350
|
+
raise "#{path} is not a directory."
|
351
|
+
end
|
352
|
+
else
|
353
|
+
raise 'specify RUBY_DEBUG_SOCK_DIR environment variable for UNIX domain socket directory.'
|
170
354
|
end
|
171
355
|
|
172
|
-
|
356
|
+
path
|
357
|
+
end
|
358
|
+
|
359
|
+
def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir)
|
360
|
+
user = ENV['USER'] || 'ruby-debug'
|
361
|
+
File.join(base_dir, "ruby-debug-#{user}")
|
362
|
+
end
|
173
363
|
|
174
|
-
|
364
|
+
def self.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir)
|
365
|
+
create_unix_domain_socket_name_prefix(base_dir) + "-#{Process.pid}"
|
175
366
|
end
|
176
367
|
|
177
|
-
|
368
|
+
## Help
|
178
369
|
|
179
|
-
def self.
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
370
|
+
def self.parse_help
|
371
|
+
helps = Hash.new{|h, k| h[k] = []}
|
372
|
+
desc = cat = nil
|
373
|
+
cmds = Hash.new
|
374
|
+
|
375
|
+
File.read(File.join(__dir__, 'session.rb')).each_line do |line|
|
376
|
+
case line
|
377
|
+
when /\A\s*### (.+)/
|
378
|
+
cat = $1
|
379
|
+
break if $1 == 'END'
|
380
|
+
when /\A when (.+)/
|
381
|
+
next unless cat
|
382
|
+
next unless desc
|
383
|
+
ws = $1.split(/,\s*/).map{|e| e.gsub('\'', '')}
|
384
|
+
helps[cat] << [ws, desc]
|
385
|
+
desc = nil
|
386
|
+
max_w = ws.max_by{|w| w.length}
|
387
|
+
ws.each{|w|
|
388
|
+
cmds[w] = max_w
|
389
|
+
}
|
390
|
+
when /\A\s+# (\s*\*.+)/
|
391
|
+
if desc
|
392
|
+
desc << "\n" + $1
|
393
|
+
else
|
394
|
+
desc = $1
|
395
|
+
end
|
185
396
|
end
|
397
|
+
end
|
398
|
+
@commands = cmds
|
399
|
+
@helps = helps
|
400
|
+
end
|
401
|
+
|
402
|
+
def self.helps
|
403
|
+
(defined?(@helps) && @helps) || parse_help
|
404
|
+
end
|
405
|
+
|
406
|
+
def self.commands
|
407
|
+
(defined?(@commands) && @commands) || (parse_help; @commands)
|
408
|
+
end
|
409
|
+
|
410
|
+
def self.help
|
411
|
+
r = []
|
412
|
+
self.helps.each{|cat, cmds|
|
413
|
+
r << "### #{cat}"
|
414
|
+
r << ''
|
415
|
+
cmds.each{|ws, desc|
|
416
|
+
r << desc
|
417
|
+
}
|
418
|
+
r << ''
|
186
419
|
}
|
420
|
+
r.join("\n")
|
187
421
|
end
|
188
422
|
end
|