debug 1.0.0.beta4 → 1.0.0.beta8

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,3 +1,4 @@
1
+ # frozen_string_literal: true
1
2
 
2
3
  module DEBUGGER__
3
4
  def self.unix_domain_socket_dir
@@ -29,83 +30,139 @@ module DEBUGGER__
29
30
  create_unix_domain_socket_name_prefix(base_dir) + "-#{Process.pid}"
30
31
  end
31
32
 
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 ';;'
37
-
33
+ CONFIG_SET = {
38
34
  # 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
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
+
51
+ # remote setting
52
+ port: ['RUBY_DEBUG_PORT', "REMOTE: TCP/IP remote debugging: port"],
53
+ host: ['RUBY_DEBUG_HOST', "REMOTE: TCP/IP remote debugging: host (localhost if not given)"],
54
+ sock_path: ['RUBY_DEBUG_SOCK_PATH', "REMOTE: UNIX Domain Socket remote debugging: socket path"],
55
+ sock_dir: ['RUBY_DEBUG_SOCK_DIR', "REMOTE: UNIX Domain Socket remote debugging: socket directory"],
56
+ cookie: ['RUBY_DEBUG_COOKIE', "REMOTE: Cookie for negotiation"],
50
57
  }.freeze
51
58
 
52
- def self.config_to_env config
53
- CONFIG_MAP.each{|key, evname|
54
- ENV[evname] = config[key].to_s if config[key]
59
+ CONFIG_MAP = CONFIG_SET.map{|k, (ev, desc)| [k, ev]}.to_h.freeze
60
+
61
+ def self.config_to_env_hash config
62
+ CONFIG_MAP.each_with_object({}){|(key, evname), env|
63
+ env[evname] = config[key].to_s if config[key]
55
64
  }
56
65
  end
57
66
 
67
+ def self.parse_config_value name, valstr
68
+ return valstr unless valstr.kind_of? String
69
+
70
+ case CONFIG_SET[name][2]
71
+ when :bool
72
+ case valstr
73
+ when '1', 'true', 'TRUE', 'T'
74
+ true
75
+ else
76
+ false
77
+ end
78
+ when :int
79
+ valstr.to_i
80
+ when :loglevel
81
+ if DEBUGGER__::LOG_LEVELS[s = valstr.to_sym]
82
+ s
83
+ else
84
+ raise "Unknown loglevel: #{valstr}"
85
+ end
86
+ when :path # array of String
87
+ valstr.split(/:/).map{|e|
88
+ if /\A\/(.+)\/\z/ =~ e
89
+ Regexp.compile $1
90
+ else
91
+ e
92
+ end
93
+ }
94
+ else
95
+ valstr
96
+ end
97
+ end
98
+
58
99
  def self.parse_argv argv
59
100
  config = {
60
101
  mode: :start,
61
- use_colorize: true,
62
102
  }
63
103
  CONFIG_MAP.each{|key, evname|
64
104
  if val = ENV[evname]
65
- if /_USE_/ =~ evname || /NONSTOP/ =~ evname
66
- case val
67
- when '1', 'true', 'TRUE', 'T'
68
- config[key] = true
69
- else
70
- config[key] = false
71
- end
72
- else
73
- config[key] = val
74
- end
105
+ config[key] = parse_config_value(key, val)
75
106
  end
76
107
  }
77
108
  return config if !argv || argv.empty?
78
109
 
79
110
  require 'optparse'
111
+ require_relative 'version'
80
112
 
81
113
  opt = OptionParser.new do |o|
82
114
  o.banner = "#{$0} [options] -- [debuggee options]"
83
115
  o.separator ''
116
+ o.version = ::DEBUGGER__::VERSION
84
117
 
85
118
  o.separator 'Debug console mode:'
86
119
  o.on('-n', '--nonstop', 'Do not stop at the beginning of the script.') do
87
120
  config[:nonstop] = '1'
88
121
  end
89
122
 
90
- o.on('-e [COMMAND]', 'execute debug command at the beginning of the script.') do |cmd|
123
+ o.on('-e DEBUG_COMMAND', 'Execute debug command at the beginning of the script.') do |cmd|
91
124
  config[:commands] ||= ''
92
- config[:commands] << cmd + ';;'
125
+ config[:commands] += cmd + ';;'
93
126
  end
94
127
 
95
- o.on('-O', '--open', 'Start debuggee with opening the debugger port.',
128
+ o.on('-x FILE', '--init-script=FILE', 'Execute debug command in the FILE.') do |file|
129
+ config[:init_script] = file
130
+ end
131
+ o.on('--no-rc', 'Ignore ~/.rdbgrc') do
132
+ config[:no_rc] = true
133
+ end
134
+ o.on('--no-color', 'Disable colorize') do
135
+ config[:no_color] = true
136
+ end
137
+ o.on('--no-sigint-hook', 'Disable to trap SIGINT') do
138
+ config[:no_sigint_hook] = true
139
+ end
140
+
141
+ o.on('-c', '--command', 'Enable command mode.',
142
+ 'The first argument should be a command name in $PATH.',
143
+ 'Example: \'rdbg -c bundle exec rake test\'') do
144
+ config[:command] = true
145
+ end
146
+
147
+ o.separator ''
148
+
149
+ o.on('-O', '--open', 'Start remote debugging with opening the network port.',
96
150
  'If TCP/IP options are not given,',
97
151
  'a UNIX domain socket will be used.') do
98
152
  config[:remote] = true
99
153
  end
100
- o.on('--sock-path=[SOCK_PATH]', 'UNIX Doman socket path') do |path|
154
+ o.on('--sock-path=SOCK_PATH', 'UNIX Doman socket path') do |path|
101
155
  config[:sock_path] = path
102
156
  end
103
- o.on('--port=[PORT]', 'Listening TCP/IP port') do |port|
157
+ o.on('--port=PORT', 'Listening TCP/IP port') do |port|
104
158
  config[:port] = port
105
159
  end
106
- o.on('--host=[HOST]', 'Listening TCP/IP host') do |host|
160
+ o.on('--host=HOST', 'Listening TCP/IP host') do |host|
107
161
  config[:host] = host
108
162
  end
163
+ o.on('--cookie=COOKIE', 'Set a cookie for connection') do |c|
164
+ config[:cookie] = c
165
+ end
109
166
 
110
167
  rdbg = 'rdbg'
111
168
 
@@ -114,6 +171,9 @@ module DEBUGGER__
114
171
  o.separator ''
115
172
  o.separator " '#{rdbg} target.rb foo bar' starts like 'ruby target.rb foo bar'."
116
173
  o.separator " '#{rdbg} -- -r foo -e bar' starts like 'ruby -r foo -e bar'."
174
+ o.separator " '#{rdbg} -c rake test' starts like 'rake test'."
175
+ o.separator " '#{rdbg} -c -- rake test -t' starts like 'rake test -t'."
176
+ o.separator " '#{rdbg} -c bundle exec rake test' starts like 'bundle exec rake test'."
117
177
  o.separator " '#{rdbg} -O target.rb foo bar' starts and accepts attaching with UNIX domain socket."
118
178
  o.separator " '#{rdbg} -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234."
119
179
  o.separator " '#{rdbg} -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234."
@@ -133,6 +193,25 @@ module DEBUGGER__
133
193
  o.separator " '#{rdbg} -A path' tries to connect via UNIX domain socket with given path name."
134
194
  o.separator " '#{rdbg} -A port' tries to connect to localhost:port via TCP/IP."
135
195
  o.separator " '#{rdbg} -A host port' tries to connect to host:port via TCP/IP."
196
+
197
+ o.separator ''
198
+ o.separator 'Other options:'
199
+
200
+ o.on("-h", "--help", "Print help") do
201
+ puts o
202
+ exit
203
+ end
204
+
205
+ o.on('--util=NAME', 'Utility mode (used by tools)') do |name|
206
+ require_relative 'client'
207
+ Client.new(name)
208
+ exit
209
+ end
210
+
211
+ o.separator ''
212
+ o.separator 'NOTE'
213
+ o.separator ' All messages communicated between a debugger and a debuggee are *NOT* encrypted.'
214
+ o.separator ' Please use the remote debugging feature carefully.'
136
215
  end
137
216
 
138
217
  opt.parse!(argv)
@@ -145,10 +224,22 @@ module DEBUGGER__
145
224
  def self.set_config kw
146
225
  kw.each{|k, v|
147
226
  if CONFIG_MAP[k]
148
- CONFIG[k] = v # TODO: ractor support
227
+ CONFIG[k] = parse_config_value(k, v) # TODO: ractor support
149
228
  else
150
- raise "unknown option: #{k}"
229
+ raise "Unknown configuration: #{k}"
151
230
  end
152
231
  }
153
232
  end
233
+
234
+ def self.append_config key, val
235
+ if CONFIG_SET[key]
236
+ if CONFIG_SET[key][2] == :path
237
+ CONFIG[key] = [*CONFIG[key], *parse_config_value(key, val)];
238
+ else
239
+ raise "not an Array type: #{key}"
240
+ end
241
+ else
242
+ raise "Unknown configuration: #{key}"
243
+ end
244
+ end
154
245
  end