debug 1.0.0.beta3 → 1.0.0.beta4

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9702de02ad9d9aab11e0f0113a3596110222f92178e2918e6f4c5ad66fb5e3fb
4
- data.tar.gz: 6f8552da80d13f0c597223fa651a49f44810d7106feb73c8a93d73249c74a987
3
+ metadata.gz: ebaae12c10e51036f850fd1cbcde66d891a1e50b64bc5d96bfdab610cb42fd1d
4
+ data.tar.gz: e62eee365aa1409c650049ccb8ed237854aeed6f7a066dc2b84f96c9cffefd12
5
5
  SHA512:
6
- metadata.gz: 1a2386cd5fcde06d356cb35e1d5ff637c587c33a4fdf9e3866d11761276a31cf5586d383b8cd8f6074be24a681592db7d577b8fcf3270d63afff9256ddaaa78f
7
- data.tar.gz: 373039bc43b6071d77c4bb103453258a50668224ea581b4fd0b5ec0bf3df7c4b95702e2bc3723e35c55e238aaf6fa9297090fa7daf8fe4c6eee22124f1087daf
6
+ metadata.gz: 2aa2b044268220dc79d72850f6366474fff647fa6893360044ce64a8a97e4d759e414da6c39dac52a6b33593512675091ed4fb4a7399c1357b344d342f3c812f
7
+ data.tar.gz: 506d0afeb3e3926da6631e99234fd2ba4c96220d5def5a95d0e51afa54ae28beb19c30873c3c0cf9a95fa975023364f7088a0190d26452e307e715925e21a78c
data/.gitignore CHANGED
@@ -8,3 +8,4 @@
8
8
  /tmp/
9
9
  *.bundle
10
10
  /Gemfile.lock
11
+ /lib/debug/debug.so
data/CONTRIBUTING.md ADDED
@@ -0,0 +1,97 @@
1
+ ## Manually Test Your Changes
2
+
3
+ You can manually test your changes with a simple Ruby script + a line of command. The following example will help you check:
4
+
5
+ - Breakpoint insertion.
6
+ - Resume from the breakpoint.
7
+ - Backtrace display.
8
+ - Information (local variables, ivars..etc.) display.
9
+ - Debugger exit.
10
+
11
+
12
+ ### Script
13
+
14
+ ```ruby
15
+ # target.rb
16
+ class Foo
17
+ def first_call
18
+ second_call(20)
19
+ end
20
+
21
+ def second_call(num)
22
+ third_call_with_block do |ten|
23
+ forth_call(num, ten)
24
+ end
25
+ end
26
+
27
+ def third_call_with_block(&block)
28
+ @ivar1 = 10; @ivar2 = 20
29
+
30
+ yield(10)
31
+ end
32
+
33
+ def forth_call(num1, num2)
34
+ num1 + num2
35
+ end
36
+ end
37
+
38
+ Foo.new.first_call
39
+ ```
40
+
41
+ ### Command
42
+
43
+ ```
44
+ $ exe/rdbg -e 'b 20;; c ;; bt ;; info ;; q!' -e c target.rb
45
+ ```
46
+
47
+ ### Expect Result
48
+
49
+ ```
50
+ ❯ exe/rdbg -e 'b 20;; c ;; bt ;; info ;; q!' -e c target.rb
51
+ [1, 10] in target.rb
52
+ => 1| class Foo
53
+ 2| def first_call
54
+ 3| second_call(20)
55
+ 4| end
56
+ 5|
57
+ 6| def second_call(num)
58
+ 7| third_call_with_block do |ten|
59
+ 8| forth_call(num, ten)
60
+ 9| end
61
+ 10| end
62
+ =>#0 <main> at target.rb:1
63
+ (rdbg:init) b 20
64
+ #1 line bp /PATH_TO_PROJECT/debug/target.rb:20 (return)
65
+ (rdbg:init) c
66
+ [15, 23] in target.rb
67
+ 15| yield(10)
68
+ 16| end
69
+ 17|
70
+ 18| def forth_call(num1, num2)
71
+ 19| num1 + num2
72
+ => 20| end
73
+ 21| end
74
+ 22|
75
+ 23| Foo.new.first_call
76
+ =>#0 Foo#forth_call(num1=20, num2=10) at target.rb:20 #=> 30
77
+ #1 block{|ten=10|} in second_call at target.rb:8
78
+ # and 4 frames (use `bt' command for all frames)
79
+
80
+ Stop by #1 line bp /PATH_TO_PROJECT/debug/target.rb:20 (return)
81
+ (rdbg:init) bt
82
+ =>#0 Foo#forth_call(num1=20, num2=10) at target.rb:20 #=> 30
83
+ #1 block{|ten=10|} in second_call at target.rb:8
84
+ #2 Foo#third_call_with_block(block=#<Proc:0x00007f8bc32f0c28 target.rb:7>) at target.rb:15
85
+ #3 Foo#second_call(num=20) at target.rb:7
86
+ #4 first_call at target.rb:3
87
+ #5 <main> at target.rb:23
88
+ (rdbg:init) info
89
+ =>#0 Foo#forth_call(num1=20, num2=10) at target.rb:20 #=> 30
90
+ %self => #<Foo:0x00007f8bc32f0ed0>
91
+ %return => 30
92
+ num1 => 20
93
+ num2 => 10
94
+ @ivar1 => 10
95
+ @ivar2 => 20
96
+ (rdbg:init) q!
97
+ ```
data/Gemfile CHANGED
@@ -2,6 +2,6 @@ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
4
4
 
5
- gem "rake", "~> 12.0"
5
+ gem "rake"
6
6
  gem "rake-compiler"
7
7
  gem "minitest", "~> 5.0"
data/README.md CHANGED
@@ -62,13 +62,14 @@ $ ruby -r debug/run target.rb
62
62
 
63
63
  $ cat target.rb
64
64
  require 'debug/run' # start the debug console
65
- ...
65
+ ... rest of program ...
66
66
 
67
67
  # or
68
68
 
69
69
  $ cat target.rb
70
70
  require 'debug/session' # introduce the functionality
71
71
  DEBUGGER__.console # and start the debug console
72
+ ... rest of program ...
72
73
 
73
74
  $ ruby target.rb
74
75
  ```
