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.
- checksums.yaml +4 -4
- data/CONTRIBUTING.md +213 -20
- data/Gemfile +1 -0
- data/README.md +460 -226
- data/Rakefile +2 -1
- data/TODO.md +0 -6
- data/bin/gentest +22 -0
- data/debug.gemspec +1 -0
- data/exe/rdbg +11 -18
- data/ext/debug/debug.c +11 -1
- data/lib/debug/breakpoint.rb +106 -62
- data/lib/debug/client.rb +11 -17
- data/lib/debug/color.rb +28 -7
- data/lib/debug/config.rb +378 -144
- data/lib/debug/console.rb +79 -57
- data/lib/debug/frame_info.rb +42 -8
- data/lib/debug/local.rb +91 -0
- data/lib/debug/open.rb +2 -1
- data/lib/debug/open_nonstop.rb +15 -0
- data/lib/debug/server.rb +96 -43
- data/lib/debug/server_dap.rb +34 -7
- data/lib/debug/session.rb +827 -341
- data/lib/debug/source_repository.rb +2 -0
- data/lib/debug/start.rb +5 -0
- data/lib/debug/thread_client.rb +691 -184
- data/lib/debug/tracer.rb +242 -0
- data/lib/debug/version.rb +3 -1
- data/lib/debug.rb +3 -0
- data/misc/README.md.erb +341 -216
- metadata +21 -4
- data/lib/debug/run.rb +0 -4
- data/lib/debug/test_console.rb +0 -0
data/lib/debug/tracer.rb
ADDED
@@ -0,0 +1,242 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module DEBUGGER__
|
4
|
+
class Tracer
|
5
|
+
include SkipPathHelper
|
6
|
+
include Color
|
7
|
+
|
8
|
+
def colorize(str, color)
|
9
|
+
# don't colorize trace sent into a file
|
10
|
+
if @into
|
11
|
+
str
|
12
|
+
else
|
13
|
+
super
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :type
|
18
|
+
|
19
|
+
def initialize ui, pattern: nil, into: nil
|
20
|
+
if /\ADEBUGGER__::(([A-Z][a-z]+?)[A-Z][a-z]+)/ =~ self.class.name
|
21
|
+
@name = $1
|
22
|
+
@type = $2.downcase
|
23
|
+
end
|
24
|
+
|
25
|
+
setup
|
26
|
+
|
27
|
+
if pattern
|
28
|
+
@pattern = Regexp.compile(pattern)
|
29
|
+
else
|
30
|
+
@pattern = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
if @into = into
|
34
|
+
@output = File.open(into, 'w')
|
35
|
+
@output.puts "PID:#{Process.pid} #{self}"
|
36
|
+
else
|
37
|
+
@output = ui
|
38
|
+
end
|
39
|
+
|
40
|
+
enable
|
41
|
+
end
|
42
|
+
|
43
|
+
def header depth
|
44
|
+
"DEBUGGER (trace/#{@type}) \#th:#{Thread.current.instance_variable_get(:@__thread_client_id)} \#depth:#{'%-2d'%depth}"
|
45
|
+
end
|
46
|
+
|
47
|
+
def enable
|
48
|
+
@tracer.enable
|
49
|
+
end
|
50
|
+
|
51
|
+
def disable
|
52
|
+
@tracer.disable
|
53
|
+
end
|
54
|
+
|
55
|
+
def description
|
56
|
+
nil
|
57
|
+
end
|
58
|
+
|
59
|
+
def to_s
|
60
|
+
s = "#{@name}#{description} (#{@tracer.enabled? ? 'enabled' : 'disabled'})"
|
61
|
+
s += " with pattern #{@pattern.inspect}" if @pattern
|
62
|
+
s += " into: #{@into}" if @into
|
63
|
+
s
|
64
|
+
end
|
65
|
+
|
66
|
+
def skip? tp
|
67
|
+
if tp.path.start_with?(__dir__) ||
|
68
|
+
tp.path.start_with?('<internal:') ||
|
69
|
+
ThreadClient.current.management? ||
|
70
|
+
skip_path?(tp.path) ||
|
71
|
+
skip_with_pattern?(tp)
|
72
|
+
true
|
73
|
+
else
|
74
|
+
false
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def skip_with_pattern?(tp)
|
79
|
+
@pattern && !tp.path.match?(@pattern)
|
80
|
+
end
|
81
|
+
|
82
|
+
def out tp, msg = nil, depth = caller.size - 1
|
83
|
+
location_str = colorize("#{tp.path}:#{tp.lineno}", [:GREEN])
|
84
|
+
buff = "#{header(depth)}#{msg} at #{location_str}"
|
85
|
+
|
86
|
+
if false # TODO: Ractor.main?
|
87
|
+
ThreadClient.current.on_trace self.object_id, buff
|
88
|
+
else
|
89
|
+
@output.puts buff
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def puts msg
|
94
|
+
@output.puts msg
|
95
|
+
end
|
96
|
+
|
97
|
+
def minfo tp
|
98
|
+
klass = tp.defined_class
|
99
|
+
|
100
|
+
if klass.singleton_class?
|
101
|
+
"#{tp.self}.#{tp.method_id}"
|
102
|
+
else
|
103
|
+
"#{klass}\##{tp.method_id}"
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class LineTracer < Tracer
|
109
|
+
def setup
|
110
|
+
@tracer = TracePoint.new(:line){|tp|
|
111
|
+
next if skip?(tp)
|
112
|
+
# pp tp.object_id, caller(0)
|
113
|
+
out tp
|
114
|
+
}
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
class CallTracer < Tracer
|
119
|
+
def setup
|
120
|
+
@tracer = TracePoint.new(:a_call, :a_return){|tp|
|
121
|
+
next if skip?(tp)
|
122
|
+
|
123
|
+
depth = caller.size
|
124
|
+
sp = ' ' * depth
|
125
|
+
|
126
|
+
call_identifier_str =
|
127
|
+
if tp.defined_class
|
128
|
+
minfo(tp)
|
129
|
+
else
|
130
|
+
"block"
|
131
|
+
end
|
132
|
+
|
133
|
+
call_identifier_str = colorize_blue(call_identifier_str)
|
134
|
+
|
135
|
+
case tp.event
|
136
|
+
when :call, :c_call, :b_call
|
137
|
+
depth += 1 if tp.event == :c_call
|
138
|
+
out tp, ">#{sp}#{call_identifier_str}", depth
|
139
|
+
when :return, :c_return, :b_return
|
140
|
+
depth += 1 if tp.event == :c_return
|
141
|
+
return_str = colorize_magenta(DEBUGGER__.short_inspect(tp.return_value))
|
142
|
+
out tp, "<#{sp}#{call_identifier_str} #=> #{return_str}", depth
|
143
|
+
end
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
def skip_with_pattern?(tp)
|
148
|
+
super && !tp.method_id&.match?(@pattern)
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
class ExceptionTracer < Tracer
|
153
|
+
def setup
|
154
|
+
@tracer = TracePoint.new(:raise) do |tp|
|
155
|
+
next if skip?(tp)
|
156
|
+
|
157
|
+
exc = tp.raised_exception
|
158
|
+
|
159
|
+
out tp, " #{colorize_magenta(exc.inspect)}"
|
160
|
+
rescue Exception => e
|
161
|
+
p e
|
162
|
+
end
|
163
|
+
end
|
164
|
+
|
165
|
+
def skip_with_pattern?(tp)
|
166
|
+
super && !tp.raised_exception.inspect.match?(@pattern)
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
class ObjectTracer < Tracer
|
171
|
+
def initialize ui, obj_id, obj_inspect, **kw
|
172
|
+
@obj_id = obj_id
|
173
|
+
@obj_inspect = obj_inspect
|
174
|
+
super(ui, **kw)
|
175
|
+
end
|
176
|
+
|
177
|
+
def description
|
178
|
+
" for #{@obj_inspect}"
|
179
|
+
end
|
180
|
+
|
181
|
+
def colorized_obj_inspect
|
182
|
+
colorize_magenta(@obj_inspect)
|
183
|
+
end
|
184
|
+
|
185
|
+
def setup
|
186
|
+
@tracer = TracePoint.new(:a_call){|tp|
|
187
|
+
next if skip?(tp)
|
188
|
+
|
189
|
+
if tp.self.object_id == @obj_id
|
190
|
+
klass = tp.defined_class
|
191
|
+
method = tp.method_id
|
192
|
+
method_info =
|
193
|
+
if klass.singleton_class?
|
194
|
+
if tp.self.is_a?(Class)
|
195
|
+
".#{method} (#{klass}.#{method})"
|
196
|
+
else
|
197
|
+
".#{method}"
|
198
|
+
end
|
199
|
+
else
|
200
|
+
"##{method} (#{klass}##{method})"
|
201
|
+
end
|
202
|
+
|
203
|
+
out tp, " #{colorized_obj_inspect} receives #{colorize_blue(method_info)}"
|
204
|
+
else
|
205
|
+
b = tp.binding
|
206
|
+
method_info = colorize_blue(minfo(tp))
|
207
|
+
|
208
|
+
tp.parameters.each{|type, name|
|
209
|
+
next unless name
|
210
|
+
|
211
|
+
colorized_name = colorize_cyan(name)
|
212
|
+
|
213
|
+
case type
|
214
|
+
when :req, :opt, :key, :keyreq
|
215
|
+
if b.local_variable_get(name).object_id == @obj_id
|
216
|
+
out tp, " #{colorized_obj_inspect} is used as a parameter #{colorized_name} of #{method_info}"
|
217
|
+
end
|
218
|
+
when :rest
|
219
|
+
next name == :"*"
|
220
|
+
|
221
|
+
ary = b.local_variable_get(name)
|
222
|
+
ary.each{|e|
|
223
|
+
if e.object_id == @obj_id
|
224
|
+
out tp, " #{colorized_obj_inspect} is used as a parameter in #{colorized_name} of #{method_info}"
|
225
|
+
end
|
226
|
+
}
|
227
|
+
when :keyrest
|
228
|
+
next if name == :'**'
|
229
|
+
h = b.local_variable_get(name)
|
230
|
+
h.each{|k, e|
|
231
|
+
if e.object_id == @obj_id
|
232
|
+
out tp, " #{colorized_obj_inspect} is used as a parameter in #{colorized_name} of #{method_info}"
|
233
|
+
end
|
234
|
+
}
|
235
|
+
end
|
236
|
+
}
|
237
|
+
end
|
238
|
+
}
|
239
|
+
end
|
240
|
+
end
|
241
|
+
end
|
242
|
+
|
data/lib/debug/version.rb
CHANGED
data/lib/debug.rb
CHANGED
data/misc/README.md.erb
CHANGED
@@ -11,8 +11,8 @@ New debug.rb has several advantages:
|
|
11
11
|
* Remote debugging: Support remote debugging natively.
|
12
12
|
* UNIX domain socket
|
13
13
|
* TCP/IP
|
14
|
-
* VSCode/DAP integration (
|
15
|
-
* Extensible: application can introduce debugging support with several
|
14
|
+
* VSCode/DAP integration ([VSCode rdbg Ruby Debugger - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg))
|
15
|
+
* Extensible: application can introduce debugging support with several ways:
|
16
16
|
* By `rdbg` command
|
17
17
|
* By loading libraries with `-r` command line option
|
18
18
|
* By calling Ruby's method explicitly
|
@@ -20,6 +20,7 @@ New debug.rb has several advantages:
|
|
20
20
|
* Support threads (almost done) and ractors (TODO).
|
21
21
|
* Support suspending and entering to the console debugging with `Ctrl-C` at most of timing.
|
22
22
|
* Show parameters on backtrace command.
|
23
|
+
* Support recording & reply debugging.
|
23
24
|
|
24
25
|
# Installation
|
25
26
|
|
@@ -29,309 +30,432 @@ $ gem install debug --pre
|
|
29
30
|
|
30
31
|
or specify `-Ipath/to/debug/lib` in `RUBYOPT` or each ruby command-line option, especially for debug this gem development.
|
31
32
|
|
32
|
-
|
33
|
+
If you use Bundler, write the following line to your Gemfile.
|
34
|
+
|
35
|
+
```
|
36
|
+
gem "debug", ">= 1.0.0.rc"
|
37
|
+
```
|
38
|
+
|
39
|
+
# HOW TO USE
|
40
|
+
|
41
|
+
To use a debugger, roughly you will do the following steps:
|
42
|
+
|
43
|
+
1. Set breakpoints.
|
44
|
+
2. Run a program with the debugger.
|
45
|
+
3. At the breakpoint, enter the debugger console.
|
46
|
+
4. Use debug commands.
|
47
|
+
* Query the program status (e.g. `p lvar` to see the local variable `lvar`).
|
48
|
+
* Control program flow (e.g. move to the another line with `step`, to the next line with `next`).
|
49
|
+
* Set another breakpoint (e.g. `catch Exception` to set a breakpoint when `Exception` is raised).
|
50
|
+
* Change the configuration (e.g. `config set no_color true` to disable coloring).
|
51
|
+
* Continue the program (`c` or `continue`) and goto 3.
|
52
|
+
|
53
|
+
## Invoke with the debugger
|
54
|
+
|
55
|
+
There are several options for (1) and (2). Please choose your favorite way.
|
56
|
+
|
57
|
+
### Modify source code as `binding.pry` and `binding.irb`
|
58
|
+
|
59
|
+
If you can modify the source code, you can use the debugger by adding `require 'debug'` line at the top of your program and putting `binding.break` method (`binding.b` for short) into lines where you want to stop as breakpoints like `binding.pry` and `binding.irb`.
|
60
|
+
After that, you run the program as usual and you will enter the debug console at breakpoints you inserted.
|
61
|
+
|
62
|
+
The following example shows the demonstration of `binding.break`.
|
63
|
+
|
64
|
+
```shell
|
65
|
+
$ cat target.rb # Sample program
|
66
|
+
require 'debug'
|
67
|
+
|
68
|
+
a = 1
|
69
|
+
b = 2
|
70
|
+
binding.break # Program will stop here
|
71
|
+
c = 3
|
72
|
+
d = 4
|
73
|
+
binding.break # Program will stop here
|
74
|
+
p [a, b, c, d]
|
75
|
+
|
76
|
+
$ ruby target.rb # Run the program normally.
|
77
|
+
DEBUGGER: Session start (pid: 7604)
|
78
|
+
[1, 10] in target.rb
|
79
|
+
1| require 'debug'
|
80
|
+
2|
|
81
|
+
3| a = 1
|
82
|
+
4| b = 2
|
83
|
+
=> 5| binding.break # Now you can see it stops at this line
|
84
|
+
6| c = 3
|
85
|
+
7| d = 4
|
86
|
+
8| binding.break
|
87
|
+
9| p [a, b, c, d]
|
88
|
+
10|
|
89
|
+
=>#0 <main> at target.rb:5
|
90
|
+
|
91
|
+
(rdbg) info locals # You can show local variables
|
92
|
+
=>#0 <main> at target.rb:5
|
93
|
+
%self => main
|
94
|
+
a => 1
|
95
|
+
b => 2
|
96
|
+
c => nil
|
97
|
+
d => nil
|
98
|
+
|
99
|
+
(rdbg) continue # Continue the execution
|
100
|
+
[3, 11] in target.rb
|
101
|
+
3| a = 1
|
102
|
+
4| b = 2
|
103
|
+
5| binding.break
|
104
|
+
6| c = 3
|
105
|
+
7| d = 4
|
106
|
+
=> 8| binding.break # Again the program stops at here
|
107
|
+
9| p [a, b, c, d]
|
108
|
+
10|
|
109
|
+
11| __END__
|
110
|
+
=>#0 <main> at target.rb:8
|
111
|
+
|
112
|
+
(rdbg) info locals # And you can see the updated local variables
|
113
|
+
=>#0 <main> at target.rb:8
|
114
|
+
%self => main
|
115
|
+
a => 1
|
116
|
+
b => 2
|
117
|
+
c => 3
|
118
|
+
d => 4
|
119
|
+
|
120
|
+
(rdbg) continue
|
121
|
+
[1, 2, 3, 4]
|
122
|
+
```
|
123
|
+
|
124
|
+
### Invoke the program from the debugger as a traditional debuggers
|
125
|
+
|
126
|
+
If you don't want to modify the source code, you can set breakpoints with a debug command `break` (`b` for short).
|
127
|
+
Using `rdbg` command to launch the program without any modifications, you can run the program with the debugger.
|
128
|
+
|
129
|
+
```shell
|
130
|
+
$ cat target.rb # Sample program
|
131
|
+
a = 1
|
132
|
+
b = 2
|
133
|
+
c = 3
|
134
|
+
d = 4
|
135
|
+
p [a, b, c, d]
|
136
|
+
|
137
|
+
$ rdbg target.rb # run like `ruby target.rb`
|
138
|
+
DEBUGGER: Session start (pid: 7656)
|
139
|
+
[1, 7] in target.rb
|
140
|
+
=> 1| a = 1
|
141
|
+
2| b = 2
|
142
|
+
3| c = 3
|
143
|
+
4| d = 4
|
144
|
+
5| p [a, b, c, d]
|
145
|
+
6|
|
146
|
+
7| __END__
|
147
|
+
=>#0 <main> at target.rb:1
|
33
148
|
|
34
|
-
|
149
|
+
(rdbg)
|
150
|
+
```
|
35
151
|
|
36
|
-
|
152
|
+
`rdbg` command suspends the program at the beginning of the given script (`target.rb` in this case) and you can use debug commands. `(rdbg)` is prompt. Let's set breakpoints on line 3 and line 5 with `break` command (`b` for short).
|
37
153
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
* (b-2) Open with TCP/IP port
|
154
|
+
```shell
|
155
|
+
(rdbg) break 3 # set breakpoint at line 3
|
156
|
+
#0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line)
|
42
157
|
|
43
|
-
(
|
44
|
-
|
158
|
+
(rdbg) b 5 # set breakpoint at line 5
|
159
|
+
#1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line)
|
45
160
|
|
46
|
-
|
161
|
+
(rdbg) break # show all registered breakpoints
|
162
|
+
#0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line)
|
163
|
+
#1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line)
|
164
|
+
```
|
47
165
|
|
48
|
-
|
49
|
-
* (2) Use `ruby -r debug...` command line option
|
50
|
-
* (3) Write `require 'debug...'` in .rb files
|
166
|
+
You can see that two breakpoints are registered. Let's continue the program by `continue` command.
|
51
167
|
|
52
|
-
|
168
|
+
```shell
|
169
|
+
(rdbg) continue
|
170
|
+
[1, 7] in target.rb
|
171
|
+
1| a = 1
|
172
|
+
2| b = 2
|
173
|
+
=> 3| c = 3
|
174
|
+
4| d = 4
|
175
|
+
5| p [a, b, c, d]
|
176
|
+
6|
|
177
|
+
7| __END__
|
178
|
+
=>#0 <main> at target.rb:3
|
53
179
|
|
54
|
-
|
180
|
+
Stop by #0 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:3 (line)
|
55
181
|
|
56
|
-
|
57
|
-
$ rdbg target.rb
|
58
|
-
$ rdbg -- -r foo -e expr # -- is required to make clear rdbg options and ruby's options
|
182
|
+
(rdbg)
|
59
183
|
```
|
60
184
|
|
61
|
-
|
185
|
+
You can see that we can stop at line 3.
|
186
|
+
Let's see the local variables with `info` command, and continue.
|
187
|
+
You can also confirm that the program will suspend at line 5 and you can use `info` command again.
|
62
188
|
|
63
|
-
```
|
64
|
-
|
65
|
-
|
189
|
+
```shell
|
190
|
+
(rdbg) info
|
191
|
+
=>#0 <main> at target.rb:3
|
192
|
+
%self => main
|
193
|
+
a => 1
|
194
|
+
b => 2
|
195
|
+
c => nil
|
196
|
+
d => nil
|
66
197
|
|
67
|
-
|
198
|
+
(rdbg) continue
|
199
|
+
[1, 7] in target.rb
|
200
|
+
1| a = 1
|
201
|
+
2| b = 2
|
202
|
+
3| c = 3
|
203
|
+
4| d = 4
|
204
|
+
=> 5| p [a, b, c, d]
|
205
|
+
6|
|
206
|
+
7| __END__
|
207
|
+
=>#0 <main> at target.rb:5
|
68
208
|
|
69
|
-
|
70
|
-
# target.rb
|
71
|
-
require 'debug/run' # start the debug console
|
209
|
+
Stop by #1 BP - Line /mnt/c/ko1/src/rb/ruby-debug/target.rb:5 (line)
|
72
210
|
|
73
|
-
|
211
|
+
(rdbg) info
|
212
|
+
=>#0 <main> at target.rb:5
|
213
|
+
%self => main
|
214
|
+
a => 1
|
215
|
+
b => 2
|
216
|
+
c => 3
|
217
|
+
d => 4
|
74
218
|
|
75
|
-
|
76
|
-
|
77
|
-
# ... rest of program ...
|
219
|
+
(rdbg) continue
|
220
|
+
[1, 2, 3, 4]
|
78
221
|
```
|
79
222
|
|
80
|
-
|
81
|
-
|
82
|
-
```
|
223
|
+
By the way, using `rdbg` command you can suspend your application with `C-c` (SIGINT) and enter the debug console.
|
224
|
+
It will help that if you want to know what the program is doing.
|
83
225
|
|
84
|
-
|
85
|
-
The debuggee program (`target.rb`) is suspended at the beginning of `target.rb`.
|
226
|
+
### Use `rdbg` with commands written in Ruby
|
86
227
|
|
87
|
-
|
88
|
-
You can suspend the debuggee program and show the debug console with `Ctrl-C`.
|
228
|
+
If you want to run a command written in Ruby like like `rake`, `rails`, `bundle`, `rspec` and so on, you can use `rdbg -c` option.
|
89
229
|
|
90
|
-
|
230
|
+
* Without `-c` option, `rdbg <name>` means that `<name>` is Ruby script and invoke it like `ruby <name>` with the debugger.
|
231
|
+
* With `-c` option, `rdbg -c <name>` means that `<name>` is command in `PATH` and simply invoke it with the debugger.
|
91
232
|
|
92
|
-
|
93
|
-
|
233
|
+
Examples:
|
234
|
+
* `rdbg -c -- rails server`
|
235
|
+
* `rdbg -c -- bundle exec ruby foo.rb`
|
236
|
+
* `rdbg -c -- bundle exec rake test`
|
237
|
+
* `rdbg -c -- ruby target.rb` is same as `rdbg target.rb`
|
94
238
|
|
95
|
-
|
96
|
-
=> 1| a = 1
|
97
|
-
2| b = 2
|
98
|
-
3| c = 3
|
99
|
-
4| p [a + b + c]
|
100
|
-
5|
|
101
|
-
--> #0 /home/ko1/src/rb/target.rb:1:in `<main>'
|
239
|
+
NOTE: `--` is needed to separate the command line options for `rdbg` and invoking command. For example, `rdbg -c rake -T` is recognized like `rdbg -c -T -- rake`. It should be `rdbg -c -- rake -T`.
|
102
240
|
|
103
|
-
(
|
104
|
-
%self => main
|
105
|
-
a => nil
|
106
|
-
b => nil
|
107
|
-
c => nil
|
241
|
+
NOTE: If you want to use bundler (`bundle` command), you need to write `gem debug` line in your `Gemfile`.
|
108
242
|
|
109
|
-
|
110
|
-
=> nil
|
243
|
+
### Using VSCode
|
111
244
|
|
112
|
-
|
245
|
+
Like other languages, you can use this debugger on the VSCode.
|
113
246
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
--> #0 /home/ko1/src/rb/target.rb:2:in `<main>'
|
247
|
+
1. Install [VSCode rdbg Ruby Debugger - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg)
|
248
|
+
2. Open `.rb` file (e.g. `target.rb`)
|
249
|
+
3. Register breakpoints with "Toggle breakpoint" in Run menu (or type F9 key)
|
250
|
+
4. Choose "Start debugging" in "Run" menu (or type F5 key)
|
251
|
+
5. You will see a dialog "Debug command line" and you can choose your favorite command line your want to run.
|
252
|
+
6. Chosen command line is invoked with `rdbg -c` and VSCode shows the details at breakpoints.
|
121
253
|
|
122
|
-
|
254
|
+
Please refer [Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging) for operations on VSCode.
|
123
255
|
|
124
|
-
|
125
|
-
|
126
|
-
2| b = 2
|
127
|
-
=> 3| c = 3
|
128
|
-
4| p [a + b + c]
|
129
|
-
5|
|
130
|
-
--> #0 /home/ko1/src/rb/target.rb:3:in `<main>'
|
256
|
+
You can configure the extension in `.vscode/launch.json`.
|
257
|
+
Please see the extension page for more details.
|
131
258
|
|
132
|
-
|
259
|
+
## Remote debugging
|
133
260
|
|
134
|
-
|
135
|
-
1| a = 1
|
136
|
-
2| b = 2
|
137
|
-
3| c = 3
|
138
|
-
=> 4| p [a + b + c]
|
139
|
-
5|
|
140
|
-
--> #0 /home/ko1/src/rb/target.rb:4:in `<main>'
|
141
|
-
|
142
|
-
(rdbg) info # Show all local variables
|
143
|
-
%self => main
|
144
|
-
a => 1
|
145
|
-
b => 2
|
146
|
-
c => 3
|
147
|
-
|
148
|
-
(rdbg) c # Continue the program ("c" is a short name of "continue")
|
149
|
-
[6]
|
150
|
-
```
|
261
|
+
You can use this debugger as a remote debugger. For example, it will help the following situations:
|
151
262
|
|
152
|
-
|
263
|
+
* Your application does not run on TTY and it is hard to use `binding.pry` or `binding.irb`.
|
264
|
+
* Your application is running on Docker container and there is no TTY.
|
265
|
+
* Your application is running as a daemon.
|
266
|
+
* Your application uses pipe for STDIN or STDOUT.
|
267
|
+
* Your application is running as a daemon and you want to query the running status (checking a backtrace and so on).
|
153
268
|
|
154
|
-
|
269
|
+
You can run your application as a remote debuggee and the remote debugger console can attach to the debuggee anytime.
|
155
270
|
|
156
|
-
|
157
|
-
$ rdbg --open target.rb # or rdbg -O target.rb for shorthand
|
158
|
-
Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-5042)
|
159
|
-
```
|
271
|
+
### Invoke as a remote debuggee
|
160
272
|
|
161
|
-
|
273
|
+
There are two ways to invoke a script as remote debuggee: Use `rdbg --open` and require `debug/open` (or `debug/open_nonstop`).
|
162
274
|
|
163
|
-
|
164
|
-
|
165
|
-
|
275
|
+
#### `rdbg --open` (or `rdbg -O` for short)
|
276
|
+
|
277
|
+
You can run a script with `rdbg --open target.rb` command and run a `target.rb` as a debuggee program. It also opens the network port and suspends at the beginning of `target.rb`.
|
278
|
+
|
279
|
+
```shell
|
280
|
+
$ exe/rdbg --open target.rb
|
281
|
+
DEBUGGER: Session start (pid: 7773)
|
282
|
+
DEBUGGER: Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773)
|
283
|
+
DEBUGGER: wait for debugger connection...
|
166
284
|
```
|
167
285
|
|
168
|
-
|
286
|
+
By default, `rdbg --open` uses UNIX domain socket and generates path name automatically (`/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773` in this case).
|
169
287
|
|
170
|
-
|
171
|
-
# target.rb
|
172
|
-
require 'debug/open' # open the debugger entry point by UNIX domain socket.
|
288
|
+
You can connect to the debuggee with `rdbg --attach` command (`rdbg -A` for short).
|
173
289
|
|
174
|
-
|
290
|
+
```shell
|
291
|
+
$ rdbg -A
|
292
|
+
[1, 7] in target.rb
|
293
|
+
=> 1| a = 1
|
294
|
+
2| b = 2
|
295
|
+
3| c = 3
|
296
|
+
4| d = 4
|
297
|
+
5| p [a, b, c, d]
|
298
|
+
6|
|
299
|
+
7| __END__
|
300
|
+
=>#0 <main> at target.rb:1
|
175
301
|
|
176
|
-
|
177
|
-
DEBUGGER__.open # open the debugger entry point by UNIX domain socket.
|
178
|
-
# or DEBUGGER__.open_unix to specify UNIX domain socket.
|
302
|
+
(rdbg:remote)
|
179
303
|
```
|
180
304
|
|
181
|
-
|
182
|
-
$ ruby target.rb
|
183
|
-
Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-5042)
|
184
|
-
```
|
305
|
+
If there is no other opening ports on the default directory, `rdbg --attach` command chooses the only one opening UNIX domain socket and connect to it. If there are more files, you need to specify the file.
|
185
306
|
|
186
|
-
|
187
|
-
The debuggee process waits for debugger connection at the beginning of `target.rb` like that:
|
307
|
+
When `rdbg --attach` connects to the debuggee, you can use any debug commands (set breakpoints, continue the program and so on) like local debug console. When an debuggee program exits, the remote console will also terminate.
|
188
308
|
|
189
|
-
|
190
|
-
$ rdbg -O ~/src/rb/target.rb
|
191
|
-
DEBUGGER: Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-29828)
|
192
|
-
DEBUGGER: wait for debugger connection...
|
193
|
-
```
|
309
|
+
NOTE: If you use `quit` command, only remote console exits and the debuggee program continues to run (and you can connect it again). If you want to exit the debuggee program, use `kill` command.
|
194
310
|
|
195
|
-
|
311
|
+
If you want to use TCP/IP for the remote debugging, you need to specify the port and host with `--port` like `rdbg --open --port 12345` and it binds to `localhost:12345`.
|
196
312
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
1| (1..).each do |i|
|
202
|
-
=> 2| sleep 0.5
|
203
|
-
3| p i
|
204
|
-
4| end
|
205
|
-
--> #0 [C] /home/ko1/src/rb/target.rb:2:in `sleep'
|
206
|
-
#1 /home/ko1/src/rb/target.rb:2:in `block in <main>' {|i=17|}
|
207
|
-
#2 [C] /home/ko1/src/rb/target.rb:1:in `each'
|
208
|
-
# and 1 frames (use `bt' command for all frames)
|
209
|
-
|
210
|
-
(rdb)
|
313
|
+
To connect to the debuggee, you need to specify the port.
|
314
|
+
|
315
|
+
```shell
|
316
|
+
$ rdbg --attach 12345
|
211
317
|
```
|
212
318
|
|
213
|
-
|
319
|
+
If you want to choose the host to bind, you can use `--host` option.
|
320
|
+
Note that all messages communicated between the debugger and the debuggee are *NOT* encrypted so please use remote debugging carefully.
|
214
321
|
|
215
|
-
|
216
|
-
You can re-connect to the debuggee process by `rdbg -A` command again, and the debuggee process suspends the execution (and debugger can input any debug commands).
|
322
|
+
#### `require 'debug/open'` in a program
|
217
323
|
|
218
|
-
If you
|
324
|
+
If you can modify the program, you can open debugging port by adding `require 'debug/open'` line in the program.
|
219
325
|
|
220
|
-
|
221
|
-
|
326
|
+
If you don't want to stop the program at the beginning, you can also use `require 'debug/open_nonstop'`.
|
327
|
+
Using `debug/open_nonstop` is useful if you want to open a backdoor to the application.
|
328
|
+
However, it is also danger because it can become another vulnerability.
|
329
|
+
Please use it carefully.
|
222
330
|
|
223
|
-
|
331
|
+
By default, UNIX domain socket is used for the debugging port. To use TCP/IP, you can set the `RUBY_DEBUG_PORT` environment variable.
|
224
332
|
|
333
|
+
```shell
|
334
|
+
$ RUBY_DEBUG_PORT=12345 ruby target.rb
|
225
335
|
```
|
226
|
-
$ rdbg --attach
|
227
|
-
Please select a debug session:
|
228
|
-
ruby-debug-ko1-19638
|
229
|
-
ruby-debug-ko1-19603
|
230
|
-
```
|
231
336
|
|
232
|
-
|
337
|
+
## Configuration
|
338
|
+
|
339
|
+
You can configure the debugger's behavior with debug commands and environment variables.
|
340
|
+
When the debug session is started, initial scripts are loaded so you can put your favorite configurations in the initial scripts.
|
341
|
+
|
342
|
+
### Configuration list
|
343
|
+
|
344
|
+
You can configure debugger's behavior with environment variables and `config` command. Each configuration has environment variable and the name which can be specified by `config` command.
|
233
345
|
|
234
346
|
```
|
235
|
-
|
347
|
+
# configuration example
|
348
|
+
config set log_level INFO
|
349
|
+
config set no_color true
|
236
350
|
```
|
237
351
|
|
238
|
-
|
239
|
-
|
240
|
-
*
|
241
|
-
*
|
352
|
+
<% cat = nil; DEBUGGER__::CONFIG_SET.each do |key, (env, desc)| %>
|
353
|
+
<% /\A(\w+): (.+)/ =~ desc; if cat != $1; cat = 1 %>
|
354
|
+
* <%= $1 %>
|
355
|
+
<% cat = $1; end %> * `<%= env %>` (`<%= key %>`): <%= $2 %><% end %>
|
242
356
|
|
243
|
-
###
|
357
|
+
### Initial scripts
|
244
358
|
|
245
|
-
|
359
|
+
If there is `~/.rdbgrc`, the file is loaded as an initial script (which contains debug commands) when the debug session is started.
|
246
360
|
|
247
|
-
|
361
|
+
* `RUBY_DEBUG_INIT_SCRIPT` environment variable can specify the initial script file.
|
362
|
+
* You can specify the initial script with `rdbg -x initial_script` (like gdb's `-x` option).
|
248
363
|
|
249
|
-
|
250
|
-
|
251
|
-
# or
|
252
|
-
$ rdbg --open --port=12345 target.rb
|
253
|
-
Debugger can attach via TCP/IP (localhost:12345)
|
254
|
-
```
|
364
|
+
Initial scripts are useful to write your favorite configurations.
|
365
|
+
For example, you can set break points with `break file:123` in `~/.rdbgrc`.
|
255
366
|
|
256
|
-
|
367
|
+
If there are `~/.rdbgrc.rb` is available, it is also loaded as a ruby script at same timing.
|
257
368
|
|
369
|
+
## Debug command on the debug console
|
258
370
|
|
259
|
-
|
260
|
-
$ RUBY_DEBUG_PORT=12345 ruby -r debug/open target.rb
|
261
|
-
Debugger can attach via TCP/IP (localhost:12345)
|
262
|
-
```
|
371
|
+
On the debug console, you can use the following debug commands.
|
263
372
|
|
264
|
-
|
373
|
+
* `Enter` repeats the last command (useful when repeating `step`s).
|
374
|
+
* `Ctrl-D` is equal to `quit` command.
|
375
|
+
* [debug command compare sheet - Google Sheets](https://docs.google.com/spreadsheets/d/1TlmmUDsvwK4sSIyoMv-io52BUUz__R5wpu-ComXlsw0/edit?usp=sharing)
|
265
376
|
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
```
|
377
|
+
You can use the following debug commands. Each command should be written in 1 line.
|
378
|
+
The `[...]` notation means this part can be eliminate. For example, `s[tep]` means `s` or `step` are valid command. `ste` is not valid.
|
379
|
+
The `<...>` notation means the argument.
|
270
380
|
|
271
|
-
|
381
|
+
<%= DEBUGGER__.help %>
|
272
382
|
|
273
|
-
|
274
|
-
$ RUBY_DEBUG_PORT=12345 ruby target.rb
|
275
|
-
Debugger can attach via TCP/IP (localhost:12345)
|
276
|
-
```
|
383
|
+
## Debugger API
|
277
384
|
|
278
|
-
|
385
|
+
### Start debugging
|
279
386
|
|
280
|
-
|
281
|
-
# target.rb
|
282
|
-
require 'debug/server' # introduce remote debugging feature
|
283
|
-
DEBUGGER__.open(port: 12345)
|
284
|
-
# or DEBUGGER__.open_tcp(port: 12345)
|
285
|
-
```
|
387
|
+
#### Start by requiring a library
|
286
388
|
|
287
|
-
|
288
|
-
$ ruby target.rb
|
289
|
-
Debugger can attach via TCP/IP (localhost:12345)
|
290
|
-
```
|
389
|
+
You can start debugging without `rdbg` command by requiring the following libraries:
|
291
390
|
|
292
|
-
|
391
|
+
* `require 'debug'`: Same as `rdbg --nonstop --no-sigint-hook`.
|
392
|
+
* `require 'debug/start'`: Same as `rdbg`.
|
393
|
+
* `require 'debug/open'`: Same as `rdbg --open`.
|
394
|
+
* `require 'debug/open_nonstop'`: Same as `rdbg --open --nonstop`.
|
293
395
|
|
294
|
-
|
396
|
+
You need to require one of them at the very beginning of the application.
|
397
|
+
Using `ruby -r` (for example `ruby -r debug/start target.rb`) is another way to invoke with debugger.
|
295
398
|
|
296
|
-
|
297
|
-
|
298
|
-
|
399
|
+
NOTE: Until Ruby 3.0, there is old `lib/debug.rb` standard library. So that if this gem is not installed, or if `Gemfile` missed to list this gem and `bundle exec` is used, you will see the following output:
|
400
|
+
|
401
|
+
```shell
|
402
|
+
$ ruby -r debug -e0
|
403
|
+
.../2.7.3/lib/ruby/2.7.0/x86_64-linux/continuation.so: warning: callcc is obsolete; use Fiber instead
|
404
|
+
Debug.rb
|
405
|
+
Emacs support available.
|
406
|
+
|
407
|
+
.../2.7.3/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb:162: if RUBYGEMS_ACTIVATION_MONITOR.respond_to?(:mon_owned?)
|
408
|
+
(rdb:1)
|
299
409
|
```
|
300
410
|
|
301
|
-
|
411
|
+
`lib/debug.rb` was not maintained well in recent years, and the purpose of this library is to rewrite old `lib/debug.rb` with recent techniques.
|
302
412
|
|
303
|
-
|
413
|
+
#### Start by method
|
304
414
|
|
305
|
-
|
415
|
+
After loading `debug/session`, you can start debug session with the following methods. They are convenient if you want to specify debug configurations in your program.
|
306
416
|
|
307
|
-
|
417
|
+
* `DEBUGGER__.start(**kw)`: start debug session with local console.
|
418
|
+
* `DEBUGGER__.open(**kw)`: open debug port with configuration (without configurations open with UNIX domain socket)
|
419
|
+
* `DEBUGGER__.open_unix(**kw)`: open debug port with UNIX domain socket
|
420
|
+
* `DEBUGGER__.open_tcp(**kw)`: open debug port with TCP/IP
|
308
421
|
|
309
|
-
|
422
|
+
For example:
|
310
423
|
|
311
|
-
|
424
|
+
```ruby
|
425
|
+
require 'debug/session'
|
426
|
+
DEBUGGER__.start(no_color: true, # disable colorize
|
427
|
+
log_level: 'INFO') # Change log_level to INFO
|
312
428
|
|
313
|
-
|
314
|
-
|
315
|
-
* `RUBY_DEBUG_COMMANDS`: Debug commands invoked at the first stop. Commands should be separated by ';;'.
|
316
|
-
* `RUBY_DEBUG_SHOW_SRC_LINES`: Show n lines source code on breakpoint (default: 10 lines).
|
317
|
-
* `RUBY_DEBUG_SHOW_FRAMES`: Show n frames on breakpoint (default: 2 frames).
|
318
|
-
* Remote debugging
|
319
|
-
* `RUBY_DEBUG_PORT`: TCP/IP remote debugging: port to open.
|
320
|
-
* `RUBY_DEBUG_HOST`: TCP/IP remote debugging: host (localhost if not given) to open.
|
321
|
-
* `RUBY_DEBUG_SOCK_PATH`: UNIX Domain Socket remote debugging: socket path to open.
|
322
|
-
* `RUBY_DEBUG_SOCK_DIR`: UNIX Domain Socket remote debugging: socket directory to open.
|
429
|
+
... # your application code
|
430
|
+
```
|
323
431
|
|
324
|
-
|
432
|
+
### `binding.break` method
|
325
433
|
|
326
|
-
|
327
|
-
* `Ctrl-D` is equal to `quit` command.
|
328
|
-
* [debug command compare sheet - Google Sheets](https://docs.google.com/spreadsheets/d/1TlmmUDsvwK4sSIyoMv-io52BUUz__R5wpu-ComXlsw0/edit?usp=sharing)
|
434
|
+
`binding.break` (or `binding.b`) set breakpoints at written line. It also has several keywords.
|
329
435
|
|
330
|
-
|
331
|
-
|
332
|
-
The `<...>` notation means the argument.
|
436
|
+
If `do: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command and continue the program.
|
437
|
+
It is useful if you only want to call a debug command and don't want to stop there.
|
333
438
|
|
334
|
-
|
439
|
+
```
|
440
|
+
def initialize
|
441
|
+
@a = 1
|
442
|
+
binding.b do: 'watch @a'
|
443
|
+
end
|
444
|
+
```
|
445
|
+
|
446
|
+
On this case, register a watch breakpoint for `@a` and continue to run.
|
447
|
+
|
448
|
+
If `pre: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command, and keep suspend.
|
449
|
+
It is useful if you have operations before suspend.
|
450
|
+
|
451
|
+
```
|
452
|
+
def foo
|
453
|
+
binding.b pre: 'p bar()'
|
454
|
+
...
|
455
|
+
end
|
456
|
+
```
|
457
|
+
|
458
|
+
On this case, you can see the result of `bar()` every time you stop there.
|
335
459
|
|
336
460
|
## rdbg command help
|
337
461
|
|
@@ -342,6 +466,7 @@ The `<...>` notation means the argument.
|
|
342
466
|
# Contributing
|
343
467
|
|
344
468
|
Bug reports and pull requests are welcome on GitHub at https://github.com/ruby/debug.
|
469
|
+
This debugger is not mature so your feedback will help us.
|
345
470
|
|
346
471
|
Please also check the [contributing guideline](/CONTRIBUTING.md).
|
347
472
|
|