tracer 0.2.1 → 0.2.3
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 +4 -4
- data/README.md +93 -47
- data/lib/tracer/base.rb +18 -8
- data/lib/tracer/exception_tracer.rb +24 -8
- data/lib/tracer/irb.rb +9 -1
- data/lib/tracer/ivar_tracer.rb +49 -0
- data/lib/tracer/version.rb +1 -1
- data/lib/tracer.rb +1 -0
- metadata +4 -7
- data/Gemfile +0 -13
- data/Gemfile.lock +0 -41
- data/Rakefile +0 -12
- data/tracer.gemspec +0 -43
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2384a62a824cb7906cd027700d16844a6b666e5dc7b04749fcb2d69ece9a2a85
|
4
|
+
data.tar.gz: 762568e03d81d9dafa2ad6735f9901d53507aaeaa350aac53ef3989325136757
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: bbdccfb4cf5054d554a76e22f9bbffd21aa8f3e9a13e366cbfb41d076803c4de4b019697b81d8dd1278655390f7c1949190e7d7c72b2fcdc7ffa83622b89a71d
|
7
|
+
data.tar.gz: b0a8142e250b24964d9fbcbc674d052107061725b6673b7d573553faf55afc722559396c0b878f3639c051dcfd1a9794e285116ba38e2f493d4be9b8d17c11ae
|
data/README.md
CHANGED
@@ -1,6 +1,19 @@
|
|
1
1
|
# Tracer
|
2
2
|
|
3
|
-
|
3
|
+
[](https://github.com/ruby/tracer/actions/workflows/main.yml)
|
4
|
+
[](https://badge.fury.io/rb/tracer)
|
5
|
+
|
6
|
+
The `tracer` gem provides helpful tracing utilities to help users observe their program's runtime behaviour.
|
7
|
+
|
8
|
+
The currently supported tracers are:
|
9
|
+
|
10
|
+
- [`ObjectTracer`](#objecttracer)
|
11
|
+
- [`IvarTracer`](#ivartracer)
|
12
|
+
- [`CallTracer`](#calltracer)
|
13
|
+
- [`ExceptionTracer`](#exceptiontracer)
|
14
|
+
- [`LineTracer`](#linetracer)
|
15
|
+
|
16
|
+
It also comes with experimental [IRB integration](#irb-integration) to allow quick access from REPL.
|
4
17
|
|
5
18
|
## Installation
|
6
19
|
|
@@ -8,6 +21,14 @@ Tracer is an extraction of [`ruby/debug`](https://github.com/ruby/debug)'s [powe
|
|
8
21
|
$ bundle add tracer --group=development,test
|
9
22
|
```
|
10
23
|
|
24
|
+
Or directly add it to your `Gemfile`
|
25
|
+
|
26
|
+
```rb
|
27
|
+
group :development, :test do
|
28
|
+
gem "tracer"
|
29
|
+
end
|
30
|
+
```
|
31
|
+
|
11
32
|
If bundler is not being used to manage dependencies, install the gem by executing:
|
12
33
|
|
13
34
|
```shell
|
@@ -54,49 +75,6 @@ trace_call { ... } # trace method calls in the given block
|
|
54
75
|
trace_exception { ... } # trace exceptions in the given block
|
55
76
|
```
|
56
77
|
|
57
|
-
### IRB-integration
|
58
|
-
|
59
|
-
Once required, `tracer` registers a few IRB commands to help you trace Ruby expressions:
|
60
|
-
|
61
|
-
```
|
62
|
-
trace Trace the target object (or self) in the given expression. Usage: `trace [target,] <expression>`
|
63
|
-
trace_call Trace method calls in the given expression. Usage: `trace_call <expression>`
|
64
|
-
trace_exception Trace exceptions in the given expression. Usage: `trace_exception <expression>`
|
65
|
-
```
|
66
|
-
|
67
|
-
**Example**
|
68
|
-
|
69
|
-
```rb
|
70
|
-
# test.rb
|
71
|
-
require "tracer"
|
72
|
-
|
73
|
-
obj = Object.new
|
74
|
-
|
75
|
-
def obj.foo
|
76
|
-
100
|
77
|
-
end
|
78
|
-
|
79
|
-
def bar(obj)
|
80
|
-
obj.foo
|
81
|
-
end
|
82
|
-
|
83
|
-
binding.irb
|
84
|
-
```
|
85
|
-
|
86
|
-
|
87
|
-
```
|
88
|
-
irb(main):001:0> trace obj, bar(obj)
|
89
|
-
#depth:23 #<Object:0x0000000107a86648> is used as a parameter obj of Object#bar at (eval):1:in `<main>'
|
90
|
-
#depth:24 #<Object:0x0000000107a86648> receives .foo at test.rb:10:in `bar'
|
91
|
-
=> 100
|
92
|
-
irb(main):002:0> trace_call bar(obj)
|
93
|
-
#depth:23> Object#bar at (eval):1:in `<main>'
|
94
|
-
#depth:24> #<Object:0x0000000107a86648>.foo at test.rb:10:in `bar'
|
95
|
-
#depth:24< #<Object:0x0000000107a86648>.foo #=> 100 at test.rb:10:in `bar'
|
96
|
-
#depth:23< Object#bar #=> 100 at (eval):1:in `<main>'
|
97
|
-
=> 100
|
98
|
-
```
|
99
|
-
|
100
78
|
### Tracer Classes
|
101
79
|
|
102
80
|
If you want to have more control over individual traces, you can use individual tracer classes:
|
@@ -126,6 +104,30 @@ end
|
|
126
104
|
#depth:4 #<User:0x000000010696cad8 @name="John"> receives #name (User#name) at test.rb:8:in `authorized?'
|
127
105
|
```
|
128
106
|
|
107
|
+
#### IvarTracer
|
108
|
+
|
109
|
+
> **Note**
|
110
|
+
> Ruby 3.0 and below's accessor calls don't trigger TracePoint properly so the result may be inaccurate with those versions.
|
111
|
+
|
112
|
+
```rb
|
113
|
+
require "tracer"
|
114
|
+
|
115
|
+
class Cat
|
116
|
+
attr_accessor :name
|
117
|
+
end
|
118
|
+
|
119
|
+
cat = Cat.new
|
120
|
+
|
121
|
+
tracer = IvarTracer.new(cat, :@name)
|
122
|
+
tracer.start do
|
123
|
+
cat.name = "Kitty"
|
124
|
+
cat.instance_variable_set(:@name, "Ketty")
|
125
|
+
end
|
126
|
+
|
127
|
+
#depth:3 Cat#name= sets @name = "Kitty" at test.rb:11
|
128
|
+
#depth:3 Kernel#instance_variable_set sets @name = "Ketty" at test.rb:12
|
129
|
+
```
|
130
|
+
|
129
131
|
#### ExceptionTracer
|
130
132
|
|
131
133
|
```rb
|
@@ -137,7 +139,8 @@ rescue StandardError
|
|
137
139
|
nil
|
138
140
|
end
|
139
141
|
|
140
|
-
|
142
|
+
#depth:0 #<RuntimeError: boom> raised at test.rb:4
|
143
|
+
#depth:1 #<RuntimeError: boom> rescued at test.rb:6
|
141
144
|
```
|
142
145
|
|
143
146
|
#### CallTracer
|
@@ -197,13 +200,56 @@ end
|
|
197
200
|
#depth:5 at test.rb:8
|
198
201
|
```
|
199
202
|
|
203
|
+
### IRB-integration
|
204
|
+
|
205
|
+
Once required, `tracer` registers a few IRB commands to help you trace Ruby expressions:
|
206
|
+
|
207
|
+
```
|
208
|
+
trace Trace the target object (or self) in the given expression. Usage: `trace [target,] <expression>`
|
209
|
+
trace_call Trace method calls in the given expression. Usage: `trace_call <expression>`
|
210
|
+
trace_exception Trace exceptions in the given expression. Usage: `trace_exception <expression>`
|
211
|
+
```
|
212
|
+
|
213
|
+
**Example**
|
214
|
+
|
215
|
+
```rb
|
216
|
+
# test.rb
|
217
|
+
require "tracer"
|
218
|
+
|
219
|
+
obj = Object.new
|
220
|
+
|
221
|
+
def obj.foo
|
222
|
+
100
|
223
|
+
end
|
224
|
+
|
225
|
+
def bar(obj)
|
226
|
+
obj.foo
|
227
|
+
end
|
228
|
+
|
229
|
+
binding.irb
|
230
|
+
```
|
231
|
+
|
232
|
+
```shell
|
233
|
+
irb(main):001:0> trace obj, bar(obj)
|
234
|
+
#depth:23 #<Object:0x0000000107a86648> is used as a parameter obj of Object#bar at (eval):1:in `<main>'
|
235
|
+
#depth:24 #<Object:0x0000000107a86648> receives .foo at test.rb:10:in `bar'
|
236
|
+
=> 100
|
237
|
+
irb(main):002:0> trace_call bar(obj)
|
238
|
+
#depth:23> Object#bar at (eval):1:in `<main>'
|
239
|
+
#depth:24> #<Object:0x0000000107a86648>.foo at test.rb:10:in `bar'
|
240
|
+
#depth:24< #<Object:0x0000000107a86648>.foo #=> 100 at test.rb:10:in `bar'
|
241
|
+
#depth:23< Object#bar #=> 100 at (eval):1:in `<main>'
|
242
|
+
=> 100
|
243
|
+
```
|
244
|
+
|
200
245
|
## Customization
|
201
246
|
|
202
247
|
TBD
|
203
248
|
|
204
|
-
##
|
249
|
+
## Acknowledgements
|
205
250
|
|
206
|
-
[@ko1](https://github.com/ko1) (Koichi Sasada)
|
251
|
+
A big shout-out to [@ko1](https://github.com/ko1) (Koichi Sasada) for his awesome work on [`ruby/debug`](https://github.com/ruby/debug).
|
252
|
+
The [cool tracers in `ruby/debug`](https://github.com/ruby/debug/blob/master/lib/debug/tracer.rb) were an inspiration and laid the groundwork for this project.
|
207
253
|
|
208
254
|
## Development
|
209
255
|
|
data/lib/tracer/base.rb
CHANGED
@@ -10,6 +10,7 @@ module Tracer
|
|
10
10
|
M_INSPECT = Object.instance_method(:inspect)
|
11
11
|
M_CLASS = Object.instance_method(:class)
|
12
12
|
M_IS_A = Object.instance_method(:is_a?)
|
13
|
+
M_INSTANCE_VARIABLE_GET = Object.instance_method(:instance_variable_get)
|
13
14
|
HOME = ENV["HOME"] ? (ENV["HOME"] + "/") : nil
|
14
15
|
|
15
16
|
include Color
|
@@ -70,12 +71,21 @@ module Tracer
|
|
70
71
|
end
|
71
72
|
end
|
72
73
|
|
73
|
-
|
74
|
+
attr_reader :header
|
75
|
+
|
76
|
+
def initialize(
|
77
|
+
output: STDOUT,
|
78
|
+
pattern: nil,
|
79
|
+
colorize: nil,
|
80
|
+
depth_offset: 0,
|
81
|
+
header: nil
|
82
|
+
)
|
74
83
|
@name = self.class.name
|
75
84
|
@type = @name.sub(/Tracer\z/, "")
|
76
85
|
@output = output
|
77
86
|
@depth_offset = depth_offset
|
78
87
|
@colorize = colorize || colorizable?
|
88
|
+
@header = header
|
79
89
|
|
80
90
|
if pattern
|
81
91
|
@pattern = Regexp.compile(pattern)
|
@@ -90,10 +100,6 @@ module Tracer
|
|
90
100
|
[@type, @pattern, @into].freeze
|
91
101
|
end
|
92
102
|
|
93
|
-
def header
|
94
|
-
""
|
95
|
-
end
|
96
|
-
|
97
103
|
def to_s
|
98
104
|
s = "#{@name} #{description}"
|
99
105
|
s += " with pattern #{@pattern.inspect}" if @pattern
|
@@ -141,10 +147,14 @@ module Tracer
|
|
141
147
|
|
142
148
|
def out(tp, msg = nil, depth: caller.size - 1, location: nil)
|
143
149
|
location ||= "#{tp.path}:#{tp.lineno}"
|
144
|
-
|
145
|
-
|
150
|
+
if header
|
151
|
+
str = +"#{header} "
|
152
|
+
else
|
153
|
+
str = +""
|
154
|
+
end
|
155
|
+
str << "\#depth:#{"%-2d" % depth}#{msg} at #{colorize("#{location}", [:GREEN])}"
|
146
156
|
|
147
|
-
puts
|
157
|
+
puts str
|
148
158
|
end
|
149
159
|
|
150
160
|
def puts(msg)
|
@@ -4,16 +4,32 @@ require_relative "base"
|
|
4
4
|
|
5
5
|
class ExceptionTracer < Tracer::Base
|
6
6
|
def setup_tp
|
7
|
-
|
8
|
-
|
7
|
+
if RUBY_VERSION >= "3.3.0"
|
8
|
+
TracePoint.new(:raise, :rescue) do |tp|
|
9
|
+
next if skip?(tp)
|
9
10
|
|
10
|
-
|
11
|
+
exc = tp.raised_exception
|
11
12
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
13
|
+
action = tp.event == :raise ? "raised" : "rescued"
|
14
|
+
|
15
|
+
out tp,
|
16
|
+
" #{colorize_magenta(exc.inspect)} #{action}",
|
17
|
+
depth: caller.size - (1 + @depth_offset)
|
18
|
+
rescue Exception => e
|
19
|
+
p e
|
20
|
+
end
|
21
|
+
else
|
22
|
+
TracePoint.new(:raise) do |tp|
|
23
|
+
next if skip?(tp)
|
24
|
+
|
25
|
+
exc = tp.raised_exception
|
26
|
+
|
27
|
+
out tp,
|
28
|
+
" #{colorize_magenta(exc.inspect)} raised",
|
29
|
+
depth: caller.size - (1 + @depth_offset)
|
30
|
+
rescue Exception => e
|
31
|
+
p e
|
32
|
+
end
|
17
33
|
end
|
18
34
|
end
|
19
35
|
|
data/lib/tracer/irb.rb
CHANGED
@@ -1,7 +1,15 @@
|
|
1
|
-
require_relative "../tracer"
|
2
1
|
require "irb/cmd/nop"
|
3
2
|
require "irb"
|
4
3
|
|
4
|
+
if Gem::Version.new(IRB::VERSION) < Gem::Version.new("1.6.0")
|
5
|
+
warn <<~MSG
|
6
|
+
Your version of IRB is too old so Tracer cannot register its commands.
|
7
|
+
Please upgrade IRB by adding `gem "irb", "~> 1.6.0"` to your Gemfile.
|
8
|
+
MSG
|
9
|
+
|
10
|
+
return
|
11
|
+
end
|
12
|
+
|
5
13
|
module Tracer
|
6
14
|
def self.register_irb_commands
|
7
15
|
ec = IRB::ExtendCommandBundle.instance_variable_get(:@EXTEND_COMMANDS)
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require_relative "base"
|
4
|
+
|
5
|
+
class IvarTracer < Tracer::Base
|
6
|
+
def initialize(target, var_name, **kw)
|
7
|
+
@target = target
|
8
|
+
@var_name = var_name
|
9
|
+
@original_value = M_INSTANCE_VARIABLE_GET.bind_call(target, var_name)
|
10
|
+
super(**kw)
|
11
|
+
end
|
12
|
+
|
13
|
+
def key
|
14
|
+
[@type, @target, @var_name, @pattern, @into].freeze
|
15
|
+
end
|
16
|
+
|
17
|
+
def description
|
18
|
+
"for #{@var_name} of #{@target} #{super}"
|
19
|
+
end
|
20
|
+
|
21
|
+
def setup_tp
|
22
|
+
TracePoint.new(:a_return) do |tp|
|
23
|
+
next if skip?(tp)
|
24
|
+
|
25
|
+
if tp.self == @target &&
|
26
|
+
value = M_INSTANCE_VARIABLE_GET.bind_call(@target, @var_name)
|
27
|
+
if tp.event == :c_return
|
28
|
+
location = nil
|
29
|
+
else
|
30
|
+
location = caller_locations(2, 1).first.to_s
|
31
|
+
next if location.match?(DIR) || location.match?(/<internal:/)
|
32
|
+
end
|
33
|
+
|
34
|
+
depth = caller.size
|
35
|
+
call_identifier_str = (tp.defined_class ? minfo(tp) : "block")
|
36
|
+
call_identifier_str = colorize_blue(call_identifier_str)
|
37
|
+
depth += 1 if tp.event == :c_return
|
38
|
+
value = safe_inspect(value)
|
39
|
+
|
40
|
+
if value != @original_value
|
41
|
+
out tp,
|
42
|
+
"#{call_identifier_str} sets #{colorize_cyan(@var_name)} = #{colorize_magenta(value)}",
|
43
|
+
depth: depth - 2 - @depth_offset,
|
44
|
+
location: location
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/tracer/version.rb
CHANGED
data/lib/tracer.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tracer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Stan Lo
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: exe
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2024-03-22 00:00:00.000000000 Z
|
13
13
|
dependencies: []
|
14
14
|
description: A Ruby tracer
|
15
15
|
email:
|
@@ -21,11 +21,8 @@ extra_rdoc_files: []
|
|
21
21
|
files:
|
22
22
|
- CHANGELOG.md
|
23
23
|
- CODE_OF_CONDUCT.md
|
24
|
-
- Gemfile
|
25
|
-
- Gemfile.lock
|
26
24
|
- LICENSE.txt
|
27
25
|
- README.md
|
28
|
-
- Rakefile
|
29
26
|
- lib/tracer.rb
|
30
27
|
- lib/tracer/base.rb
|
31
28
|
- lib/tracer/call_tracer.rb
|
@@ -33,10 +30,10 @@ files:
|
|
33
30
|
- lib/tracer/exception_tracer.rb
|
34
31
|
- lib/tracer/helper.rb
|
35
32
|
- lib/tracer/irb.rb
|
33
|
+
- lib/tracer/ivar_tracer.rb
|
36
34
|
- lib/tracer/line_tracer.rb
|
37
35
|
- lib/tracer/object_tracer.rb
|
38
36
|
- lib/tracer/version.rb
|
39
|
-
- tracer.gemspec
|
40
37
|
homepage: https://github.com/ruby/tracer
|
41
38
|
licenses:
|
42
39
|
- Ruby
|
@@ -60,7 +57,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
60
57
|
- !ruby/object:Gem::Version
|
61
58
|
version: '0'
|
62
59
|
requirements: []
|
63
|
-
rubygems_version: 3.4
|
60
|
+
rubygems_version: 3.5.4
|
64
61
|
signing_key:
|
65
62
|
specification_version: 4
|
66
63
|
summary: A Ruby tracer
|
data/Gemfile
DELETED
data/Gemfile.lock
DELETED
@@ -1,41 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
tracer (0.2.1)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: https://rubygems.org/
|
8
|
-
specs:
|
9
|
-
io-console (0.6.0)
|
10
|
-
irb (1.6.2)
|
11
|
-
reline (>= 0.3.0)
|
12
|
-
language_server-protocol (3.17.0.3)
|
13
|
-
power_assert (2.0.2)
|
14
|
-
prettier_print (1.2.0)
|
15
|
-
rake (13.0.6)
|
16
|
-
reline (0.3.2)
|
17
|
-
io-console (~> 0.5)
|
18
|
-
ruby-lsp (0.3.8)
|
19
|
-
language_server-protocol (~> 3.17.0)
|
20
|
-
sorbet-runtime
|
21
|
-
syntax_tree (>= 5.0.0, < 6)
|
22
|
-
sorbet-runtime (0.5.10648)
|
23
|
-
syntax_tree (5.3.0)
|
24
|
-
prettier_print (>= 1.2.0)
|
25
|
-
test-unit (3.5.5)
|
26
|
-
power_assert
|
27
|
-
|
28
|
-
PLATFORMS
|
29
|
-
arm64-darwin-21
|
30
|
-
arm64-darwin-22
|
31
|
-
x86_64-linux
|
32
|
-
|
33
|
-
DEPENDENCIES
|
34
|
-
irb
|
35
|
-
rake (~> 13.0)
|
36
|
-
ruby-lsp
|
37
|
-
test-unit (~> 3.0)
|
38
|
-
tracer!
|
39
|
-
|
40
|
-
BUNDLED WITH
|
41
|
-
2.4.2
|
data/Rakefile
DELETED
data/tracer.gemspec
DELETED
@@ -1,43 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
require_relative "lib/tracer/version"
|
4
|
-
|
5
|
-
Gem::Specification.new do |spec|
|
6
|
-
spec.name = "tracer"
|
7
|
-
spec.version = Tracer::VERSION
|
8
|
-
spec.authors = ["Stan Lo", "Keiju ISHITSUKA"]
|
9
|
-
spec.email = %w[stan001212@gmail.com keiju@ruby-lang.org]
|
10
|
-
|
11
|
-
spec.summary = "A Ruby tracer"
|
12
|
-
spec.description = "A Ruby tracer"
|
13
|
-
spec.homepage = "https://github.com/ruby/tracer"
|
14
|
-
spec.licenses = %w[Ruby BSD-2-Clause]
|
15
|
-
spec.required_ruby_version = ">= 2.7.0"
|
16
|
-
|
17
|
-
spec.metadata["homepage_uri"] = spec.homepage
|
18
|
-
spec.metadata["source_code_uri"] = "https://github.com/ruby/tracer"
|
19
|
-
spec.metadata[
|
20
|
-
"changelog_uri"
|
21
|
-
] = "https://github.com/ruby/tracer/blob/main/CHANGELOG.md"
|
22
|
-
|
23
|
-
# Specify which files should be added to the gem when it is released.
|
24
|
-
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
|
25
|
-
spec.files =
|
26
|
-
Dir.chdir(__dir__) do
|
27
|
-
`git ls-files -z`.split("\x0")
|
28
|
-
.reject do |f|
|
29
|
-
(f == __FILE__) ||
|
30
|
-
f.match(
|
31
|
-
%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|circleci)|appveyor)}
|
32
|
-
)
|
33
|
-
end
|
34
|
-
end
|
35
|
-
spec.bindir = "exe"
|
36
|
-
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
|
37
|
-
spec.require_paths = ["lib"]
|
38
|
-
|
39
|
-
# Uncomment to register a new dependency of your gem
|
40
|
-
|
41
|
-
# For more information and examples about making a new gem, check out our
|
42
|
-
# guide at: https://bundler.io/guides/creating_gem.html
|
43
|
-
end
|