@@ -373,11 +374,11 @@ The `<...>` notation means the argument.
373
374
  * Note that edited file will not be reloaded.
374
375
  * `edit <file>`
375
376
  * Open <file> on the editor.
376
- * `i[nfo]`
377
+ * `i[nfo]`, `i[nfo] l[ocal[s]]`
377
378
  * Show information about the current frame (local variables)
378
379
  * It includes `self` as `%self` and a return value as `%return`.
379
- * `i[nfo] <expr>`
380
- * Show information about the result of <expr>.
380
+ * `i[nfo] th[read[s]]
381
+ * Show all threads (same as `th[read]`).
381
382
  * `display`
382
383
  * Show display setting.
383
384
  * `display <expr>`
@@ -437,27 +438,28 @@ Debug console mode:
437
438
  -O, --open Start debuggee with opening the debugger port.
438
439
  If TCP/IP options are not given,
439
440
  a UNIX domain socket will be used.
441
+ --sock-path=[SOCK_PATH] UNIX Doman socket path
440
442
  --port=[PORT] Listening TCP/IP port
441
443
  --host=[HOST] Listening TCP/IP host
442
444
 
443
445
  Debug console mode runs Ruby program with the debug console.
444
446
 
445
- rdbg target.rb foo bar starts like 'ruby target.rb foo bar'.
446
- rdbg -- -r foo -e bar starts like 'ruby -r foo -e bar'.
447
- rdbg -O target.rb foo bar starts and accepts attaching with UNIX domain socket.
448
- rdbg -O --port 1234 target.rb foo bar starts accepts attaching with TCP/IP localhost:1234.
449
- rdbg -O --port 1234 -- -r foo -e bar starts accepts attaching with TCP/IP localhost:1234.
447
+ 'rdbg target.rb foo bar' starts like 'ruby target.rb foo bar'.
448
+ 'rdbg -- -r foo -e bar' starts like 'ruby -r foo -e bar'.
449
+ 'rdbg -O target.rb foo bar' starts and accepts attaching with UNIX domain socket.
450
+ 'rdbg -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234.
451
+ 'rdbg -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234.
450
452
 
451
453
  Attach mode:
452
454
  -A, --attach Attach to debuggee process.
453
455
 
454
456
  Attach mode attaches the remote debug console to the debuggee process.
455
457
 
456
- 'rdbg -A' tries to connect via UNIX domain socket.
457
- If there are multiple processes are waiting for the
458
- debugger connection, list possible debuggee names.
459
- 'rdbg -A path' tries to connect via UNIX domain socket with given path name.
460
- 'rdbg -A port' tries to connect to localhost:port via TCP/IP.
458
+ 'rdbg -A' tries to connect via UNIX domain socket.
459
+ If there are multiple processes are waiting for the
460
+ debugger connection, list possible debuggee names.
461
+ 'rdbg -A path' tries to connect via UNIX domain socket with given path name.
462
+ 'rdbg -A port' tries to connect to localhost:port via TCP/IP.
461
463
  'rdbg -A host port' tries to connect to host:port via TCP/IP.
462
464
 
463
465
  ```
@@ -465,3 +467,5 @@ Attach mode:
465
467
  # Contributing
466
468
 
467
469
  Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/debug.
470
+
471
+ Please also check the [contributing guideline](/CONTRIBUTING.md).
data/TODO.md ADDED
@@ -0,0 +1,33 @@
1
+ # TODO
2
+
3
+ ## Basic functionality
4
+
5
+ * VScode DAP support
6
+ * Support Ractors
7
+ * Signal (SIGINT) support
8
+ * Remote connection with openssl
9
+
10
+ ## UI
11
+
12
+ * Coloring
13
+ * Interactive breakpoint setting
14
+ * irb integration
15
+ * Web browser integrated UI
16
+
17
+ ## Debug command
18
+
19
+ * Breakpoints
20
+ * Lightweight pending method break points with Ruby 3.1 feature (TP:method_added)
21
+ * Non-stop breakpoint but runs some code.
22
+ * Watch points
23
+ * Lightweight watchpoints for instance variables with Ruby 3.1 features (TP:ivar_set)
24
+ * Faster `next`/`finish` command by specifying target code.
25
+ * `set`/`show` configurations
26
+ * In-memory line traces
27
+ * Timemachine debugging
28
+
29
+ ## Tests
30
+
31
+ * Test framework
32
+ * Tests for commands
33
+ * Tests for remote debugging
data/ext/debug/debug.c CHANGED
@@ -90,7 +90,7 @@ method_added_tracker(VALUE tpval, void *ptr)
90
90
  VALUE mid = rb_tracearg_callee_id(arg);
91
91
 
