debug 1.0.0.beta5 → 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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[:use_colorize]
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
- IRB::ColorPrinter.pp(obj, "")
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 CONFIG[:use_colorize]
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
- def self.unix_domain_socket_dir
4
- case
5
- when path = ::DEBUGGER__::CONFIG[:sock_dir]
6
- when path = ENV['XDG_RUNTIME_DIR']
7
- when home = ENV['HOME']
8
- path = File.join(home, '.ruby-debug-sock')
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
- case
11
- when !File.exist?(path)
12
- Dir.mkdir(path, 0700)
13
- when !File.directory?(path)
14
- raise "#{path} is not a directory."
15
- end
16
- else
17
- raise 'specify RUBY_DEBUG_SOCK_DIR environment variable for UNIX domain socket directory.'
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
- path
21
- end
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
- def self.create_unix_domain_socket_name_prefix(base_dir = unix_domain_socket_dir)
24
- user = ENV['USER'] || 'ruby-debug'
25
- File.join(base_dir, "ruby-debug-#{user}")
26
- end
48
+ update self.class.parse_argv(argv)
49
+ end
27
50
 
28
- def self.create_unix_domain_socket_name(base_dir = unix_domain_socket_dir)
29
- create_unix_domain_socket_name_prefix(base_dir) + "-#{Process.pid}"
30
- end
51
+ def [](key)
52
+ config[key]
53
+ end
31
54
 
32
- CONFIG_MAP = {
33
- # boot setting
34
- nonstop: 'RUBY_DEBUG_NONSTOP', # Nonstop mode ('1' is nonstop)
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
- # UI setting
39
- show_src_lines: 'RUBY_DEBUG_SHOW_SRC_LINES', # Show n lines source code on breakpoint (default: 10 lines).
40
- show_frames: 'RUBY_DEBUG_SHOW_FRAMES', # Show n frames on breakpoint (default: 2 frames).
41
- use_short_path: 'RUBY_DEBUG_USE_SHORT_PATH', # Show shoten PATH (like $(Gem)/foo.rb).
42
- skip_nosrc: 'RUBY_DEBUG_SKIP_NOSRC', # Skip on no source code lines (default: false).
43
- use_colorize: 'RUBY_DEBUG_USE_COLORIZE', # Show coloring
44
-
45
- # remote
46
- port: 'RUBY_DEBUG_PORT', # TCP/IP remote debugging: port
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
- def self.config_to_env config
54
- CONFIG_MAP.each{|key, evname|
55
- ENV[evname] = config[key].to_s if config[key]
56
- }
57
- end
69
+ update conf
70
+ end
58
71
 
59
- def self.parse_argv argv
60
- config = {
61
- mode: :start,
62
- use_colorize: true,
63
- }
64
- CONFIG_MAP.each{|key, evname|
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
- config[key] = val
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
- require 'optparse'
81
- require_relative 'version'
85
+ update conf
86
+ end
82
87
 
83
- opt = OptionParser.new do |o|
84
- o.banner = "#{$0} [options] -- [debuggee options]"
85
- o.separator ''
86
- o.version = ::DEBUGGER__::VERSION
88
+ def update conf
89
+ old_conf = self.class.instance_variable_get(:@config) || {}
87
90
 
88
- o.separator 'Debug console mode:'
89
- o.on('-n', '--nonstop', 'Do not stop at the beginning of the script.') do
90
- config[:nonstop] = '1'
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
- o.on('-e COMMAND', 'execute debug command at the beginning of the script.') do |cmd|
94
- config[:commands] ||= ''
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
- o.on('-x FILE', '--init-script=FILE', 'execute debug command in the FILE.') do |file|
99
- config[:init_script] = file
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
- o.separator ''
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
- o.on('-O', '--open', 'Start remote debugging with opening the network port.',
105
- 'If TCP/IP options are not given,',
106
- 'a UNIX domain socket will be used.') do
107
- config[:remote] = true
108
- end
109
- o.on('--sock-path=SOCK_PATH', 'UNIX Doman socket path') do |path|
110
- config[:sock_path] = path
111
- end
112
- o.on('--port=PORT', 'Listening TCP/IP port') do |port|
113
- config[:port] = port
114
- end
115
- o.on('--host=HOST', 'Listening TCP/IP host') do |host|
116
- config[:host] = host
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
- rdbg = 'rdbg'
123
-
124
- o.separator ''
125
- o.separator ' Debug console mode runs Ruby program with the debug console.'
126
- o.separator ''
127
- o.separator " '#{rdbg} target.rb foo bar' starts like 'ruby target.rb foo bar'."
128
- o.separator " '#{rdbg} -- -r foo -e bar' starts like 'ruby -r foo -e bar'."
129
- o.separator " '#{rdbg} -O target.rb foo bar' starts and accepts attaching with UNIX domain socket."
130
- o.separator " '#{rdbg} -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234."
131
- o.separator " '#{rdbg} -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234."
132
-
133
- o.separator ''
134
- o.separator 'Attach mode:'
135
- o.on('-A', '--attach', 'Attach to debuggee process.') do
136
- config[:mode] = :attach
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
- o.separator ''
140
- o.separator ' Attach mode attaches the remote debug console to the debuggee process.'
141
- o.separator ''
142
- o.separator " '#{rdbg} -A' tries to connect via UNIX domain socket."
143
- o.separator " #{' ' * rdbg.size} If there are multiple processes are waiting for the"
144
- o.separator " #{' ' * rdbg.size} debugger connection, list possible debuggee names."
145
- o.separator " '#{rdbg} -A path' tries to connect via UNIX domain socket with given path name."
146
- o.separator " '#{rdbg} -A port' tries to connect to localhost:port via TCP/IP."
147
- o.separator " '#{rdbg} -A host port' tries to connect to host:port via TCP/IP."
148
-
149
- o.separator ''
150
- o.separator 'Other options:'
151
-
152
- o.on("-h", "--help", "Print help") do
153
- puts o
154
- exit
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
- o.on('-c', '--command', 'Command mode (first argument is command name)') do
158
- config[:command] = true
203
+ if argv.kind_of? String
204
+ require 'shellwords'
205
+ argv = Shellwords.split(argv)
159
206
  end
160
207
 
161
- o.on('--util=NAME', 'Utility mode (used by tools)') do |name|
162
- Client.new(name)
163
- exit
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
- o.separator ''
167
- o.separator 'NOTE'
168
- o.separator ' All messages communicated between a debugger and a debuggee are *NOT* encrypted.'
169
- o.separator ' Please use the remote debugging feature carefully.'
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
- opt.parse!(argv)
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
- config
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
- CONFIG = ::DEBUGGER__.parse_argv(ENV['RUBY_DEBUG_OPT'])
368
+ ## Help
178
369
 
179
- def self.set_config kw
180
- kw.each{|k, v|
181
- if CONFIG_MAP[k]
182
- CONFIG[k] = v # TODO: ractor support
183
- else
184
- raise "unknown option: #{k}"
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