ruby_tracer 0.1.0 → 0.3.0

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: 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