92
92
  if (RB_UNLIKELY(mid == ID2SYM(rb_intern("method_added")) ||
93
- mid == ID2SYM(rb_intern("singleton_method_method_added")))) {
93
+ mid == ID2SYM(rb_intern("singleton_method_added")))) {
94
94
  VALUE args[] = {
95
95
  tpval,
96
96
  };
data/lib/debug.rb CHANGED
@@ -1 +1 @@
1
- require_relative 'debug/run'
1
+ require_relative 'debug/session'
@@ -209,7 +209,7 @@ module DEBUGGER__
209
209
  @tp = TracePoint.new(:raise){|tp|
210
210
  exc = tp.raised_exception
211
211
  exc.class.ancestors.each{|cls|
212
- suspend if pat === cls.name
212
+ suspend if @pat === cls.name
213
213
  }
214
214
  }
215
215
  end
@@ -333,7 +333,11 @@ module DEBUGGER__
333
333
  end
334
334
  end
335
335
 
336
- def enable quiet: false
336
+ def enable
337
+ try_enable
338
+ end
339
+
340
+ def try_enable quiet: false
337
341
  eval_class_name
338
342
  search_method
339
343
 
data/lib/debug/config.rb CHANGED
@@ -2,7 +2,7 @@
2
2
  module DEBUGGER__
3
3
  def self.unix_domain_socket_dir
4
4
  case
5
- when path = DEBUGGER__::CONFIG[:sock_dir]
5
+ when path = ::DEBUGGER__::CONFIG[:sock_dir]
6
6
  when path = ENV['XDG_RUNTIME_DIR']
7
7
  when home = ENV['HOME']
8
8
  path = File.join(home, '.ruby-debug-sock')
@@ -40,25 +40,39 @@ module DEBUGGER__
40
40
  show_frames: 'RUBY_DEBUG_SHOW_FRAMES', # Show n frames on breakpoint (default: 2 frames).
41
41
  use_short_path: 'RUBY_DEBUG_USE_SHORT_PATH', # Show shoten PATH (like $(Gem)/foo.rb).
42
42
  skip_nosrc: 'RUBY_DEBUG_SKIP_NOSRC', # Skip on no source code lines (default: false).
43
+ use_colorize: 'RUBY_DEBUG_USE_COLORIZE', # Show coloring
43
44
 
44
45
  # remote
45
46
  port: 'RUBY_DEBUG_PORT', # TCP/IP remote debugging: port
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
47
49
  sock_dir: 'RUBY_DEBUG_SOCK_DIR', # UNIX Domain Socket remote debugging: socket directory
48
50
  }.freeze
49
51
 
50
52
  def self.config_to_env config
51
53
  CONFIG_MAP.each{|key, evname|
52
- ENV[evname] = config[key]
54
+ ENV[evname] = config[key].to_s if config[key]
53
55
  }
54
56
  end
55
57
 
56
58
  def self.parse_argv argv
57
59
  config = {
58
60
  mode: :start,
61
+ use_colorize: true,
59
62
  }
60
63
  CONFIG_MAP.each{|key, evname|
61
- config[key] = ENV[evname] if ENV[evname]
64
+ 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
75
+ end
62
76
  }
63
77
  return config if !argv || argv.empty?
64
78
 
@@ -83,6 +97,9 @@ module DEBUGGER__
83
97
  'a UNIX domain socket will be used.') do
84
98
  config[:remote] = true
85
99
  end
100
+ o.on('--sock-path=[SOCK_PATH]', 'UNIX Doman socket path') do |path|
101
+ config[:sock_path] = path
102
+ end
86
103
  o.on('--port=[PORT]', 'Listening TCP/IP port') do |port|
87
104
  config[:port] = port
88
105
  end
@@ -95,11 +112,11 @@ module DEBUGGER__
95
112
  o.separator ''
96
113
  o.separator ' Debug console mode runs Ruby program with the debug console.'
97
114
  o.separator ''
98
- o.separator " #{rdbg} target.rb foo bar starts like 'ruby target.rb foo bar'."
99
- o.separator " #{rdbg} -- -r foo -e bar starts like 'ruby -r foo -e bar'."
100
- o.separator " #{rdbg} -O target.rb foo bar starts and accepts attaching with UNIX domain socket."
101
- o.separator " #{rdbg} -O --port 1234 target.rb foo bar starts accepts attaching with TCP/IP localhost:1234."
102
- o.separator " #{rdbg} -O --port 1234 -- -r foo -e bar starts accepts attaching with TCP/IP localhost:1234."
115
+ o.separator " '#{rdbg} target.rb foo bar' starts like 'ruby target.rb foo bar'."
116
+ o.separator " '#{rdbg} -- -r foo -e bar' starts like 'ruby -r foo -e bar'."
117
+ o.separator " '#{rdbg} -O target.rb foo bar' starts and accepts attaching with UNIX domain socket."
118
+ o.separator " '#{rdbg} -O --port 1234 target.rb foo bar' starts accepts attaching with TCP/IP localhost:1234."
119
+ o.separator " '#{rdbg} -O --port 1234 -- -r foo -e bar' starts accepts attaching with TCP/IP localhost:1234."
103
120
 
104
121
  o.separator ''
105
122
  o.separator 'Attach mode:'
@@ -110,11 +127,11 @@ module DEBUGGER__
110
127
  o.separator ''
111
128
  o.separator ' Attach mode attaches the remote debug console to the debuggee process.'
112
129
  o.separator ''
113
- o.separator " '#{rdbg} -A' tries to connect via UNIX domain socket."
114
- o.separator " #{' ' * rdbg.size} If there are multiple processes are waiting for the"
115
- o.separator " #{' ' * rdbg.size} debugger connection, list possible debuggee names."
116
- o.separator " '#{rdbg} -A path' tries to connect via UNIX domain socket with given path name."
117
- o.separator " '#{rdbg} -A port' tries to connect to localhost:port via TCP/IP."
130
+ o.separator " '#{rdbg} -A' tries to connect via UNIX domain socket."
131
+ o.separator " #{' ' * rdbg.size} If there are multiple processes are waiting for the"
132
+ o.separator " #{' ' * rdbg.size} debugger connection, list possible debuggee names."
133
+ o.separator " '#{rdbg} -A path' tries to connect via UNIX domain socket with given path name."
134
+ o.separator " '#{rdbg} -A port' tries to connect to localhost:port via TCP/IP."
118
135
  o.separator " '#{rdbg} -A host port' tries to connect to host:port via TCP/IP."
