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 +4 -4
- data/Gemfile +1 -0
- data/Gemfile.lock +7 -1
- data/README.md +94 -13
- data/lib/ruby_tracer/base.rb +2 -1
- data/lib/ruby_tracer/call_tracer.rb +11 -3
- data/lib/ruby_tracer/exception_tracer.rb +3 -1
- data/lib/ruby_tracer/helper.rb +3 -0
- data/lib/ruby_tracer/irb.rb +90 -0
- data/lib/ruby_tracer/object_tracer.rb +4 -4
- data/lib/ruby_tracer/version.rb +1 -1
- data/lib/ruby_tracer.rb +25 -0
- metadata +3 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0ac708e995302930fed59fa3b9396dc5b3c45ab76d4306c8bbac65fe69c49377
|
4
|
+
data.tar.gz: 99f23f4e5fb2d5f8ae694ff1203524707c757fd5a91cd29131ac61e79c5cb15f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: dd1e5c1aef7115227db143c95939ad7ce180c65924334f30540dbed8f9d2f5cd468f0e3e989af1e7d6dd4e49e023798d5a1133c26cf49ccef54080eceb6f925c
|
7
|
+
data.tar.gz: 0dd0e49cf41d3e9fe4fd880b68364f1727d4d29bb4fb8c35b76e37ed97549979130d2678f2f2a0c720282ad17bd0abb15b0694478dea24b40dc57baf452b434b
|
data/Gemfile
CHANGED
data/Gemfile.lock
CHANGED
@@ -1,15 +1,20 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
ruby_tracer (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
|
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
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
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.
|
data/lib/ruby_tracer/base.rb
CHANGED
@@ -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,
|
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,
|
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
|
@@ -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
|
data/lib/ruby_tracer/version.rb
CHANGED
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.
|
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
|