debug 1.0.0.beta8 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 if 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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DEBUGGER__
4
- VERSION = "1.0.0.beta8"
4
+ VERSION = "1.1.0"
5
5
  end
data/misc/README.md.erb CHANGED
@@ -8,7 +8,7 @@ This debug.rb is replacement of traditional lib/debug.rb standard library which
8
8
  New debug.rb has several advantages:
9
9
 
10
10
  * Fast: No performance penalty on non-stepping mode and non-breakpoints.
11
- * Remote debugging: Support remote debugging natively.
11
+ * [Remote debugging](#remote-debugging): Support remote debugging natively.
12
12
  * UNIX domain socket
13
13
  * TCP/IP
14
14
  * VSCode/DAP integration ([VSCode rdbg Ruby Debugger - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg))
@@ -20,11 +20,12 @@ 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
 
26
27
  ```
27
- $ gem install debug --pre
28
+ $ gem install debug
28
29
  ```
29
30
 
30
31
  or specify `-Ipath/to/debug/lib` in `RUBYOPT` or each ruby command-line option, especially for debug this gem development.
@@ -32,7 +33,7 @@ or specify `-Ipath/to/debug/lib` in `RUBYOPT` or each ruby command-line option,
32
33
  If you use Bundler, write the following line to your Gemfile.
33
34
 
34
35
  ```
35
- gem "debug", ">= 1.0.0.beta"
36
+ gem "debug", ">= 1.0.0"
36
37
  ```
37
38
 
38
39
  # HOW TO USE
@@ -43,25 +44,27 @@ To use a debugger, roughly you will do the following steps:
43
44
  2. Run a program with the debugger.
44
45
  3. At the breakpoint, enter the debugger console.
45
46
  4. Use debug commands.
46
- * Query the prgram status (e.g. `p lvar` to see the local variable `lvar`).
47
- * Control program flow (e.g. move to the another line with `step`, to the next line with `next`).
48
- * Set another breakpoints (e.g. `catch Exception` to set the breakpoints when `Exception` is raiesd).
49
- * Change the configuration (e.g. `config set no_color true` to disable coloring).
47
+ * [Evaluate Ruby expressions](#evaluate) (e.g. `p lvar` to see the local variable `lvar`).
48
+ * [Query the program status](#information) (e.g. `info` to see information about the current frame).
49
+ * [Control program flow](#control-flow) (e.g. move to the another line with `step`, to the next line with `next`).
50
+ * [Set another breakpoint](#breakpoint) (e.g. `catch Exception` to set a breakpoint that'll be triggered when `Exception` is raised).
51
+ * [Activate tracing in your program](#trace) (e.g. `trace call` to trace method calls).
52
+ * [Change the configuration](#configuration-1) (e.g. `config set no_color true` to disable coloring).
50
53
  * Continue the program (`c` or `continue`) and goto 3.
51
54
 
52
55
  ## Invoke with the debugger
53
56
 
54
57
  There are several options for (1) and (2). Please choose your favorite way.
55
58
 
56
- ### Modify source code as `binding.pry` and `binding.irb`
59
+ ### Modify source code with [`binding.break`](#bindingbreak-method) (similar to `binding.pry` or `binding.irb`)
57
60
 
58
- 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`.
59
- After that, you run the program as usuall and you will enter the debug console at breakpoints you inserted.
61
+ 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`](#bindingbreak-method) method (`binding.b` for short) into lines where you want to stop as breakpoints like `binding.pry` and `binding.irb`.
62
+ After that, you run the program as usual and you will enter the debug console at breakpoints you inserted.
60
63
 
61
- The following example shows the demonstration of `binding.break`.
64
+ The following example shows the demonstration of [`binding.break`](#bindingbreak-method).
62
65
 
63
66
  ```shell
64
- $ cat target.rb # Sample prgram
67
+ $ cat target.rb # Sample program
65
68
  require 'debug'
66
69
 
67
70
  a = 1
@@ -120,13 +123,13 @@ d => 4
120
123
  [1, 2, 3, 4]
121
124
  ```
122
125
 
123
- ### Invoke the prorgam from the debugger as a traditional debuggers
126
+ ### Invoke the program from the debugger as a traditional debuggers
124
127
 
125
128
  If you don't want to modify the source code, you can set breakpoints with a debug command `break` (`b` for short).
126
129
  Using `rdbg` command to launch the program without any modifications, you can run the program with the debugger.
127
130
 
128
131
  ```shell
129
- $ cat target.rb # Sample prgram
132
+ $ cat target.rb # Sample program
130
133
  a = 1
131
134
  b = 2
132
135
  c = 3
@@ -241,16 +244,16 @@ NOTE: If you want to use bundler (`bundle` command), you need to write `gem debu
241
244
 
242
245
  ### Using VSCode
243
246
 
244
- Like other langauges, you can use this debugger on the VSCode.
247
+ Like other languages, you can use this debugger on the VSCode.
245
248
 
246
- 1. Install [VSCode rdbg Ruby Debugger - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg)
249
+ 1. Install [VSCode rdbg Ruby Debugger - Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=KoichiSasada.vscode-rdbg)
247
250
  2. Open `.rb` file (e.g. `target.rb`)
248
251
  3. Register breakpoints with "Toggle breakpoint" in Run menu (or type F9 key)
249
252
  4. Choose "Start debugging" in "Run" menu (or type F5 key)
250
253
  5. You will see a dialog "Debug command line" and you can choose your favorite command line your want to run.
251
- 6. Chosed command line is invoked with `rdbg -c` and VSCode shows the details at breakponts.
254
+ 6. Chosen command line is invoked with `rdbg -c` and VSCode shows the details at breakpoints.
252
255
 
253
- Plase refer [Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging) for operations on VSCode.
256
+ Please refer [Debugging in Visual Studio Code](https://code.visualstudio.com/docs/editor/debugging) for operations on VSCode.
254
257
 
255
258
  You can configure the extension in `.vscode/launch.json`.
256
259
  Please see the extension page for more details.
@@ -265,7 +268,7 @@ You can use this debugger as a remote debugger. For example, it will help the fo
265
268
  * Your application uses pipe for STDIN or STDOUT.
266
269
  * Your application is running as a daemon and you want to query the running status (checking a backtrace and so on).
267
270
 
268
- You can run your application as a remote debuggee and the remote debugger console can attach to the debugee anytime.
271
+ You can run your application as a remote debuggee and the remote debugger console can attach to the debuggee anytime.
269
272
 
270
273
  ### Invoke as a remote debuggee
271
274
 
@@ -279,10 +282,10 @@ You can run a script with `rdbg --open target.rb` command and run a `target.rb`
279
282
  $ exe/rdbg --open target.rb
280
283
  DEBUGGER: Session start (pid: 7773)
281
284
  DEBUGGER: Debugger can attach via UNIX domain socket (/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773)
282
- DEBUGGER: wait for debuger connection...
285
+ DEBUGGER: wait for debugger connection...
283
286
  ```
284
287
 
285
- By deafult, `rdbg --open` uses UNIX domain socket and generates path name automatically (`/home/ko1/.ruby-debug-sock/ruby-debug-ko1-7773` in this case).
288
+ 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).
286
289
 
287
290
  You can connect to the debuggee with `rdbg --attach` command (`rdbg -A` for short).
288
291
 
@@ -309,7 +312,7 @@ NOTE: If you use `quit` command, only remote console exits and the debuggee prog
309
312
 
310
313
  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`.
311
314
 
312
- To connect to the debugeee, you need to specify the port.
315
+ To connect to the debuggee, you need to specify the port.
313
316
 
314
317
  ```shell
315
318
  $ rdbg --attach 12345
@@ -324,7 +327,7 @@ If you can modify the program, you can open debugging port by adding `require 'd
324
327
 
325
328
  If you don't want to stop the program at the beginning, you can also use `require 'debug/open_nonstop'`.
326
329
  Using `debug/open_nonstop` is useful if you want to open a backdoor to the application.
327
- However, it is also danger because it can become antoher vulnerability.
330
+ However, it is also danger because it can become another vulnerability.
328
331
  Please use it carefully.
329
332
 
330
333
  By default, UNIX domain socket is used for the debugging port. To use TCP/IP, you can set the `RUBY_DEBUG_PORT` environment variable.
@@ -336,14 +339,14 @@ $ RUBY_DEBUG_PORT=12345 ruby target.rb
336
339
  ## Configuration
337
340
 
338
341
  You can configure the debugger's behavior with debug commands and environment variables.
339
- When the debug session is started, initial scripts are loaded so you can put your favorite configurations in the intial scripts.
342
+ When the debug session is started, initial scripts are loaded so you can put your favorite configurations in the initial scripts.
340
343
 
341
344
  ### Configuration list
342
345
 
343
346
  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.
344
347
 
345
348
  ```
346
- # configulation example
349
+ # configuration example
347
350
  config set log_level INFO
348
351
  config set no_color true
349
352
  ```
@@ -355,7 +358,7 @@ config set no_color true
355
358
 
356
359
  ### Initial scripts
357
360
 
358
- If there is `~/.rdbgrc`, the file is loaded as an initial scripts which contains debug commands) when the debug session is started.
361
+ If there is `~/.rdbgrc`, the file is loaded as an initial script (which contains debug commands) when the debug session is started.
359
362
 
360
363
  * `RUBY_DEBUG_INIT_SCRIPT` environment variable can specify the initial script file.
361
364
  * You can specify the initial script with `rdbg -x initial_script` (like gdb's `-x` option).
@@ -369,7 +372,12 @@ If there are `~/.rdbgrc.rb` is available, it is also loaded as a ruby script at
369
372
 
370
373
  On the debug console, you can use the following debug commands.
371
374
 
372
- * `Enter` repeats the last command (useful when repeating `step`s).
375
+ There are additional features:
376
+
377
+ * `<expr>` without debug command is almost same as `pp <expr>`.
378
+ * If the input line `<expr>` does *NOT* start with any debug command, the line `<expr>` will be evaluated as a Ruby expression and the result will be printed with `pp` method. So that the input `foo.bar` is same as `pp foo.bar`.
379
+ * If `<expr>` is recognized as a debug command, of course it is not evaluated as a Ruby expression, but is executed as debug command. For example, you can not evaluate such single letter local variables `i`, `b`, `n`, `c` because they are single letter debug commands. Use `p i` instead.
380
+ * `Enter` without any input repeats the last command (useful when repeating `step`s).
373
381
  * `Ctrl-D` is equal to `quit` command.
374
382
  * [debug command compare sheet - Google Sheets](https://docs.google.com/spreadsheets/d/1TlmmUDsvwK4sSIyoMv-io52BUUz__R5wpu-ComXlsw0/edit?usp=sharing)
375
383
 
@@ -395,7 +403,7 @@ You can start debugging without `rdbg` command by requiring the following librar
395
403
  You need to require one of them at the very beginning of the application.
396
404
  Using `ruby -r` (for example `ruby -r debug/start target.rb`) is another way to invoke with debugger.
397
405
 
398
- 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 `bunde exec` is used, you will see the following output:
406
+ 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:
399
407
 
400
408
  ```shell
401
409
  $ ruby -r debug -e0
@@ -411,7 +419,7 @@ Emacs support available.
411
419
 
412
420
  #### Start by method
413
421
 
414
- After loading `debug/session`, you can start debug session with the following methods. They are convinient if you want to specifies debug configrations in your program.
422
+ 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.
415
423
 
416
424
  * `DEBUGGER__.start(**kw)`: start debug session with local console.
417
425
  * `DEBUGGER__.open(**kw)`: open debug port with configuration (without configurations open with UNIX domain socket)
@@ -436,15 +444,15 @@ If `do: 'command'` is specified, the debugger suspends the program and run the `
436
444
  It is useful if you only want to call a debug command and don't want to stop there.
437
445
 
438
446
  ```
439
- def initialzie
447
+ def initialize
440
448
  @a = 1
441
449
  binding.b do: 'watch @a'
442
450
  end
443
451
  ```
444
452
 
445
- On this case, register a watch breakpont for `@a` and continue to run.
453
+ On this case, register a watch breakpoint for `@a` and continue to run.
446
454
 
447
- If `pre: 'command'` is specified, the debuger suspends the program and run the `command` as a debug command, and keep suspend.
455
+ If `pre: 'command'` is specified, the debugger suspends the program and run the `command` as a debug command, and keep suspend.
448
456
  It is useful if you have operations before suspend.
449
457
 
450
458
  ```
@@ -454,7 +462,7 @@ def foo
454
462
  end
455
463
  ```
456
464
 
457
- On this case, you can see the result of `bar()` everytime when you stops there.
465
+ On this case, you can see the result of `bar()` every time you stop there.
458
466
 
459
467
  ## rdbg command help
460
468