119
136
  end
120
137
 
@@ -122,4 +139,16 @@ module DEBUGGER__
122
139
 
123
140
  config
124
141
  end
142
+
143
+ CONFIG = ::DEBUGGER__.parse_argv(ENV['RUBY_DEBUG_OPT'])
144
+
145
+ def self.set_config kw
146
+ kw.each{|k, v|
147
+ if CONFIG_MAP[k]
148
+ CONFIG[k] = v # TODO: ractor support
149
+ else
150
+ raise "unknown option: #{k}"
151
+ end
152
+ }
153
+ end
125
154
  end
@@ -0,0 +1,113 @@
1
+
2
+ module DEBUGGER__
3
+ FrameInfo = Struct.new(:location, :self, :binding, :iseq, :class, :frame_depth,
4
+ :has_return_value, :return_value, :show_line)
5
+
6
+
7
+ # extend FrameInfo with debug.so
8
+ if File.exist? File.join(__dir__, 'debug.so')
9
+ require_relative 'debug.so'
10
+ else
11
+ require "debug/debug"
12
+ end
13
+
14
+ class FrameInfo
15
+ HOME = ENV['HOME'] ? (ENV['HOME'] + '/') : nil
16
+
17
+ def path
18
+ location.path
19
+ end
20
+
21
+ def realpath
22
+ location.absolute_path
23
+ end
24
+
25
+ def pretty_path
26
+ use_short_path = ::DEBUGGER__::CONFIG[:use_short_path]
27
+
28
+ case
29
+ when use_short_path && path.start_with?(dir = ::DEBUGGER__::CONFIG["rubylibdir"] + '/')
30
+ path.sub(dir, '$(rubylibdir)/')
31
+ when use_short_path && Gem.path.any? do |gp|
32
+ path.start_with?(dir = gp + '/gems/')
33
+ end
34
+ path.sub(dir, '$(Gem)/')
35
+ when HOME && path.start_with?(HOME)
36
+ path.sub(HOME, '~/')
37
+ else
38
+ path
39
+ end
40
+ end
41
+
42
+ def file_lines
43
+ SESSION.source(realpath || path)
44
+ end
45
+
46
+ def call_identifier_str
47
+ if binding && iseq
48
+ if iseq.type == :block
49
+ if (argc = iseq.argc) > 0
50
+ args = parameters_info iseq.locals[0...argc]
51
+ args_str = "{|#{args}|}"
52
+ end
53
+
54
+ location.label.sub('block'){ "block#{args_str}" }
55
+ elsif (callee = binding.eval('__callee__', __FILE__, __LINE__)) && (argc = iseq.argc) > 0
56
+ args = parameters_info iseq.locals[0...argc]
57
+ "#{klass_sig}#{callee}(#{args})"
58
+ else
59
+ location.label
60
+ end
61
+ else
62
+ "[C] #{klass_sig}#{location.base_label}"
63
+ end
64
+ end
65
+
66
+ def return_str
67
+ if binding && iseq && has_return_value
68
+ short_inspect(return_value)
69
+ end
70
+ end
71
+
72
+ def location_str
73
+ "#{pretty_path}:#{location.lineno}"
74
+ end
75
+
76
+ private
77
+
78
+ SHORT_INSPECT_LENGTH = 40
79
+
80
+ def short_inspect obj
81
+ str = obj.inspect
82
+ if str.length > SHORT_INSPECT_LENGTH
83
+ str[0...SHORT_INSPECT_LENGTH] + '...'
84
+ else
85
+ str
86
+ end
87
+ end
88
+
89
+ def get_singleton_class obj
90
+ obj.singleton_class # TODO: don't use it
91
+ rescue TypeError
92
+ nil
93
+ end
94
+
95
+ def parameters_info vars
96
+ vars.map{|var|
97
+ begin
98
+ "#{var}=#{short_inspect(binding.local_variable_get(var))}"
99
+ rescue NameError, TypeError
100
+ nil
101
+ end
102
+ }.compact.join(', ')
103
+ end
104
+
105
+ def klass_sig
106
+ if self.class == get_singleton_class(self.self)
107
+ "#{self.self}."
108
+ else
109
+ "#{self.class}#"
110
+ end
111
+ end
112
+ end
113
+ end
data/lib/debug/server.rb CHANGED
@@ -187,39 +187,30 @@ module DEBUGGER__
187
187
  end
188
188
 
189
189
  class UI_UnixDomainServer < UI_ServerBase
190
- def initialize sock_dir: nil
190
+ def initialize sock_dir: nil, sock_path: nil
191
+ @sock_path = sock_path
191
192
  @sock_dir = sock_dir || DEBUGGER__.unix_domain_socket_dir
192
193
 
193
194
  super()
194
195
  end
195
196
 
196
197
  def accept
197
- @file = DEBUGGER__.create_unix_domain_socket_name(@sock_dir)
198
+ case
199
+ when @sock_path
200
+ when sp = ::DEBUGGER__::CONFIG[:sock_path]
201
+ @sock_path = sp
202
+ else
203
+ @sock_path = DEBUGGER__.create_unix_domain_socket_name(@sock_dir)
204
+ end
198
205
 
199
- ::DEBUGGER__.message "Debugger can attach via UNIX domain socket (#{@file})"
200
- Socket.unix_server_loop @file do |sock, client|
206
+ ::DEBUGGER__.message "Debugger can attach via UNIX domain socket (#{@sock_path})"
207
+ Socket.unix_server_loop @sock_path do |sock, client|
201
208
  @client_addr = client
202
209
  yield sock
203
210
  end
204
211
  end
205
212
  end
