debug 1.0.0.beta2 → 1.0.0.beta7

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,5 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'socket'
4
+ require 'io/console/size'
5
+
2
6
  require_relative 'config'
7
+ require_relative 'version'
8
+
9
+ # $VERBOSE = true
3
10
 
4
11
  module DEBUGGER__
5
12
  class CommandLineOptionError < Exception; end
@@ -8,16 +15,18 @@ module DEBUGGER__
8
15
  begin
9
16
  require 'readline'
10
17
  def readline
11
- Readline.readline("\n(rdb) ", true)
18
+ Readline.readline("\n(rdbg:remote) ", true)
12
19
  end
13
20
  rescue LoadError
14
21
  def readline
15
- print "\n(rdb) "
22
+ print "\n(rdbg:remote) "
16
23
  gets
17
24
  end
18
25
  end
19
26
 
20
27
  def initialize argv
28
+ return util(argv) if String === argv
29
+
21
30
  case argv.size
22
31
  when 0
23
32
  connect_unix
@@ -36,6 +45,24 @@ module DEBUGGER__
36
45
  else
37
46
  raise CommandLineOptionError
38
47
  end
48
+
49
+ @width = IO.console_size[1]
50
+ @width = 80 if @width == 0
51
+ @width_changed = false
52
+
53
+ send "version: #{VERSION} width: #{@width} cookie: #{CONFIG[:cookie]}"
54
+ end
55
+
56
+ def util name
57
+ case name
58
+ when 'gen-sockpath'
59
+ puts DEBUGGER__.create_unix_domain_socket_name
60
+ when 'list-socks'
61
+ cleanup_unix_domain_sockets
62
+ puts list_connections
63
+ else
64
+ raise "Unknown utility: #{name}"
65
+ end
39
66
  end
40
67
 
41
68
  def cleanup_unix_domain_sockets
@@ -50,16 +77,20 @@ module DEBUGGER__
50
77
  end
51
78
  end
52
79
 
80
+ def list_connections
81
+ Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*')
82
+ end
83
+
53
84
  def connect_unix name = nil
54
85
  if name
55
86
  if File.exist? name
56
87
  @s = Socket.unix(name)
57
88
  else
58
- @s = Socket.unix(File.join(DEBUGGER__.unix_domain_socket_basedir, name))
89
+ @s = Socket.unix(File.join(DEBUGGER__.unix_domain_socket_dir, name))
59
90
  end
60
91
  else
61
92
  cleanup_unix_domain_sockets
62
- files = Dir.glob(DEBUGGER__.create_unix_domain_socket_name_prefix + '*')
93
+ files = list_connections
63
94
  case files.size
64
95
  when 0
65
96
  $stderr.puts "No debug session is available."
@@ -80,13 +111,22 @@ module DEBUGGER__
80
111
  @s = Socket.tcp(host, port)
81
112
  end
82
113
 
114
+ def send msg
115
+ p send: msg if $VERBOSE
116
+ @s.puts msg
117
+ end
118
+
83
119
  def connect
84
120
  trap(:SIGINT){
85
- @s.puts "pause"
121
+ send "pause"
122
+ }
123
+ trap(:SIGWINCH){
124
+ @width = IO.console_size[1]
125
+ @width_changed = true
86
126
  }
87
127
 
88
128
  while line = @s.gets
89
- # p line: line
129
+ p recv: line if $VERBOSE
90
130
  case line
91
131
  when /^out (.*)/
92
132
  puts "#{$1}"
@@ -102,10 +142,16 @@ module DEBUGGER__
102
142
  end
103
143
 
104
144
  line = (line || 'quit').strip
105
- @s.puts "command #{line}"
145
+
146
+ if @width_changed
147
+ @width_changed = false
148
+ send "width #{@width}"
149
+ end
150
+
151
+ send "command #{line}"
106
152
  when /^ask (.*)/
107
153
  print $1
108
- @s.puts "answer #{gets || ''}"
154
+ send "answer #{gets || ''}"
109
155
  when /^quit/
