tracer 0.2.1 → 0.2.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 13d3b55fac8c903711f78ccb14e872f8c312dde28dda5ed7f5fa664d04dd2a3b
4
- data.tar.gz: 71c21f53b34f98870d4e6b49c84432867375c24f56c3d3a4ebd47e4d6bdaeef5
3
+ metadata.gz: 2384a62a824cb7906cd027700d16844a6b666e5dc7b04749fcb2d69ece9a2a85
4
+ data.tar.gz: 762568e03d81d9dafa2ad6735f9901d53507aaeaa350aac53ef3989325136757
5
5
  SHA512:
6
- metadata.gz: 54cb3be80359764539089df166a7ad922dea9ebea0cb8450a815f1956fbd780c3d6ed31bd5ea8e01cbf24689131c6463a5edb156220313b0c12e024f54d99565
7
- data.tar.gz: 683343e41cb7150fdd256c4427a4ca9971a87b592093721e5d3f00a40141136eeaf884a9242e7b20f46a0e47f35f445eede9c2b612731f12a38f5f1c20ad6c3a
6
+ metadata.gz: bbdccfb4cf5054d554a76e22f9bbffd21aa8f3e9a13e366cbfb41d076803c4de4b019697b81d8dd1278655390f7c1949190e7d7c72b2fcdc7ffa83622b89a71d
7
+ data.tar.gz: b0a8142e250b24964d9fbcbc674d052107061725b6673b7d573553faf55afc722559396c0b878f3639c051dcfd1a9794e285116ba38e2f493d4be9b8d17c11ae
data/README.md CHANGED
@@ -1,6 +1,19 @@
1
1
  # Tracer
2
2
 
3
- Tracer is an extraction of [`ruby/debug`](https://github.com/ruby/debug)'s [powerful tracers](https://github.com/ruby/debug/blob/master/lib/debug/tracer.rb), with user-facing APIs, IRB-integration, and some improvements.
3
+ [![Ruby](https://github.com/ruby/tracer/actions/workflows/main.yml/badge.svg)](https://github.com/ruby/tracer/actions/workflows/main.yml)
4
+ [![Gem Version](https://badge.fury.io/rb/tracer.svg)](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
- #depth:1 #<RuntimeError: boom> at test.rb:4
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
- ## Acknowledgement
249
+ ## Acknowledgements
205
250
 
206
- [@ko1](https://github.com/ko1) (Koichi Sasada) implemented the majority of [`ruby/debug`](https://github.com/ruby/debug), including its tracers. So this project wouldn't exist without his amazing work there.
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
- def initialize(output: STDOUT, pattern: nil, colorize: nil, depth_offset: 0)
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
- buff =
145
- "#{header} \#depth:#{"%-2d" % depth}#{msg} at #{colorize("#{location}", [:GREEN])}"
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 buff
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
- TracePoint.new(:raise) do |tp|
8
- next if skip?(tp)
7
+ if RUBY_VERSION >= "3.3.0"
8
+ TracePoint.new(:raise, :rescue) do |tp|
9
+ next if skip?(tp)
9
10
 
10
- exc = tp.raised_exception
11
+ exc = tp.raised_exception
11
12
 
12
- out tp,
13
- " #{colorize_magenta(exc.inspect)}",
14
- depth: caller.size - (1 + @depth_offset)
15
- rescue Exception => e
16
- p e
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
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tracer
4
- VERSION = "0.2.1"
4
+ VERSION = "0.2.3"
5
5
  end
data/lib/tracer.rb CHANGED
@@ -5,6 +5,7 @@ require_relative "tracer/line_tracer"
5
5
  require_relative "tracer/call_tracer"
6
6
  require_relative "tracer/exception_tracer"
7
7
  require_relative "tracer/object_tracer"
8
+ require_relative "tracer/ivar_tracer"
8
9
 
9
10
  module Tracer
10
11
  module Helper
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.1
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: 2023-05-19 00:00:00.000000000 Z
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.10
60
+ rubygems_version: 3.5.4
64
61
  signing_key:
65
62
  specification_version: 4
66
63
  summary: A Ruby tracer
data/Gemfile DELETED
@@ -1,13 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- source "https://rubygems.org"
4
-
5
- # Specify your gem's dependencies in ruby-tracer.gemspec
6
- gemspec
7
-
8
- gem "rake", "~> 13.0"
9
- gem "irb"
10
-
11
- gem "test-unit", "~> 3.0"
12
-
13
- gem "ruby-lsp"
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
@@ -1,12 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require "bundler/gem_tasks"
4
- require "rake/testtask"
5
-
6
- Rake::TestTask.new(:test) do |t|
7
- t.libs << "test"
8
- t.libs << "lib"
9
- t.test_files = FileList["test/**/*_test.rb"]
10
- end
11
-
12
- task default: :test
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