ruby_tracer 0.1.0 → 0.3.0

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: 752f406d1a3b8967596a30cc918fe2176f0da30df84651316ce452fae36cc940
4
- data.tar.gz: cc21e0f38a5508be8154621d7a68a247a2cc1a43d0fc9c3294176bb32ad079ca
3
+ metadata.gz: 0ac708e995302930fed59fa3b9396dc5b3c45ab76d4306c8bbac65fe69c49377
4
+ data.tar.gz: 99f23f4e5fb2d5f8ae694ff1203524707c757fd5a91cd29131ac61e79c5cb15f
5
5
  SHA512:
6
- metadata.gz: ef97f505b367ef51efe01877912697208f42be67e5d531515c176aa9b2e6c14a0c795a5707b075d3a9f4b4051a96257220c905c0437bfc64f7e99a01deb942aa
7
- data.tar.gz: 5162367688b0581d28c63219f6a38953eff903fcbe9473b22001edb8670ad1f945c89308f9833d7684d7b331457dc35d25e3ff6cdb4cef4d34defccee1d0ef5e
6
+ metadata.gz: dd1e5c1aef7115227db143c95939ad7ce180c65924334f30540dbed8f9d2f5cd468f0e3e989af1e7d6dd4e49e023798d5a1133c26cf49ccef54080eceb6f925c
7
+ data.tar.gz: 0dd0e49cf41d3e9fe4fd880b68364f1727d4d29bb4fb8c35b76e37ed97549979130d2678f2f2a0c720282ad17bd0abb15b0694478dea24b40dc57baf452b434b
data/Gemfile CHANGED
@@ -6,6 +6,7 @@ source "https://rubygems.org"
6
6
  gemspec
7
7
 
8
8
  gem "rake", "~> 13.0"
9
+ gem "irb"
9
10
 
10
11
  gem "test-unit", "~> 3.0"
11
12
 
data/Gemfile.lock CHANGED
@@ -1,15 +1,20 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- ruby_tracer (0.1.0)
4
+ ruby_tracer (0.3.0)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
8
8
  specs:
9
+ io-console (0.6.0)
10
+ irb (1.6.2)
11
+ reline (>= 0.3.0)
9
12
  language_server-protocol (3.17.0.3)
10
13
  power_assert (2.0.2)
11
14
  prettier_print (1.2.0)
12
15
  rake (13.0.6)
16
+ reline (0.3.2)
17
+ io-console (~> 0.5)
13
18
  ruby-lsp (0.3.8)
14
19
  language_server-protocol (~> 3.17.0)
15
20
  sorbet-runtime
@@ -25,6 +30,7 @@ PLATFORMS
25
30
  x86_64-linux
26
31
 
27
32
  DEPENDENCIES
33
+ irb
28
34
  rake (~> 13.0)
29
35
  ruby-lsp
30
36
  ruby_tracer!
data/README.md CHANGED
@@ -1,14 +1,6 @@
1
1
  # ruby_tracer
2
2
 
3
- ruby_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 and some improvements on accuracy.
4
-
5
- Its goal is to help users understand their Ruby programss activities by emitting useful trace information, such us:
6
-
7
- - How and where is the target object is being used (`ObjectTracer`)
8
- - What exceptions are raised during the execution (`ExceptionTracer`)
9
- - When method calls are being performed (`CallTracer`)
10
- - Line execution (`LineTracer`)
11
-
3
+ ruby_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 improvements on accuracy.
12
4
 
13
5
  ## Installation
14
6
 
@@ -24,7 +16,92 @@ $ gem install ruby_tracer
24
16
 
25
17
  ## Usage
26
18
 