206
213
 
207
- def self.open host: nil, port: ::DEBUGGER__::CONFIG[:port], sock_dir: nil
208
- if port
209
- open_tcp host: host, port: port
210
- else
211
- open_unix sock_dir: sock_dir
212
- end
213
- end
214
-
215
- def self.open_tcp(host: nil, port:)
216
- initialize_session UI_TcpServer.new(host: host, port: port)
217
- end
218
-
219
- def self.open_unix sock_dir: nil
220
- initialize_session UI_UnixDomainServer.new(sock_dir: sock_dir)
221
- end
222
-
223
214
  def self.message msg
224
215
  $stderr.puts "DEBUGGER: #{msg}"
225
216
  end
data/lib/debug/session.rb CHANGED
@@ -1,18 +1,7 @@
1
- module DEBUGGER__
2
- # used in thread_client.c
3
- FrameInfo = Struct.new(:location, :self, :binding, :iseq, :class, :frame_depth,
4
- :has_return_value, :return_value, :show_line)
5
- end
6
-
7
- if File.exist? File.join(__dir__, 'debug.so')
8
- require_relative 'debug.so'
9
- else
10
- require "debug/debug"
11
- end
12
-
1
+ 
2
+ require_relative 'thread_client'
13
3
  require_relative 'source_repository'
14
4
  require_relative 'breakpoint'
15
- require_relative 'thread_client'
16
5
  require_relative 'config'
17
6
 
18
7
  class RubyVM::InstructionSequence
@@ -61,7 +50,7 @@ module DEBUGGER__
61
50
  @bps = {} # bp.key => bp
62
51
  # [file, line] => LineBreakpoint
63
52
  # "Error" => CatchBreakpoint
64
- # Method => MethodBreakpoint
53
+ # "Foo#bar" => MethodBreakpoint
65
54
  # [:watch, expr] => WatchExprBreakpoint
66
55
  # [:check, expr] => CheckBreakpoint
67
56
  @th_clients = {} # {Thread => ThreadClient}
@@ -73,7 +62,8 @@ module DEBUGGER__
73
62
 
74
63
  @tp_load_script = TracePoint.new(:script_compiled){|tp|
75
64
  ThreadClient.current.on_load tp.instruction_sequence, tp.eval_script
76
- }.enable
65
+ }
66
+ @tp_load_script.enable
77
67
 
78
68
  @session_server = Thread.new do
79
69
  Thread.current.abort_on_exception = true
@@ -149,7 +139,8 @@ module DEBUGGER__
149
139
 
150
140
  @tp_thread_begin = TracePoint.new(:thread_begin){|tp|
151
141
  ThreadClient.current.on_thread_begin Thread.current
152
- }.enable
142
+ }
143
+ @tp_thread_begin.enable
153
144
  end
154
145
 
155
146
  def add_initial_commands cmds
@@ -160,7 +151,11 @@ module DEBUGGER__
160
151
  end
161
152
 
162
153
  def source path
163
- @sr.get(path)
154
+ if CONFIG[:use_colorize]
155
+ @sr.get_colored(path)
156
+ else
157
+ @sr.get(path)
158
+ end
164
159
  end
165
160
 
166
161
  def inspect
@@ -409,17 +404,23 @@ module DEBUGGER__
409
404
 
410
405
  @tc << [:show, :edit, arg]
411
406
 
412
- # * `i[nfo]`
407
+ # * `i[nfo]`, `i[nfo] l[ocal[s]]`
413
408
  # * Show information about the current frame (local variables)
414
409
  # * It includes `self` as `%self` and a return value as `%return`.
415
- # * `i[nfo] <expr>`
416
- # * Show information about the result of <expr>.
410
+ # * `i[nfo] th[read[s]]
411
+ # * Show all threads (same as `th[read]`).
417
412
  when 'i', 'info'
418
413
  case arg
419
414
  when nil
420
415
  @tc << [:show, :local]
416
+ when 'l', /locals?/
417
+ @tc << [:show, :local]
418
+ when 'th', /threads?/
419
+ thread_list
420
+ return :retry
421
421
  else
422
- @tc << [:show, :object_info, arg]
422
+ show_help 'info'
423
+ return :retry
423
424
  end
424
425
 
425
426
  # * `display`
@@ -855,7 +856,7 @@ module DEBUGGER__
855
856
 
856
857
  def add_catch_breakpoint arg
857
858
  bp = CatchBreakpoint.new(arg)
858
- add_braekpoint bp
859
+ add_breakpoint bp
859
860
  end
860
861
 
861
862
  def add_check_breakpoint expr
@@ -897,26 +898,37 @@ module DEBUGGER__
897
898
  b = tp.binding
898
899
  if var_name = b.local_variables.first
899
900
  mid = b.local_variable_get(var_name)
900
- found = false
901
+ unresolved = false
901
902
 
902
903
  @bps.each{|k, bp|
903
904
  case bp
904
905
  when MethodBreakpoint
905
906
  if bp.method.nil?
906
- found = true
907
907
  if bp.sig_method_name == mid.to_s
908
- bp.enable(quiet: true)
908
+ bp.try_enable(quiet: true)
909
909
  end
910
910
  end
911
+
912
+ unresolved = true unless bp.enabled?
911
913
  end
912
914
  }
913
- unless found
915
+ unless unresolved
914
916
  METHOD_ADDED_TRACKER.disable
915
917
  end
916
918
  end
917
919
  end
918
920
  end
919
921
 
922
+ # manual configuration methods
923
+
924
+ def self.add_line_breakpoint file, line, **kw
925
+ ::DEBUGGER__::SESSION.add_line_breakpoint file, line, **kw
926
+ end
927
+
928
+ def self.add_catch_breakpoint pat
929
+ ::DEBUGGER__::SESSION.add_catch_breakpoint pat
930
+ end
931
+
920
932
  # String for requring location