110
156
  raise 'quit'
111
157
  else
@@ -119,10 +165,6 @@ module DEBUGGER__
119
165
  end
120
166
  end
121
167
 
122
- def connect argv = ARGV
123
- DEBUGGER__::Client.new(argv).connect
124
- end
125
-
126
168
  if __FILE__ == $0
127
- connect
169
+ DEBUGGER__::Client.new(argv).connect
128
170
  end
@@ -0,0 +1,76 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require 'irb/color'
5
+ require "irb/color_printer"
6
+ rescue LoadError
7
+ warn "DEBUGGER: can not load newer irb for coloring. Write 'gem \"debug\" in your Gemfile."
8
+ end
9
+
10
+ module DEBUGGER__
11
+ module Color
12
+ if defined? IRB::Color.colorize
13
+ def colorize str, color
14
+ if !CONFIG[:no_color]
15
+ IRB::Color.colorize str, color
16
+ else
17
+ str
18
+ end
19
+ end
20
+ else
21
+ def colorize str, color
22
+ str
23
+ end
24
+ end
25
+
26
+ if defined? IRB::ColorPrinter.pp
27
+ def color_pp obj, width = SESSION.width
28
+ if !CONFIG[:no_color]
29
+ IRB::ColorPrinter.pp(obj, "".dup, width)
30
+ else
31
+ obj.pretty_inspect
32
+ end
33
+ end
34
+ else
35
+ def color_pp obj
36
+ obj.pretty_inspect
37
+ end
38
+ end
39
+
40
+ def colored_inspect obj, no_color: false
41
+ if !no_color
42
+ color_pp obj
43
+ else
44
+ obj.pretty_inspect
45
+ end
46
+ rescue => ex
47
+ err_msg = "#{ex.inspect} rescued during inspection"
48
+ string_result = obj.to_s rescue nil
49
+
50
+ # don't colorize the string here because it's not from user's application
51
+ if string_result
52
+ %Q{"#{string_result}" from #to_s because #{err_msg}}
53
+ else
54
+ err_msg
55
+ end
56
+ end
57
+
58
+ if defined? IRB::Color.colorize_code
59
+ def colorize_code code
60
+ IRB::Color.colorize_code(code)
61
+ end
62
+ else
63
+ def colorize_code code
64
+ code
65
+ end
66
+ end
67
+
68
+ def colorize_cyan(str)
69
+ colorize(str, [:CYAN, :BOLD])
70
+ end
71
+
72
+ def colorize_blue(str)
73
+ colorize(str, [:BLUE, :BOLD])
74
+ end
75
+ end
76
+ end
data/lib/debug/config.rb CHANGED
@@ -1,8 +1,9 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module DEBUGGER__
3
4
  def self.unix_domain_socket_dir
4
5
  case
5
- when path = DEBUGGER__::CONFIG[:sock_dir]
6
+ when path = ::DEBUGGER__::CONFIG[:sock_dir]
6
7
  when path = ENV['XDG_RUNTIME_DIR']
7
8
  when home = ENV['HOME']
8
9
  path = File.join(home, '.ruby-debug-sock')
@@ -29,71 +30,151 @@ module DEBUGGER__
29
30
  create_unix_domain_socket_name_prefix(base_dir) + "-#{Process.pid}"
30
31
  end
31
32
 
32
- CONFIG_MAP = {
33
- # execution preferences
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 ';;'
37
- show_src_lines: 'RUBY_DEBUG_SHOW_SRC_LINES', # Show n lines source code on breakpoint (default: 10 lines).
38
- show_frames: 'RUBY_DEBUG_SHOW_FRAMES', # Show n frames on breakpoint (default: 2 frames).
39
-
40
- # remote
41
- port: 'RUBY_DEBUG_PORT', # TCP/IP remote debugging: port
42
- host: 'RUBY_DEBUG_HOST', # TCP/IP remote debugging: host (localhost if not given)
43
- sock_dir: 'RUBY_DEBUG_SOCK_DIR', # UNIX Domain Socket remote debugging: socket directory
33
+ CONFIG_SET = {
34
+ # UI setting
35
+ log_level: ['RUBY_DEBUG_LOG_LEVEL', "UI: Log level same as Logger (default: WARN)", :loglevel],
36
+ show_src_lines: ['RUBY_DEBUG_SHOW_SRC_LINES', "UI: Show n lines source code on breakpoint (default: 10 lines)", :int],
37
+ show_frames: ['RUBY_DEBUG_SHOW_FRAMES', "UI: Show n frames on breakpoint (default: 2 frames)", :int],
38
+ show_info_lines:['RUBY_DEBUG_SHOW_INFO_LINES',"UI: Show n lines on info command (default: 10 lines, 0 for unlimited)", :int],
39
+ use_short_path: ['RUBY_DEBUG_USE_SHORT_PATH', "UI: Show shoten PATH (like $(Gem)/foo.rb)", :bool],
40
+ skip_nosrc: ['RUBY_DEBUG_SKIP_NOSRC', "UI: Skip on no source code lines (default: false)", :bool],
41
+ skip_path: ['RUBY_DEBUG_SKIP_PATH', "UI: Skip showing frames for given paths (default: [])", :path],
42
+ no_color: ['RUBY_DEBUG_NO_COLOR', "UI: Do not use colorize (default: false)", :bool],
43
+ no_sigint_hook: ['RUBY_DEBUG_NO_SIGINT_HOOK', "UI: Do not suspend on SIGINT (default: false)", :bool],
44
+
45
+ # boot setting
46
+ nonstop: ['RUBY_DEBUG_NONSTOP', "BOOT: Nonstop mode", :bool],
47
+ init_script: ['RUBY_DEBUG_INIT_SCRIPT', "BOOT: debug command script path loaded at first stop"],
48
+ commands: ['RUBY_DEBUG_COMMANDS', "BOOT: debug commands invoked at first stop. commands should be separated by ';;'"],
49
+ no_rc: ['RUBY_DEBUG_NO_RC', "BOOT: ignore loading ~/.rdbgrc(.rb)", :bool],
50
+ history: ['RUBY_DEBUG_HISTORY', "BOOT: save and load history file (default: ~/.rdbg_history)"],
51
+
52
+ # remote setting
53
+ port: ['RUBY_DEBUG_PORT', "REMOTE: TCP/IP remote debugging: port"],
54
+ host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host (localhost if not given)"],
55
+ sock_path: ['RUBY_DEBUG_SOCK_PATH', "REMOTE: UNIX Domain Socket remote debugging: socket path"],
56
+ sock_dir: ['RUBY_DEBUG_SOCK_DIR', "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
57
+ cookie: ['RUBY_DEBUG_COOKIE', "REMOTE: Cookie for negotiation"],
44
58
  }.freeze
45
59
 
46
- def self.config_to_env config
47
- CONFIG_MAP.each{|key, evname|
48
- ENV[evname] = config[key]
60
+ CONFIG_MAP = CONFIG_SET.map{|k, (ev, desc)| [k, ev]}.to_h.freeze
61
+
62
+ def self.config_to_env_hash config
63
+ CONFIG_MAP.each_with_object({}){|(key, evname), env|
64
+ env[evname] = config[key].to_s if config[key]
49
65
  }
50
66
  end
51
67
 
68
+ def self.parse_config_value name, valstr
69
+ return valstr unless valstr.kind_of? String
70
+
71
+ case CONFIG_SET[name][2]
72
+ when :bool
73
+ case valstr
74
+ when '1', 'true', 'TRUE', 'T'
75
+ true
76
+ else
77
+ false
78
+ end
79
+ when :int
80
+ valstr.to_i
81
+ when :loglevel
82
+ if DEBUGGER__::LOG_LEVELS[s = valstr.to_sym]
83
+ s
84
+ else
85
+ raise "Unknown loglevel: #{valstr}"
86
+ end
87
+ when :path # array of String
88
+ valstr.split(/:/).map{|e|
89
+ if /\A\/(.+)\/\z/ =~ e
90
+ Regexp.compile $1
91
+ else
92
+ e
93
+ end
94
+ }
95
+ else
96
+ valstr
97
+ end
98
+ end
99
+
52
100
  def self.parse_argv argv
53
101
  config = {
54
102
  mode: :start,
55
103
  }
56
104
  CONFIG_MAP.each{|key, evname|
57
- config[key] = ENV[evname] if ENV[evname]
105
+ if val = ENV[evname]
106
+ config[key] = parse_config_value(key, val)
107
+ end
58
108
  }
59
109
  return config if !argv || argv.empty?
60
110
 
61
111
  require 'optparse'
112
+ require_relative 'version'
62
113
 
63
114
  opt = OptionParser.new do |o|
64
115
  o.banner = "#{$0} [options] -- [debuggee options]"
65
116
  o.separator ''
117
+ o.version = ::DEBUGGER__::VERSION
66
118
 
67
119
  o.separator 'Debug console mode:'
68
120
  o.on('-n', '--nonstop', 'Do not stop at the beginning of the script.') do
69
121
  config[:nonstop] = '1'
70
122
  end
71
123
 
72
- o.on('-e [COMMAND]', 'execute debug command at the beginning of the script.') do |cmd|
124
+ o.on('-e DEBUG_COMMAND', 'Execute debug command at the beginning of the script.') do |cmd|
73
125
  config[:commands] ||= ''
74
- config[:commands] << cmd + ';;'
126
+ config[:commands] += cmd + ';;'
75
127
  end
76
128
 
77
- o.on('-O', '--open', 'Start debuggee with opening the debugger port.',
129
+ o.on('-x FILE', '--init-script=FILE', 'Execute debug command in the FILE.') do |file|
130
+ config[:init_script] = file
131
+ end
132
+ o.on('--no-rc', 'Ignore ~/.rdbgrc') do
133
+ config[:no_rc] = true
134
+ end
135
+ o.on('--no-color', 'Disable colorize') do
136
+ config[:no_color] = true
137
+ end
138
+
139
+ o.on('-c', '--command', 'Enable command mode.',
140
+ 'The first argument should be a command name in $PATH.',
141
+ 'Example: \'rdbg -c bundle exec rake test\'') do
142
+ config[:command] = true
143
+ end
144
+
145
+ o.separator ''
146
+
147
+ o.on('-O', '--open', 'Start remote debugging with opening the network port.',
78
148
  'If TCP/IP options are not given,',
79
149
  'a UNIX domain socket will be used.') do
80
150
  config[:remote] = true
81
151
  end
82
- o.on('--port=[PORT]', 'Listening TCP/IP port') do |port|
152
+ o.on('--sock-path=SOCK_PATH', 'UNIX Doman socket path') do |path|
153
+ config[:sock_path] = path
154
+ end
155
+ o.on('--port=PORT', 'Listening TCP/IP port') do |port|
83
156
  config[:port] = port
84
157
  end
85
- o.on('--host=[HOST]', 'Listening TCP/IP host') do |host|
158
+ o.on('--host=HOST', 'Listening TCP/IP host') do |host|
86
159
  config[:host] = host
87
160
  end
161
+ o.on('--cookie=COOKIE', 'Set a cookie for connection') do |c|
162
+ config[:cookie] = c
163
+ end
164
+
165
+ rdbg = 'rdbg'
88
166
 
89
167
  o.separator ''
90
168
  o.separator ' Debug console mode runs Ruby program with the debug console.'
91
169
  o.separator ''
92
- o.separator " #{$0} target.rb foo bar starts like 'ruby target.rb foo bar'."
93
- o.separator " #{$0} -- -r foo -e bar starts like 'ruby -r foo -e bar'."
94
- o.separator " #{$0} -O target.rb foo bar starts and accepts attaching with UNIX domain socket."
95
- o.separator " #{$0} -O --port 1234 target.rb foo bar starts accepts attaching with TCP/IP localhost:1234."
96
- o.separator " #{$0} -O --port 1234 -- -r foo -e bar starts accepts attaching with TCP/IP localhost:1234."
170
+ o.separator " '#{rdbg} target.rb foo bar' starts like 'ruby target.rb foo bar'."
171
+ o.separator " '#{rdbg} -- -r foo -e bar' starts like 'ruby -r foo -e bar'."
172
+ o.separator " '#{rdbg} -c rake test' starts like 'rake test'."
173
+ o.separator " '#{rdbg} -c -- rake test -t' starts like 'rake test -t'."
174
+ o.separator " '#{rdbg} -c bundle exec rake test' starts like 'bundle exec rake test'."
175
+ o.separator " '#{rdbg} -O target.rb foo bar' starts and accepts attaching with UNIX domain socket."
176
+ o.separator " '#{rdbg} -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234."
177
+ o.separator " '#{rdbg} -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234."
97
178
 
98
179
  o.separator ''
99
180
  o.separator 'Attach mode:'
@@ -104,16 +185,59 @@ module DEBUGGER__
104
185
  o.separator ''
105
186
  o.separator ' Attach mode attaches the remote debug console to the debuggee process.'
106
187
  o.separator ''
107
- o.separator " '#{$0} -A' tries to connect via UNIX domain socket."
108
- o.separator " #{' ' * $0.size} If there are multiple processes are waiting for the"
109
- o.separator " #{' ' * $0.size} debugger connection, list possible debuggee names."
110
- o.separator " '#{$0} -A path' tries to connect via UNIX domain socket with given path name."
111
- o.separator " '#{$0} -A port' tries to connect localhost:port via TCP/IP."
112
- o.separator " '#{$0} -A host port' tries to connect host:port via TCP/IP."
188
+ o.separator " '#{rdbg} -A' tries to connect via UNIX domain socket."
189
+ o.separator " #{' ' * rdbg.size} If there are multiple processes are waiting for the"
190
+ o.separator " #{' ' * rdbg.size} debugger connection, list possible debuggee names."
191
+ o.separator " '#{rdbg} -A path' tries to connect via UNIX domain socket with given path name."
192
+ o.separator " '#{rdbg} -A port' tries to connect to localhost:port via TCP/IP."
193
+ o.separator " '#{rdbg} -A host port' tries to connect to host:port via TCP/IP."
194
+
195
+ o.separator ''
196
+ o.separator 'Other options:'
197
+
198
+ o.on("-h", "--help", "Print help") do
199
+ puts o
200
+ exit
201
+ end
202
+
203
+ o.on('--util=NAME', 'Utility mode (used by tools)') do |name|
204
+ require_relative 'client'
205
+ Client.new(name)
206
+ exit
207
+ end
208
+
209
+ o.separator ''
210
+ o.separator 'NOTE'
211
+ o.separator ' All messages communicated between a debugger and a debuggee are *NOT* encrypted.'
212
+ o.separator ' Please use the remote debugging feature carefully.'
113
213
  end
114
214
 
115
215
  opt.parse!(argv)
116
216
 
117
217
  config
118
218
  end
219
+
220
+ CONFIG = ::DEBUGGER__.parse_argv(ENV['RUBY_DEBUG_OPT'])
221
+
222
+ def self.set_config kw
223
+ kw.each{|k, v|
224
+ if CONFIG_MAP[k]
225
+ CONFIG[k] = parse_config_value(k, v) # TODO: ractor support
226
+ else
227
+ raise "Unknown configuration: #{k}"
228
+ end
229
+ }
230
+ end
231
+
232
+ def self.append_config key, val
233
+ if CONFIG_SET[key]
234
+ if CONFIG_SET[key][2] == :path
235
+ CONFIG[key] = [*CONFIG[key], *parse_config_value(key, val)];
236
+ else
237
+ raise "not an Array type: #{key}"
238
+ end
239
+ else
240
+ raise "Unknown configuration: #{key}"
241
+ end
242
+ end
119
243
  end