27
- ### ObjectTracer
19
+ ```rb
20
+ Tracer.trace(object) { ... } # trace object's activities in the given block
21
+ Tracer.trace_call { ... } # trace method calls in the given block
22
+ Tracer.trace_exception { ... } # trace exceptions in the given block
23
+ ```
24
+
25
+ **Example**
26
+
27
+ ```rb
28
+ require "ruby_tracer"
29
+
30
+ obj = Object.new
31
+
32
+ def obj.foo
33
+ 100
34
+ end
35
+
36
+ def bar(obj)
37
+ obj.foo
38
+ end
39
+
40
+ Tracer.trace(obj) { bar(obj) }
41
+ #depth:1 #<Object:0x000000010903c190> is used as a parameter obj of Object#bar at test.rb:13:in `block in <main>'
42
+ #depth:2 #<Object:0x000000010903c190> receives .foo at test.rb:10:in `bar'
43
+ ```
44
+
45
+ ### `ruby_tracer/helper`
46
+
47
+ If you want to avoid the `Tracer` namespace, you can do `require "ruby_tracer/helper"` instead:
48
+
49
+ ```rb
50
+ require "ruby_tracer/helper"
51
+
52
+ trace(object) { ... } # trace object's activities in the given block
53
+ trace_call { ... } # trace method calls in the given block
54
+ trace_exception { ... } # trace exceptions in the given block
55
+ ```
56
+
57
+ ### IRB-integration
58
+
59
+ Once required, `ruby_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 "ruby_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
+ ### Tracer Classes
101
+
102
+ If you want to have more control over individual traces, you can use individual tracer classes:
103
+
104
+ #### ObjectTracer
28
105
 
29
106
  ```rb
30
107
  class User
@@ -49,7 +126,7 @@ end
49
126
  #depth:4 #<User:0x000000010696cad8 @name="John"> receives #name (User#name) at test.rb:8:in `authorized?'
50
127
  ```
51
128
 
52
- ### ExceptionTracer
129
+ #### ExceptionTracer
53
130
 
54
131
  ```rb
55
132
  ExceptionTracer.new.start
@@ -63,7 +140,7 @@ end
63
140
  #depth:1 #<RuntimeError: boom> at test.rb:4
64
141
  ```
65
142
 
66
- ### CallTracer
143
+ #### CallTracer
67
144
 
68
145
  ```rb
69
146
  class User
@@ -95,7 +172,7 @@ end
95
172
  #depth:4 < block #=> true at test.rb:16
96
173
  ```
97
174
 
98
- ### LineTracer
175
+ #### LineTracer
99
176
 
100
177
  ```rb
101
178
  class User
@@ -120,6 +197,10 @@ end
120
197
  #depth:5 at test.rb:8