921
933
  # nil for -r
922
934
  def self.require_location
@@ -934,7 +946,12 @@ module DEBUGGER__
934
946
  nil
935
947
  end
936
948
 
937
- def self.console
949
+ # start methods
950
+
951
+ def self.console **kw
952
+ set_config(kw)
953
+
954
+ require_relative 'console'
938
955
  initialize_session UI_Console.new
939
956
 
940
957
  @prev_handler = trap(:SIGINT){
@@ -942,14 +959,30 @@ module DEBUGGER__
942
959
  }
943
960
  end
944
961
 
945
- def self.add_line_breakpoint file, line, **kw
946
- ::DEBUGGER__::SESSION.add_line_breakpoint file, line, **kw
962
+ def self.open host: nil, port: ::DEBUGGER__::CONFIG[:port], sock_path: nil, sock_dir: nil, **kw
963
+ set_config(kw)
964
+
965
+ if port
966
+ open_tcp host: host, port: port
967
+ else
968
+ open_unix sock_path: sock_path, sock_dir: sock_dir
969
+ end
947
970
  end
948
971
 
949
- def self.add_catch_breakpoint pat
950
- ::DEBUGGER__::SESSION.add_catch_breakpoint pat
972
+ def self.open_tcp host: nil, port:, **kw
973
+ set_config(kw)
974
+ require_relative 'server'
975
+ initialize_session UI_TcpServer.new(host: host, port: port)
951
976
  end
952
977
 
978
+ def self.open_unix sock_path: nil, sock_dir: nil, **kw
979
+ set_config(kw)
980
+ require_relative 'server'
981
+ initialize_session UI_UnixDomainServer.new(sock_dir: sock_dir, sock_path: sock_path)
982
+ end
983
+
984
+ # boot utilities
985
+
953
986
  class << self
954
987
  define_method :initialize_session do |ui|
955
988
  ::DEBUGGER__.const_set(:SESSION, Session.new(ui))
@@ -963,7 +996,7 @@ module DEBUGGER__
963
996
  def bp; nil; end
964
997
  end
965
998
 
966
- if ::DEBUGGER__::CONFIG[:nonstop] != '1'
999
+ if !::DEBUGGER__::CONFIG[:nonstop]
967
1000
  if loc = ::DEBUGGER__.require_location
968
1001
  # require 'debug/console' or 'debug'
969
1002
  add_line_breakpoint loc.absolute_path, loc.lineno + 1, oneshot: true, hook_call: false
@@ -1052,8 +1085,6 @@ module DEBUGGER__
1052
1085
  r.join("\n")
1053
1086
  end
1054
1087
 
1055
- CONFIG = ::DEBUGGER__.parse_argv(ENV['RUBY_DEBUG_OPT'])
1056
-
1057
1088
  class ::Module
1058
1089
  def method_added mid; end
1059
1090
  def singleton_method_added mid; end
@@ -1,27 +1,57 @@
1
+ require 'irb/color' # IRB::Color.colorize_code
2
+
1
3
  module DEBUGGER__
2
4
  class SourceRepository
3
5
  def initialize
4
6
  @files = {} # filename => [src, iseq]
7
+ @color_files = {}
5
8
  end
6
9
 
7
10
  def add iseq, src
8
11
  path = iseq.absolute_path
9
12
  path = '-e' if iseq.path == '-e'
13
+ add_path path, src: src
14
+ end
10
15
 
16
+ def add_path path, src: nil
11
17
  case
12
- when path = iseq.absolute_path
13
- src = File.read(path)
14
- when iseq.path == '-e'
15
- path = '-e'
18
+ when src
19
+ if File.file?(path)
20
+ path = '(eval)' + path
21
+ src = nil
22
+ end
23
+ when path == '-e'
24
+ when path
25
+ begin
26
+ src = File.read(path)
27
+ rescue SystemCallError
28
+ end
16
29
  else
17
30
  src = nil
18
31
  end
19
32
 
20
- @files[path] = src.lines if src
33
+ if src
34
+ src = src.gsub("\r\n", "\n") # CRLF -> LF
35
+ @files[path] = src.lines
36
+ end
21
37
  end
22
38
 
23
39
  def get path
24
- @files[path]
40
+ if @files.has_key? path
41
+ @files[path]
42
+ else
43
+ add_path path
44
+ end
45
+ end
46
+
47
+ def get_colored path
48
+ if src_lines = @color_files[path]
49
+ return src_lines
50
+ else
51
+ if src_lines = get(path)
52
+ @color_files[path] = IRB::Color.colorize_code(src_lines.join).lines
53
+ end
54
+ end
25
55
  end
26
56
  end
27
57
  end
@@ -1,5 +1,7 @@
1
1
  require 'objspace'
2
2
  require 'pp'
3
+ require 'irb/color'
4
+ require_relative 'frame_info'
3
5
 
4
6
  module DEBUGGER__
5
7
  class ThreadClient
@@ -12,6 +14,27 @@ module DEBUGGER__
12
14
 
13
15
  attr_reader :location, :thread, :mode, :id
14
16
 
17
+ def colorize str, color
18
+ if CONFIG[:use_colorize]
19
+ IRB::Color.colorize str, color
20
+ else
21
+ str
22
+ end
23
+ end
24
+
25
+ def default_frame_formatter frame
26
+ call_identifier_str = colorize(frame.call_identifier_str, [:BLUE, :BOLD])
27
+ location_str = colorize(frame.location_str, [:YELLOW])
28
+ result = "#{call_identifier_str} at #{location_str}"
29
+
30
+ if return_str = frame.return_str
31
+ return_str = colorize(frame.return_str, [:MAGENTA])
32
+ result += " #=> #{return_str}"
33
+ end
34
+
35
+ result
36
+ end
37
+
15
38
  def initialize id, q_evt, q_cmd, thr = Thread.current
