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.
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