121
198
  ```
122
199
 
200
+ ## Customization
201
+
202
+ TBD
203
+
123
204
  ## Acknowledgement
124
205
 
125
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.
@@ -70,10 +70,11 @@ module Tracer
70
70
  end
71
71
  end
72
72
 
73
- def initialize(output: STDOUT, pattern: nil, colorize: nil)
73
+ def initialize(output: STDOUT, pattern: nil, colorize: nil, depth_offset: 0)
74
74
  @name = self.class.name
75
75
  @type = @name.sub(/Tracer\z/, "")
76
76
  @output = output
77
+ @depth_offset = depth_offset
77
78
  @colorize = colorize || colorizable?
78
79
 
79
80
  if pattern
@@ -7,22 +7,30 @@ class CallTracer < Tracer::Base
7
7
  TracePoint.new(:a_call, :a_return) do |tp|
8
8
  next if skip?(tp)
9
9
 
10
+ location = caller_locations(2, 1).first.to_s
11
+ next if location.match?(DIR) || location.match?(/<internal:/)
12
+
10
13
  depth = caller.size
11
14
 
12
15
  call_identifier_str = (tp.defined_class ? minfo(tp) : "block")
13
-
14
16
  call_identifier_str = colorize_blue(call_identifier_str)
15
17
 
16
18
  case tp.event
17
19
  when :call, :c_call, :b_call
18
20
  depth += 1 if tp.event == :c_call
19
21
  sp = " " * depth
20
- out tp, ">#{sp}#{call_identifier_str}", depth: depth
22
+ out tp,
23
+ ">#{sp}#{call_identifier_str}",
24
+ depth: depth - 2 - @depth_offset,
25
+ location: location
21
26
  when :return, :c_return, :b_return
22
27
  depth += 1 if tp.event == :c_return
23
28
  sp = " " * depth
24
29
  return_str = colorize_magenta(safe_inspect(tp.return_value))
25
- out tp, "<#{sp}#{call_identifier_str} #=> #{return_str}", depth: depth
30
+ out tp,
31
+ "<#{sp}#{call_identifier_str} #=> #{return_str}",
32
+ depth: depth - 2 - @depth_offset,
33
+ location: location
26
34
  end
27
35
  end
28
36
  end
@@ -9,7 +9,9 @@ class ExceptionTracer < Tracer::Base
9
9
 
10
10
  exc = tp.raised_exception
11
11
 
12
- out tp, " #{colorize_magenta(exc.inspect)}"
12
+ out tp,
13
+ " #{colorize_magenta(exc.inspect)}",
14
+ depth: caller.size - (1 + @depth_offset)
13
15
  rescue Exception => e
14
16
  p e
15
17
  end
@@ -0,0 +1,3 @@
1
+ require "ruby_tracer"
2
+
3
+ Object.include(Tracer::Helper)
@@ -0,0 +1,90 @@
1
+ require "irb/cmd/nop"
2
+ require "irb"
3
+
4
+ module Tracer
5
+ def self.register_irb_commands
6
+ ec = IRB::ExtendCommandBundle.instance_variable_get(:@EXTEND_COMMANDS)
7
+
8
+ [
9
+ [:trace, :Trace, nil, [:trace, IRB::ExtendCommandBundle::OVERRIDE_ALL]],
10
+ [
11
+ :trace_call,
12
+ :TraceCall,
13
+ nil,
14
+ [:trace_call, IRB::ExtendCommandBundle::OVERRIDE_ALL]
15
+ ],
16
+ [
17
+ :trace_exception,
18
+ :TraceException,
19
+ nil,
20
+ [:trace_exception, IRB::ExtendCommandBundle::OVERRIDE_ALL]
21
+ ]
22
+ ].each do |ecconfig|
23
+ ec.push(ecconfig)
24
+ IRB::ExtendCommandBundle.def_extend_command(*ecconfig)
25
+ end
26
+ end
27
+ end
28
+
29
+ module IRB
30
+ module ExtendCommand
31
+ class TraceCommand < Nop
32
+ class << self
33
+ def transform_args(args)
34
+ # Return a string literal as is for backward compatibility
35
+ if args.empty? || string_literal?(args)
36
+ args
37
+ else # Otherwise, consider the input as a String for convenience
38
+ args.strip.dump
39
+ end
40
+ end
41
+ end
42
+ end
43
+
44
+ class Trace < TraceCommand
45
+ category "Tracing"
46
+ description "Trace the target object (or self) in the given expression. Usage: `trace [target,] <expression>`"
47
+
48
+ def execute(*args)
49
+ args = args.first.split(/,/, 2)
50
+
51
+ case args.size
52
+ when 1
53
+ target = irb_context.workspace.main
54
+ expression = args.first
55
+ when 2
56
+ target = eval(args.first, irb_context.workspace.binding)
57
+ expression = args.last
58
+ else
59
+ raise ArgumentError,
60
+ "wrong number of arguments (given #{args.size}, expected 1..2)"
61
+ end
62
+
63
+ b = irb_context.workspace.binding
64
+ Tracer.trace(target) { eval(expression, b) }
65
+ end
66
+ end
67
+
68
+ class TraceCall < TraceCommand
69
+ category "Tracing"
70
+ description "Trace method calls in the given expression. Usage: `trace_call <expression>`"
71
+
72
+ def execute(expression)
73
+ b = irb_context.workspace.binding
74
+ Tracer.trace_call { eval(expression, b) }
75
+ end
76
+ end
77
+
78
+ class TraceException < TraceCommand
79
+ category "Tracing"
80
+ description "Trace exceptions in the given expression. Usage: `trace_exception <expression>`"
81
+
82
+ def execute(expression)
83
+ b = irb_context.workspace.binding
84
+ Tracer.trace_exception { eval(expression, b) }
85
+ end
86
+ end
87
+ end
88
+ end
89
+
90
+ Tracer.register_irb_commands
@@ -65,7 +65,7 @@ class ObjectTracer < Tracer::Base
65
65
  out tp,
66
66
  " #{colorized_target_label} receives #{colorize_blue(method_info)}",
67
67
  location: caller_locations(internal_depth, 1).first,
68
- depth: caller.size - internal_depth
68
+ depth: caller.size - internal_depth - @depth_offset
69
69
  elsif !tp.parameters.empty?
70
70
  b = tp.binding
71
71
  method_info = colorize_blue(minfo(tp))
@@ -82,7 +82,7 @@ class ObjectTracer < Tracer::Base
82
82
  out tp,
83
83
  " #{colorized_target_label} is used as a parameter #{colorized_name} of #{method_info}",
84
84
  location: caller_locations(internal_depth, 1).first,
85
- depth: caller.size - internal_depth
85
+ depth: caller.size - internal_depth - @depth_offset
86
86
  end
87
87
  when :rest
88
88
  next if name == :"*"
@@ -94,7 +94,7 @@ class ObjectTracer < Tracer::Base
94
94
  out tp,
95
95
  " #{colorized_target_label} is used as a parameter in #{colorized_name} of #{method_info}",
96
96
  location: caller_locations(internal_depth, 1).first,
97
- depth: caller.size - internal_depth
97
+ depth: caller.size - internal_depth - @depth_offset
98
98
  end
99
99
  end
100
100
  when :keyrest
@@ -106,7 +106,7 @@ class ObjectTracer < Tracer::Base
106
106
  out tp,
107
107
  " #{colorized_target_label} is used as a parameter in #{colorized_name} of #{method_info}",
108
108
  location: caller_locations(internal_depth, 1).first,
109
- depth: caller.size - internal_depth
109
+ depth: caller.size - internal_depth - @depth_offset
110
110
  end
111
111
  end
112
112
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Tracer
4
- VERSION = "0.1.0"
4
+ VERSION = "0.3.0"
5
5
  end
data/lib/ruby_tracer.rb CHANGED
@@ -5,3 +5,28 @@ require_relative "ruby_tracer/line_tracer"
5
5
  require_relative "ruby_tracer/call_tracer"
6
6
  require_relative "ruby_tracer/exception_tracer"
7
7
  require_relative "ruby_tracer/object_tracer"
8
+
9
+ module Tracer
10
+ module Helper
11
+ DEPTH_OFFSET = 3
12
+
13
+ def trace_exception(&block)
14
+ tracer = ExceptionTracer.new(depth_offset: DEPTH_OFFSET)
15
+ tracer.start(&block)
16
+ end
17
+
18
+ def trace_call(&block)
19
+ tracer = CallTracer.new(depth_offset: DEPTH_OFFSET)
20
+ tracer.start(&block)
21
+ end
22
+
23
+ def trace(target, &block)
24
+ tracer = ObjectTracer.new(target, depth_offset: DEPTH_OFFSET)
25
+ tracer.start(&block)
26
+ end
27
+ end
28
+
29
+ extend Helper
30
+ end
31
+
32
+ require_relative "ruby_tracer/irb"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby_tracer
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Stan Lo
@@ -29,6 +29,8 @@ files:
29
29
  - lib/ruby_tracer/call_tracer.rb
30
30
  - lib/ruby_tracer/color.rb
31
31
  - lib/ruby_tracer/exception_tracer.rb
32
+ - lib/ruby_tracer/helper.rb
33
+ - lib/ruby_tracer/irb.rb
32
34
  - lib/ruby_tracer/line_tracer.rb
33
35
  - lib/ruby_tracer/object_tracer.rb
34
36
  - lib/ruby_tracer/version.rb