16
39
  @id = id
17
40
  @thread = thr
@@ -19,8 +42,9 @@ module DEBUGGER__
19
42
  @q_cmd = q_cmd
20
43
  @step_tp = nil
21
44
  @output = []
22
- @src_lines_on_stop = (DEBUGGER__::CONFIG[:show_src_lines] || 10).to_i
23
- @show_frames_on_stop = (DEBUGGER__::CONFIG[:show_frames] || 2).to_i
45
+ @src_lines_on_stop = (::DEBUGGER__::CONFIG[:show_src_lines] || 10).to_i
46
+ @show_frames_on_stop = (::DEBUGGER__::CONFIG[:show_frames] || 2).to_i
47
+ @frame_formatter = method(:default_frame_formatter)
24
48
  set_mode nil
25
49
  end
26
50
 
@@ -129,7 +153,7 @@ module DEBUGGER__
129
153
  next if SESSION.break? tp.path, tp.lineno
130
154
  next if !yield
131
155
  next if tp.path.start_with?(__dir__)
132
- next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
156
+ next unless File.exist?(tp.path) if ::DEBUGGER__::CONFIG[:skip_nosrc]
133
157
 
134
158
  tp.disable
135
159
  on_suspend tp.event, tp
@@ -140,7 +164,7 @@ module DEBUGGER__
140
164
  next if thread != Thread.current
141
165
  next if SESSION.break? tp.path, tp.lineno
142
166
  next if !yield
143
- next unless File.exist?(tp.path) if CONFIG[:skip_nosrc]
167
+ next unless File.exist?(tp.path) if ::DEBUGGER__::CONFIG[:skip_nosrc]
144
168
 
145
169
  tp.disable
146
170
  on_suspend tp.event, tp
@@ -157,23 +181,15 @@ module DEBUGGER__
157
181
  end
158
182
  end
159
183
 
160
- def file_lines path
161
- if (src_lines = SESSION.source(path))
162
- src_lines
163
- elsif File.exist?(path)
164
- File.readlines(path)
165
- end
166
- end
167
-
168
184
  def show_src(frame_index: @current_frame_index,
169
185
  update_line: false,
170
186
  max_lines: 10,
171
187
  start_line: nil,
172
188
  end_line: nil,
173
189
  dir: +1)
174
- #
190
+
175
191
  if @target_frames && frame = @target_frames[frame_index]
176
- if file_lines = file_lines(path = frame.location.path)
192
+ if file_lines = frame.file_lines
177
193
  frame_line = frame.location.lineno - 1
178
194
 
179
195
  lines = file_lines.map.with_index do |e, i|
@@ -206,11 +222,11 @@ module DEBUGGER__
206
222
  end
207
223
 
208
224
  if start_line != end_line && max_lines
209
- puts "[#{start_line+1}, #{end_line}] in #{pretty_path(path)}" if !update_line && max_lines != 1
225
+ puts "[#{start_line+1}, #{end_line}] in #{frame.pretty_path}" if !update_line && max_lines != 1
210
226
  puts lines[start_line ... end_line]
211
227
  end
212
228
  else # no file lines
213
- puts "# No sourcefile available for #{path}"
229
+ puts "# No sourcefile available for #{frame.path}"
214
230
  end
215
231
  end
216
232
  end
@@ -218,7 +234,7 @@ module DEBUGGER__
218
234
  def show_by_editor path = nil
219
235
  unless path
220
236
  if @target_frames && frame = @target_frames[@current_frame_index]
221
- path = frame.location.path
237
+ path = frame.path
222
238
  else
223
239
  return # can't get path
224
240
  end
@@ -287,101 +303,12 @@ module DEBUGGER__
287
303
  end
288
304
  end
289
305
 
290
- def parameters_info b, vars
291
- vars.map{|var|
292
- begin
293
- "#{var}=#{short_inspect(b.local_variable_get(var))}"
294
- rescue NameError, TypeError
295
- nil
296
- end
297
- }.compact.join(', ')
298
- end
299
-
300
- def get_singleton_class obj
301
- obj.singleton_class # TODO: don't use it
302
- rescue TypeError
303
- nil
304
- end
305
-
306
- def klass_sig frame
307
- klass = frame.class
308
- if klass == get_singleton_class(frame.self)
309
- "#{frame.self}."
310
- else
311
- "#{frame.class}#"
312
- end
313
- end
314
-
315
- SHORT_INSPECT_LENGTH = 40
316
-
317
- def short_inspect obj
318
- str = obj.inspect
319
- if str.length > SHORT_INSPECT_LENGTH
320
- str[0...SHORT_INSPECT_LENGTH] + '...'
321
- else
322
- str
323
- end
324
- end
325
-
326
- HOME = ENV['HOME'] ? (ENV['HOME'] + '/') : nil
327
-
328
- def pretty_path path
329
- use_short_path = CONFIG[:use_short_path]
330
-
331
- case
332
- when use_short_path && path.start_with?(dir = RbConfig::CONFIG["rubylibdir"] + '/')
333
- path.sub(dir, '$(rubylibdir)/')
334
- when use_short_path && Gem.path.any? do |gp|
335
- path.start_with?(dir = gp + '/gems/')
336
- end
337
- path.sub(dir, '$(Gem)/')
338
- when HOME && path.start_with?(HOME)
339
- path.sub(HOME, '~/')
340
- else
341
- path
342
- end
343
- end
344
-
345
- def pretty_location loc
346
- " at #{pretty_path(loc.path)}:#{loc.lineno}"
347
- end
348
-
349
- def frame_str i
350
- frame = @target_frames[i]
351
- b = frame.binding
352
-
306
+ def frame_str(i)
353
307
  cur_str = (@current_frame_index == i ? '=>' : ' ')
354
-
355
- if b && (iseq = frame.iseq)
356
- if iseq.type == :block
357
- if (argc = iseq.argc) > 0
358
- args = parameters_info b, iseq.locals[0...argc]
359
- args_str = "{|#{args}|}"
360
- end
361
-
362
- label_prefix = frame.location.label.sub('block'){ "block#{args_str}" }
363
- ci_str = label_prefix
364
- elsif (callee = b.eval('__callee__', __FILE__, __LINE__)) && (argc = iseq.argc) > 0
365
- args = parameters_info b, iseq.locals[0...argc]
366
- ksig = klass_sig frame
367
- ci_str = "#{ksig}#{callee}(#{args})"
368
- else
369
- ci_str = frame.location.label
370
- end
371
-
372
- loc_str = "#{pretty_location(frame.location)}"
373
-
374
- if frame.has_return_value
375
- return_str = " #=> #{short_inspect(frame.return_value)}"
376
- end
377
- else
378
- ksig = klass_sig frame
379
- callee = frame.location.base_label
380
- ci_str = "[C] #{ksig}#{callee}"
381
- loc_str = "#{pretty_location(frame.location)}"
382
- end
383
-
384
- "#{cur_str}##{i}\t#{ci_str}#{loc_str}#{return_str}"
308
+ prefix = "#{cur_str}##{i}"
309
+ frame = @target_frames[i]
310
+ frame_string = @frame_formatter.call(frame)
311
+ "#{prefix}\t#{frame_string}"
385
312
  end
386
313
 
387
314
  def show_frames max = (@target_frames || []).size
@@ -457,7 +384,7 @@ module DEBUGGER__
457
384
  step_tp{true}
458
385
  when :next
459
386
  frame = @target_frames.first
460
- path = frame.location.absolute_path || "!eval:#{frame.location.path}"
387
+ path = frame.location.absolute_path || "!eval:#{frame.path}"
461
388
  line = frame.location.lineno
462
389
  frame.iseq.traceable_lines_norec(lines = {})
463
390
  next_line = lines.keys.bsearch{|e| e > line}
@@ -533,7 +460,7 @@ module DEBUGGER__
533
460
  case type
534
461
  when :up
535
462
  if @current_frame_index + 1 < @target_frames.size
536
- @current_frame_index += 1
463
+ @current_frame_index += 1
537
464
  show_src max_lines: 1
538
465
  show_frame(@current_frame_index)
539
466
  end
data/lib/debug/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module DEBUGGER__
2
- VERSION = "1.0.0.beta3"
2
+ VERSION = "1.0.0.beta4"
3
3
  end
data/misc/README.md.erb CHANGED
@@ -62,13 +62,14 @@ $ ruby -r debug/run target.rb
62
62
 
63
63
  $ cat target.rb
64
64
  require 'debug/run' # start the debug console
65
- ...
65
+ ... rest of program ...
66
66
 
67
67
  # or
68
68
 
69
69
  $ cat target.rb
70
70
  require 'debug/session' # introduce the functionality
71
71
  DEBUGGER__.console # and start the debug console
72
+ ... rest of program ...
72
73
 
73
74
  $ ruby target.rb
74
75
  ```
@@ -295,10 +296,10 @@ You can control debuggee's behavior with environment variables:
295
296
  * `RUBY_DEBUG_COMMANDS`: Debug commands invoked at the first stop. Commands should be separated by ';;'.
296
297
  * `RUBY_DEBUG_SHOW_SRC_LINES`: Show n lines source code on breakpoint (default: 10 lines).
297
298
  * `RUBY_DEBUG_SHOW_FRAMES`: Show n frames on breakpoint (default: 2 frames).
298
-
299
299
  * Remote debugging
300
300
  * `RUBY_DEBUG_PORT`: TCP/IP remote debugging: port to open.
301
301
  * `RUBY_DEBUG_HOST`: TCP/IP remote debugging: host (localhost if not given) to open.
302
+ * `RUBY_DEBUG_SOCK_PATH`: UNIX Domain Socket remote debugging: socket path to open.
302
303
  * `RUBY_DEBUG_SOCK_DIR`: UNIX Domain Socket remote debugging: socket directory to open.
303
304
 
304
305
  ## Debug command on the debug console
@@ -322,3 +323,5 @@ The `<...>` notation means the argument.
322
323
  # Contributing
323
324
 
324
325
  Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/debug.
326
+
327
+ Please also check the [contributing guideline](/CONTRIBUTING.md).
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: debug
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.beta3
4
+ version: 1.0.0.beta4
5
5
  platform: ruby
6
6
  authors:
7
7
  - Koichi Sasada
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2021-05-14 00:00:00.000000000 Z
11
+ date: 2021-05-25 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Debugging functionality for Ruby. This is completely rewritten debug.rb
14
14
  which was contained by the encient Ruby versions.
@@ -21,10 +21,12 @@ extensions:
21
21
  extra_rdoc_files: []
22
22
  files:
23
23
  - ".gitignore"
24
+ - CONTRIBUTING.md
24
25
  - Gemfile
25
26
  - LICENSE.txt
26
27
  - README.md
27
28
  - Rakefile
29
+ - TODO.md
28
30
  - bin/console
29
31
  - bin/setup
30
32
  - debug.gemspec
@@ -38,6 +40,7 @@ files:
38
40
  - lib/debug/client.rb
39
41
  - lib/debug/config.rb
40
42
  - lib/debug/console.rb
43
+ - lib/debug/frame_info.rb
41
44
  - lib/debug/open.rb
42
45
  - lib/debug/run.rb
43
46
  - lib/debug/server.rb