im-lost 1.1.0 → 1.2.1
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/LICENSE +1 -1
- data/README.md +37 -30
- data/examples/foo.rb +29 -22
- data/examples/kernel_calls.rb +0 -1
- data/lib/im-lost/version.rb +1 -1
- data/lib/im-lost.rb +273 -176
- metadata +3 -7
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 533f4f50596d5c6a55f674e813d7053d954a26864ea80c2e266f8c6bd92c00fd
|
4
|
+
data.tar.gz: 31a7bd884fabb3aba95789ff9771811300e3c7cb6e75ec3c762a394df603b2ed
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4631c248cc8af2691d09651ed3a90b863f72ba6954e3c14172208ceb153e69b834e5f67c12555347fa474f2f97a44ad579f8ef85a6b5db4be07ea574b76a7f36
|
7
|
+
data.tar.gz: d1accd54c95db533220dd98a6a741380be91050568d16cbc3b6acdaef1ba147b98f2ad39f560b1613b7351c01dca4a44bafd4db46e7ef526b169e5821da68fc0
|
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -10,7 +10,7 @@ ImLost helps you by analyzing function calls of objects, informing you about exc
|
|
10
10
|
|
11
11
|
## Description
|
12
12
|
|
13
|
-
If you like to
|
13
|
+
If you like to understand method call details you get a call trace with `ImLost.trace`:
|
14
14
|
|
15
15
|
```ruby
|
16
16
|
File.open('test.txt', 'w') do |file|
|
@@ -22,13 +22,13 @@ end
|
|
22
22
|
|
23
23
|
# output will look like
|
24
24
|
# > IO#<<(?)
|
25
|
-
# /
|
25
|
+
# /examples/test.rb:1
|
26
26
|
# > IO#write(*)
|
27
|
-
# /
|
27
|
+
# /examples/test.rb:1
|
28
28
|
# > IO#puts(*)
|
29
|
-
# /
|
29
|
+
# /examples/test.rb:2
|
30
30
|
# > IO#write(*)
|
31
|
-
# /
|
31
|
+
# /examples/test.rb:2
|
32
32
|
```
|
33
33
|
|
34
34
|
When you need to know if exceptions are raised and handled you can use `ImLost.trace_exceptions`:
|
@@ -42,11 +42,11 @@ end
|
|
42
42
|
|
43
43
|
# output will look like
|
44
44
|
# x Errno::EEXIST: File exists @ rb_sysopen - /
|
45
|
-
# /
|
45
|
+
# /examples/test.rb:2
|
46
46
|
# ! Errno::EEXIST: File exists @ rb_sysopen - /
|
47
|
-
# /
|
47
|
+
# /examples/test.rb:3
|
48
48
|
# x RuntimeError: something went wrong!
|
49
|
-
# /
|
49
|
+
# /examples/test.rb:4
|
50
50
|
```
|
51
51
|
|
52
52
|
When you like to know if a code point is reached, `ImLost.here` will help:
|
@@ -94,7 +94,6 @@ class Foo
|
|
94
94
|
def bar = :bar
|
95
95
|
end
|
96
96
|
|
97
|
-
ImLost.trace_results = true
|
98
97
|
ImLost.trace(Foo)
|
99
98
|
|
100
99
|
my_foo = Foo.create(value: :foo!)
|
@@ -111,47 +110,55 @@ ImLost.vars(my_foo)
|
|
111
110
|
|
112
111
|
# output will look like
|
113
112
|
# > Foo.create(:foo!)
|
114
|
-
# /
|
113
|
+
# /examples/foo.rb:24
|
115
114
|
# > Foo.new(*)
|
116
|
-
# /
|
115
|
+
# /examples/foo.rb:6
|
117
116
|
# < Foo.new(*)
|
118
|
-
#
|
117
|
+
# /examples/foo.rb:6
|
118
|
+
# = #<Foo:0x00000001006448c0 @value=:foo!>
|
119
119
|
# < Foo.create(:foo!)
|
120
|
-
#
|
120
|
+
# /examples/foo.rb:24
|
121
|
+
# = #<Foo:0x00000001006448c0 @value=:foo!>
|
121
122
|
# > Foo#foo(1, *[], :none, **{}, &nil)
|
122
|
-
# /
|
123
|
+
# /examples/foo.rb:27
|
123
124
|
# > Foo#bar()
|
124
|
-
# /
|
125
|
+
# /examples/foo.rb:15
|
125
126
|
# < Foo#bar()
|
127
|
+
# /examples/foo.rb:15
|
126
128
|
# = :bar
|
127
129
|
# < Foo#foo(1, *[], :none, **{}, &nil)
|
130
|
+
# /examples/foo.rb:27
|
128
131
|
# = "1-none-[]-{}-bar"
|
129
|
-
#
|
130
|
-
# instance variables
|
131
|
-
#
|
132
|
+
# * /examples/foo.rb:28
|
133
|
+
# > instance variables
|
134
|
+
# @value: "1-none-[]-{}-bar"
|
132
135
|
# > Foo#foo(2, *[:a, :b, :c], :some, **{:name=>:value}, &nil)
|
133
|
-
# /
|
136
|
+
# /examples/foo.rb:30
|
134
137
|
# > Foo#bar()
|
135
|
-
# /
|
138
|
+
# /examples/foo.rb:15
|
136
139
|
# < Foo#bar()
|
140
|
+
# /examples/foo.rb:15
|
137
141
|
# = :bar
|
138
142
|
# < Foo#foo(2, *[:a, :b, :c], :some, **{:name=>:value}, &nil)
|
143
|
+
# /examples/foo.rb:30
|
139
144
|
# = "2-some-[a,b,c]-{:name=>:value}-bar"
|
140
|
-
#
|
141
|
-
# instance variables
|
142
|
-
#
|
143
|
-
# > Foo#foo(3, *[], nil, **{}, &#<Proc:
|
144
|
-
# /
|
145
|
+
# * /examples/foo.rb:31
|
146
|
+
# > instance variables
|
147
|
+
# @value: "2-some-[a,b,c]-{:name=>:value}-bar"
|
148
|
+
# > Foo#foo(3, *[], nil, **{}, &#<Proc:0x0000000100641d28 /examples/foo.rb:33>)
|
149
|
+
# /examples/foo.rb:33
|
145
150
|
# > Foo#bar()
|
146
|
-
# /
|
151
|
+
# /examples/foo.rb:15
|
147
152
|
# < Foo#bar()
|
153
|
+
# /examples/foo.rb:15
|
148
154
|
# = :bar
|
149
155
|
# 3--[]-{}-bar
|
150
|
-
# < Foo#foo(3, *[], nil, **{}, &#<Proc:
|
156
|
+
# < Foo#foo(3, *[], nil, **{}, &#<Proc:0x0000000100641d28 /examples/foo.rb:33>)
|
157
|
+
# /examples/foo.rb:33
|
151
158
|
# = nil
|
152
|
-
#
|
153
|
-
# instance variables
|
154
|
-
#
|
159
|
+
# * /examples/foo.rb:34
|
160
|
+
# > instance variables
|
161
|
+
# @value: "3--[]-{}-bar"
|
155
162
|
```
|
156
163
|
|
157
164
|
See [examples dir](./examples) for more…
|
data/examples/foo.rb
CHANGED
@@ -19,7 +19,6 @@ class Foo
|
|
19
19
|
def bar = :bar
|
20
20
|
end
|
21
21
|
|
22
|
-
ImLost.trace_results = true
|
23
22
|
ImLost.trace(Foo)
|
24
23
|
|
25
24
|
my_foo = Foo.create(value: :foo!)
|
@@ -36,44 +35,52 @@ ImLost.vars(my_foo)
|
|
36
35
|
|
37
36
|
# output will look like
|
38
37
|
# > Foo.create(:foo!)
|
39
|
-
# /
|
38
|
+
# /examples/foo.rb:24
|
40
39
|
# > Foo.new(*)
|
41
|
-
# /
|
40
|
+
# /examples/foo.rb:6
|
42
41
|
# < Foo.new(*)
|
43
|
-
#
|
42
|
+
# /examples/foo.rb:6
|
43
|
+
# = #<Foo:0x00000001006448c0 @value=:foo!>
|
44
44
|
# < Foo.create(:foo!)
|
45
|
-
#
|
45
|
+
# /examples/foo.rb:24
|
46
|
+
# = #<Foo:0x00000001006448c0 @value=:foo!>
|
46
47
|
# > Foo#foo(1, *[], :none, **{}, &nil)
|
47
|
-
# /
|
48
|
+
# /examples/foo.rb:27
|
48
49
|
# > Foo#bar()
|
49
|
-
# /
|
50
|
+
# /examples/foo.rb:15
|
50
51
|
# < Foo#bar()
|
52
|
+
# /examples/foo.rb:15
|
51
53
|
# = :bar
|
52
54
|
# < Foo#foo(1, *[], :none, **{}, &nil)
|
55
|
+
# /examples/foo.rb:27
|
53
56
|
# = "1-none-[]-{}-bar"
|
54
|
-
#
|
55
|
-
# instance variables
|
56
|
-
#
|
57
|
+
# * /examples/foo.rb:28
|
58
|
+
# > instance variables
|
59
|
+
# @value: "1-none-[]-{}-bar"
|
57
60
|
# > Foo#foo(2, *[:a, :b, :c], :some, **{:name=>:value}, &nil)
|
58
|
-
# /
|
61
|
+
# /examples/foo.rb:30
|
59
62
|
# > Foo#bar()
|
60
|
-
# /
|
63
|
+
# /examples/foo.rb:15
|
61
64
|
# < Foo#bar()
|
65
|
+
# /examples/foo.rb:15
|
62
66
|
# = :bar
|
63
67
|
# < Foo#foo(2, *[:a, :b, :c], :some, **{:name=>:value}, &nil)
|
68
|
+
# /examples/foo.rb:30
|
64
69
|
# = "2-some-[a,b,c]-{:name=>:value}-bar"
|
65
|
-
#
|
66
|
-
# instance variables
|
67
|
-
#
|
68
|
-
# > Foo#foo(3, *[], nil, **{}, &#<Proc:
|
69
|
-
# /
|
70
|
+
# * /examples/foo.rb:31
|
71
|
+
# > instance variables
|
72
|
+
# @value: "2-some-[a,b,c]-{:name=>:value}-bar"
|
73
|
+
# > Foo#foo(3, *[], nil, **{}, &#<Proc:0x0000000100641d28 /examples/foo.rb:33>)
|
74
|
+
# /examples/foo.rb:33
|
70
75
|
# > Foo#bar()
|
71
|
-
# /
|
76
|
+
# /examples/foo.rb:15
|
72
77
|
# < Foo#bar()
|
78
|
+
# /examples/foo.rb:15
|
73
79
|
# = :bar
|
74
80
|
# 3--[]-{}-bar
|
75
|
-
# < Foo#foo(3, *[], nil, **{}, &#<Proc:
|
81
|
+
# < Foo#foo(3, *[], nil, **{}, &#<Proc:0x0000000100641d28 /examples/foo.rb:33>)
|
82
|
+
# /examples/foo.rb:33
|
76
83
|
# = nil
|
77
|
-
#
|
78
|
-
# instance variables
|
79
|
-
#
|
84
|
+
# * /examples/foo.rb:34
|
85
|
+
# > instance variables
|
86
|
+
# @value: "3--[]-{}-bar"
|
data/examples/kernel_calls.rb
CHANGED
data/lib/im-lost/version.rb
CHANGED
data/lib/im-lost.rb
CHANGED
@@ -26,9 +26,10 @@ module ImLost
|
|
26
26
|
|
27
27
|
#
|
28
28
|
# The output device used to write information.
|
29
|
-
# This should be an `IO` device or any other object responding to
|
29
|
+
# This should be an `IO` device or any other object responding to `#<<`
|
30
|
+
# like a Logger.
|
30
31
|
#
|
31
|
-
#
|
32
|
+
# `STDERR` is configured by default.
|
32
33
|
#
|
33
34
|
# @example Write to a file
|
34
35
|
# ImLost.output = File.new('./trace', 'w')
|
@@ -50,8 +51,13 @@ module ImLost
|
|
50
51
|
attr_reader :output
|
51
52
|
|
52
53
|
def output=(value)
|
53
|
-
return @output = value if
|
54
|
-
raise(
|
54
|
+
return @output = value if defined?(value.<<)
|
55
|
+
raise(
|
56
|
+
NoMethodError,
|
57
|
+
"undefined method '<<' for an instance of #{
|
58
|
+
Kernel.instance_method(:class).bind(value).call
|
59
|
+
}"
|
60
|
+
)
|
55
61
|
end
|
56
62
|
|
57
63
|
#
|
@@ -67,18 +73,18 @@ module ImLost
|
|
67
73
|
# @attribute [r] trace_calls
|
68
74
|
# @return [Boolean] whether method calls will be traced
|
69
75
|
#
|
70
|
-
def trace_calls = @trace_calls
|
76
|
+
def trace_calls = @trace_calls.enabled?
|
71
77
|
|
72
78
|
def trace_calls=(value)
|
73
79
|
if value
|
74
|
-
@trace_calls.
|
80
|
+
@trace_calls.enable unless trace_calls
|
75
81
|
elsif trace_calls
|
76
|
-
@trace_calls.
|
82
|
+
@trace_calls.disable
|
77
83
|
end
|
78
84
|
end
|
79
85
|
|
80
86
|
#
|
81
|
-
# Traces
|
87
|
+
# Traces exceptions raised within a given block.
|
82
88
|
#
|
83
89
|
# @example Trace exception and rescue handling
|
84
90
|
# ImLost.trace_exceptions do
|
@@ -89,13 +95,13 @@ module ImLost
|
|
89
95
|
#
|
90
96
|
# # output will look like
|
91
97
|
# # x Errno::EEXIST: File exists @ rb_sysopen - /
|
92
|
-
# #
|
98
|
+
# # /examples/test.rb:2
|
93
99
|
# # ! Errno::EEXIST: File exists @ rb_sysopen - /
|
94
|
-
# #
|
100
|
+
# # /examples/test.rb:3
|
95
101
|
# # x RuntimeError: something went wrong!
|
96
|
-
# #
|
102
|
+
# # /examples/test.rb:4
|
97
103
|
#
|
98
|
-
# @param with_locations [Boolean]
|
104
|
+
# @param with_locations [Boolean] whether the locations should be included
|
99
105
|
# into the exception trace information
|
100
106
|
# @yieldreturn [Object] return result
|
101
107
|
#
|
@@ -112,52 +118,52 @@ module ImLost
|
|
112
118
|
end
|
113
119
|
|
114
120
|
#
|
115
|
-
# Enables/disables tracing of returned
|
116
|
-
# This is
|
121
|
+
# Enables/disables tracing of returned values of method calls.
|
122
|
+
# This is enabled by default.
|
117
123
|
#
|
118
124
|
# @attribute [r] trace_results
|
119
125
|
# @return [Boolean] whether return values will be traced
|
120
126
|
#
|
121
|
-
def trace_results = @trace_results
|
127
|
+
def trace_results = @trace_results.enabled?
|
122
128
|
|
123
129
|
def trace_results=(value)
|
124
130
|
if value
|
125
|
-
@trace_results.
|
131
|
+
@trace_results.enable unless trace_results
|
126
132
|
elsif trace_results
|
127
|
-
@trace_results.
|
133
|
+
@trace_results.disable
|
128
134
|
end
|
129
135
|
end
|
130
136
|
|
131
137
|
#
|
132
138
|
# Print the call location conditionally.
|
133
139
|
#
|
134
|
-
# @example
|
140
|
+
# @example Print current location
|
135
141
|
# ImLost.here
|
136
142
|
#
|
137
|
-
# @example
|
143
|
+
# @example Print current location when instance variable is empty
|
138
144
|
# ImLost.here(@name.empty?)
|
139
145
|
#
|
140
|
-
# @example
|
146
|
+
# @example Print current location when instance variable is nil or empty
|
141
147
|
# ImLost.here { @name.nil? || @name.empty? }
|
142
148
|
#
|
143
149
|
# @overload here
|
144
|
-
# Prints the
|
150
|
+
# Prints the call location.
|
145
151
|
# @return [true]
|
146
152
|
#
|
147
153
|
# @overload here(test)
|
148
|
-
# Prints the
|
154
|
+
# Prints the call location when given argument is truthy.
|
149
155
|
# @param test [Object]
|
150
156
|
# @return [Object] test
|
151
157
|
#
|
152
158
|
# @overload here
|
153
|
-
# Prints the
|
159
|
+
# Prints the call location when given block returns a truthy result.
|
154
160
|
# @yield When the block returns a truthy result the location will be print
|
155
161
|
# @yieldreturn [Object] return result
|
156
162
|
#
|
157
163
|
def here(test = true)
|
158
164
|
return test if !test || (block_given? && !(test = yield))
|
159
165
|
loc = Kernel.caller_locations(1, 1)[0]
|
160
|
-
@output
|
166
|
+
@output << "* #{loc.path}:#{loc.lineno}\n"
|
161
167
|
test
|
162
168
|
end
|
163
169
|
|
@@ -166,12 +172,12 @@ module ImLost
|
|
166
172
|
#
|
167
173
|
# The given arguments can be any object instance or module or class.
|
168
174
|
#
|
169
|
-
# @example
|
175
|
+
# @example Trace method calls of an instance variable for a while
|
170
176
|
# ImLost.trace(@file)
|
171
177
|
# # ...
|
172
178
|
# ImLost.untrace(@file)
|
173
179
|
#
|
174
|
-
# @example
|
180
|
+
# @example Temporary trace method calls
|
175
181
|
# File.open('test.txt', 'w') do |file|
|
176
182
|
# ImLost.trace(file) do
|
177
183
|
# file << 'hello '
|
@@ -181,17 +187,17 @@ module ImLost
|
|
181
187
|
#
|
182
188
|
# # output will look like
|
183
189
|
# # > IO#<<(?)
|
184
|
-
# # /
|
190
|
+
# # /examples/test.rb:1
|
185
191
|
# # > IO#write(*)
|
186
|
-
# # /
|
192
|
+
# # /examples/test.rb:1
|
187
193
|
# # > IO#puts(*)
|
188
|
-
# # /
|
194
|
+
# # /examples/test.rb:2
|
189
195
|
# # > IO#write(*)
|
190
|
-
# # /
|
196
|
+
# # /examples/test.rb:2
|
191
197
|
#
|
192
198
|
# @overload trace(*args)
|
193
199
|
# @param args [[Object]] one or more objects to be traced
|
194
|
-
# @return [
|
200
|
+
# @return [Array<Object>] the traced object(s)
|
195
201
|
# Start tracing the given objects.
|
196
202
|
# @see untrace
|
197
203
|
# @see untrace_all!
|
@@ -209,30 +215,37 @@ module ImLost
|
|
209
215
|
args.size == 1 ? _trace_b(args[0], &block) : _trace_all_b(args, &block)
|
210
216
|
end
|
211
217
|
|
218
|
+
#
|
219
|
+
# Test if a given object is currently traced.
|
220
|
+
#
|
221
|
+
# @param arg [Object] object to be tested
|
222
|
+
# @return [Boolean] wheter the object is beeing traced
|
223
|
+
#
|
224
|
+
def traced?(obj) = @trace.key?(obj)
|
225
|
+
|
212
226
|
#
|
213
227
|
# Stop tracing objects.
|
214
228
|
#
|
215
|
-
# @example
|
216
|
-
#
|
229
|
+
# @example Trace some objects for some code lines
|
230
|
+
# traced_obj = ImLost.trace(@file, @client)
|
217
231
|
# # ...
|
218
|
-
# ImLost.untrace(*
|
232
|
+
# ImLost.untrace(*traced_obj)
|
219
233
|
#
|
220
234
|
# @see trace
|
221
235
|
#
|
222
|
-
# @param args [[Object]] one or more objects which should not longer be
|
236
|
+
# @param args [[]Object]] one or more objects which should not longer be
|
223
237
|
# traced
|
224
|
-
# @return [
|
238
|
+
# @return [Array<Object>] the object(s) which are not longer be traced
|
225
239
|
# @return [nil] when none of the objects was traced before
|
226
240
|
#
|
227
241
|
def untrace(*args)
|
228
|
-
|
229
|
-
args.size
|
242
|
+
args = args.filter_map { @trace.delete(_1) }
|
243
|
+
args.size < 2 ? args[0] : args
|
230
244
|
end
|
231
245
|
|
232
246
|
#
|
233
|
-
# Stop tracing any object.
|
234
|
-
#
|
235
|
-
# objects.)
|
247
|
+
# Stop tracing any object. When you are really lost and just like to stop
|
248
|
+
# tracing of all your objects.
|
236
249
|
#
|
237
250
|
# @see trace
|
238
251
|
#
|
@@ -244,101 +257,140 @@ module ImLost
|
|
244
257
|
end
|
245
258
|
|
246
259
|
#
|
247
|
-
# Inspect internal variables.
|
260
|
+
# Inspect internal variables of a given object.
|
261
|
+
#
|
262
|
+
# @note The dedicated handling of `Fiber` is platform dependent!
|
263
|
+
#
|
264
|
+
# @example Inspect current instance variables
|
265
|
+
# @a = 22
|
266
|
+
# b = 20
|
267
|
+
# c = @a + b
|
268
|
+
# ImLost.vars(self)
|
269
|
+
# # => print value of `@a`
|
270
|
+
#
|
271
|
+
# @example Inspect local variables
|
272
|
+
# @a = 22
|
273
|
+
# b = 20
|
274
|
+
# c = @a + b
|
275
|
+
# ImLost.vars(binding)
|
276
|
+
# # => print values of `b` and 'c'
|
248
277
|
#
|
249
|
-
# @
|
250
|
-
#
|
251
|
-
#
|
252
|
-
#
|
278
|
+
# @example Inspect a thread's variables
|
279
|
+
# th = Thread.new { th[:var1] += 20 }
|
280
|
+
# th[:var1] = 22
|
281
|
+
# ImLost.vars(th)
|
282
|
+
# # => print value of `var1`
|
283
|
+
# th.join
|
284
|
+
# ImLost.vars(th)
|
253
285
|
#
|
254
|
-
# @
|
255
|
-
#
|
256
|
-
#
|
257
|
-
#
|
286
|
+
# @example Inspect the current fiber's storage
|
287
|
+
# Fiber[:var1] = 22
|
288
|
+
# Fiber[:var2] = 20
|
289
|
+
# Fiber[:var3] = Fiber[:var1] + Fiber[:var2]
|
290
|
+
# ImLost.vars(Fiber.current)
|
291
|
+
#
|
292
|
+
# When the given object is
|
293
|
+
#
|
294
|
+
# - a `Binding` it prints the local variables of the binding
|
295
|
+
# - a `Thread` it prints the fiber-local and thread variables
|
296
|
+
# - the current `Fiber` it prints the fibers' storage
|
297
|
+
#
|
298
|
+
# Be aware that only the current fiber can be inspected.
|
299
|
+
#
|
300
|
+
# When the given object can not be inspected it prints an error message.
|
301
|
+
#
|
302
|
+
# @param object [Object] which instance variables should be print
|
303
|
+
# @return [Object] the given object
|
258
304
|
#
|
259
305
|
def vars(object)
|
260
|
-
|
261
|
-
|
262
|
-
return
|
263
|
-
|
306
|
+
out = LineStore.new
|
307
|
+
traced = @trace.delete(object)
|
308
|
+
return _local_vars(out, object) if Binding === object
|
309
|
+
out.location(Kernel.caller_locations(1, 1)[0])
|
310
|
+
return _thread_vars(out, object) if Thread === object
|
311
|
+
return _fiber_vars(out, object) if @fiber_support && Fiber === object
|
312
|
+
return _instance_vars(out, object) if defined?(object.instance_variables)
|
313
|
+
out << ' !!! unable to retrieve vars'
|
314
|
+
object
|
264
315
|
ensure
|
265
316
|
@trace[traced] = traced if traced
|
317
|
+
@output << out.to_s
|
266
318
|
end
|
267
319
|
|
268
320
|
private
|
269
321
|
|
270
|
-
def
|
271
|
-
|
272
|
-
case info.self
|
273
|
-
when Class, Module
|
274
|
-
"#{prefix} #{info.self}.#{info.method_id}(#{args})"
|
275
|
-
else
|
276
|
-
"#{prefix} #{info.defined_class}##{info.method_id}(#{args})"
|
277
|
-
end
|
322
|
+
def _can_trace?(arg)
|
323
|
+
(id = arg.__id__) != __id__ && id != @output.__id__
|
278
324
|
end
|
279
325
|
|
280
326
|
def _trace(arg)
|
281
|
-
|
282
|
-
@trace[id] = id if __id__ != id && @output.__id__ != id
|
327
|
+
@trace[arg] = arg if _can_trace?(arg)
|
283
328
|
arg
|
284
329
|
end
|
285
330
|
|
286
|
-
def _trace_all(args)
|
287
|
-
args.each do |arg|
|
288
|
-
arg = arg.__id__
|
289
|
-
@trace[arg] = arg if __id__ != arg && @output.__id__ != arg
|
290
|
-
end
|
291
|
-
args
|
292
|
-
end
|
331
|
+
def _trace_all(args) = args.each { @trace[_1] = _1 if _can_trace?(_1) }
|
293
332
|
|
294
333
|
def _trace_b(arg)
|
295
|
-
|
296
|
-
return yield(arg) if __id__ == id || @output.__id__ == id
|
334
|
+
return yield(arg) if @trace.key?(arg) || !_can_trace?(arg)
|
297
335
|
begin
|
298
|
-
@trace[
|
336
|
+
@trace[arg] = arg
|
299
337
|
yield(arg)
|
300
338
|
ensure
|
301
|
-
@trace.delete(
|
339
|
+
@trace.delete(arg)
|
302
340
|
end
|
303
341
|
end
|
304
342
|
|
305
343
|
def _trace_all_b(args)
|
306
|
-
|
344
|
+
temp =
|
307
345
|
args.filter_map do |arg|
|
308
|
-
arg = arg.
|
309
|
-
@trace[arg] = arg if __id__ != arg && @output.__id__ != arg
|
346
|
+
@trace[arg] = arg if !@trace.key?(arg) && _can_trace?(arg)
|
310
347
|
end
|
311
348
|
yield(args)
|
312
349
|
ensure
|
313
|
-
|
350
|
+
temp.each { @trace.delete(_1) }
|
314
351
|
end
|
315
352
|
|
316
|
-
def
|
317
|
-
|
318
|
-
vars
|
319
|
-
|
320
|
-
@output.puts(' <no instance variables defined>')
|
321
|
-
return obj
|
353
|
+
def _local_vars(out, binding)
|
354
|
+
out << "* #{binding.source_location.join(':')}"
|
355
|
+
out.vars('local variables', binding.local_variables) do |name|
|
356
|
+
binding.local_variable_get(name)
|
322
357
|
end
|
323
|
-
|
324
|
-
|
325
|
-
|
358
|
+
binding
|
359
|
+
end
|
360
|
+
|
361
|
+
def _thread_vars(out, thread)
|
362
|
+
out << " #{_thread_identifier(thread)}"
|
363
|
+
flv = thread.keys
|
364
|
+
out.vars('fiber-local variables', flv) { thread[_1] } unless flv.empty?
|
365
|
+
out.vars('thread variables', thread.thread_variables) do |name|
|
366
|
+
thread.thread_variable_get(name)
|
326
367
|
end
|
327
|
-
|
368
|
+
thread
|
328
369
|
end
|
329
370
|
|
330
|
-
def
|
331
|
-
|
332
|
-
|
333
|
-
|
334
|
-
|
335
|
-
|
371
|
+
def _fiber_vars(out, fiber)
|
372
|
+
if Fiber.current == fiber
|
373
|
+
storage = fiber.storage || {}
|
374
|
+
out.vars('fiber storage', storage.keys) { storage[_1] }
|
375
|
+
else
|
376
|
+
out << ' !!! given Fiber is not the current Fiber' <<
|
377
|
+
" #{fiber.inspect}"
|
336
378
|
end
|
337
|
-
|
338
|
-
|
339
|
-
|
379
|
+
fiber
|
380
|
+
end
|
381
|
+
|
382
|
+
def _instance_vars(out, object)
|
383
|
+
out.vars('instance variables', object.instance_variables) do |n|
|
384
|
+
object.instance_variable_get(n)
|
340
385
|
end
|
341
|
-
|
386
|
+
object
|
387
|
+
end
|
388
|
+
|
389
|
+
def _thread_identifier(thr)
|
390
|
+
state = thr.status
|
391
|
+
"#{state || (state.nil? ? 'aborted' : 'terminated')} Thread #{
|
392
|
+
defined?(thr.native_thread_id) ? thr.native_thread_id : thr.__id__
|
393
|
+
} #{thr.name}".rstrip
|
342
394
|
end
|
343
395
|
end
|
344
396
|
|
@@ -365,7 +417,7 @@ module ImLost
|
|
365
417
|
# # the timer with name 'my_test' is not longer valid now
|
366
418
|
#
|
367
419
|
#
|
368
|
-
# @example Use an anonymous timer
|
420
|
+
# @example Use an anonymous timer
|
369
421
|
# tmr = ImLost.timer.create
|
370
422
|
#
|
371
423
|
# # ...your code here...
|
@@ -397,12 +449,12 @@ module ImLost
|
|
397
449
|
def count = ids.size
|
398
450
|
|
399
451
|
# @attribute [r] empty?
|
400
|
-
# @return [Boolean]
|
452
|
+
# @return [Boolean] whether the timer store is empty or not
|
401
453
|
def empty? = ids.empty?
|
402
454
|
|
403
455
|
# @attribute [r] ids
|
404
456
|
# @return [Array<Integer>] IDs of all registered timers
|
405
|
-
def ids = (@ll.keys.keep_if {
|
457
|
+
def ids = (@ll.keys.keep_if { Integer === _1 })
|
406
458
|
|
407
459
|
#
|
408
460
|
# Create and register a new named or anonymous timer.
|
@@ -428,7 +480,7 @@ module ImLost
|
|
428
480
|
#
|
429
481
|
def delete(*id_or_names)
|
430
482
|
id_or_names.flatten.each do |id|
|
431
|
-
if id
|
483
|
+
if Integer === id
|
432
484
|
del = @ll.delete(id)
|
433
485
|
@ll.delete(del[0]) if del
|
434
486
|
else
|
@@ -440,7 +492,7 @@ module ImLost
|
|
440
492
|
end
|
441
493
|
|
442
494
|
#
|
443
|
-
# Print the ID or name and the runtime since timer was created.
|
495
|
+
# Print the ID or name and the runtime since a timer was created.
|
444
496
|
# It includes the location.
|
445
497
|
#
|
446
498
|
# @param id_or_name [Integer, #to_s] the identifier or the name of the timer
|
@@ -449,10 +501,10 @@ module ImLost
|
|
449
501
|
# identifier or name
|
450
502
|
#
|
451
503
|
def [](id_or_name)
|
452
|
-
|
453
|
-
timer = @ll[
|
504
|
+
now = self.class.now
|
505
|
+
timer = @ll[Integer === id_or_name ? id_or_name : id_or_name.to_s]
|
454
506
|
raise(ArgumentError, "not a timer - #{id_or_name.inspect}") unless timer
|
455
|
-
@cb[timer[0], Kernel.caller_locations(1, 1)[0],
|
507
|
+
@cb[timer[0], Kernel.caller_locations(1, 1)[0], (now - timer[1]).round(4)]
|
456
508
|
timer.__id__
|
457
509
|
end
|
458
510
|
|
@@ -469,87 +521,132 @@ module ImLost
|
|
469
521
|
nil
|
470
522
|
end
|
471
523
|
|
472
|
-
|
524
|
+
private
|
525
|
+
|
473
526
|
def initialize(&block)
|
474
527
|
@cb = block
|
475
528
|
@ll = {}
|
476
529
|
end
|
477
530
|
end
|
478
531
|
|
479
|
-
|
480
|
-
|
481
|
-
|
532
|
+
class LineStore
|
533
|
+
def initialize(*lines) = (@lines = lines)
|
534
|
+
def to_s = (@lines << nil).join("\n")
|
535
|
+
def <<(str) = @lines << str
|
536
|
+
def location(loc) = @lines << "* #{loc.path}:#{loc.lineno}"
|
482
537
|
|
483
|
-
|
484
|
-
|
485
|
-
|
486
|
-
|
487
|
-
@timer =
|
488
|
-
TimerStore.new do |title, location, time|
|
489
|
-
@output.puts(
|
490
|
-
"T #{title}: #{time ? "#{time} sec." : 'created'}",
|
491
|
-
" #{location.path}:#{location.lineno}"
|
492
|
-
)
|
538
|
+
def vars(kind, names)
|
539
|
+
return @lines << " <no #{kind} defined>" if names.empty?
|
540
|
+
@lines << " > #{kind}"
|
541
|
+
names.sort!.each { @lines << " #{_1}: #{yield(_1).inspect}" }
|
493
542
|
end
|
494
|
-
|
543
|
+
end
|
544
|
+
private_constant :LineStore
|
495
545
|
|
496
|
-
|
497
|
-
|
498
|
-
|
499
|
-
|
500
|
-
|
501
|
-
|
502
|
-
|
503
|
-
|
504
|
-
|
505
|
-
|
506
|
-
|
507
|
-
|
508
|
-
|
509
|
-
|
510
|
-
|
511
|
-
"#{ARG_SIG[kind]}#{ctx.local_variable_get(name).inspect}"
|
512
|
-
end
|
513
|
-
)
|
514
|
-
)
|
515
|
-
next unless @caller_locations
|
516
|
-
loc = ctx.eval('caller_locations(4,1)')[0]
|
517
|
-
@output.puts(" #{loc.path}:#{loc.lineno}")
|
518
|
-
end
|
519
|
-
]
|
520
|
-
|
521
|
-
@trace_results = [
|
522
|
-
TracePoint.new(:c_return) do |tp|
|
523
|
-
next if !@trace.key?(tp.self.__id__) || tp.path == __FILE__
|
524
|
-
@output.puts(as_sig('<', tp, tp.parameters.map { ARG_SIG[_1[0]] || '?' }))
|
525
|
-
@output.puts(" = #{tp.return_value.inspect}")
|
526
|
-
end,
|
527
|
-
TracePoint.new(:return) do |tp|
|
528
|
-
next if !@trace.key?(tp.self.__id__) || tp.path == __FILE__
|
529
|
-
ctx = tp.binding
|
530
|
-
@output.puts(
|
531
|
-
as_sig(
|
532
|
-
'<',
|
533
|
-
tp,
|
534
|
-
tp.parameters.map do |kind, name|
|
535
|
-
next name if NO_NAME.key?(name)
|
536
|
-
"#{ARG_SIG[kind]}#{ctx.local_variable_get(name).inspect}"
|
537
|
-
end
|
538
|
-
)
|
539
|
-
)
|
540
|
-
@output.puts(" = #{tp.return_value.inspect}")
|
546
|
+
class CCallLineStore < LineStore
|
547
|
+
def initialize(prefix, info, include_location)
|
548
|
+
@info = info
|
549
|
+
sig = "#{info.method_id}(#{args.join(', ')})"
|
550
|
+
case info.self
|
551
|
+
when Class, Module
|
552
|
+
super("#{prefix} #{info.self}.#{sig}")
|
553
|
+
else
|
554
|
+
super("#{prefix} #{info.defined_class}##{sig}")
|
555
|
+
end
|
556
|
+
return unless include_location
|
557
|
+
loc = location
|
558
|
+
path = loc.path
|
559
|
+
path = path[10..-2] if path.start_with?('<internal:')
|
560
|
+
@lines << " #{path}:#{loc.lineno}"
|
541
561
|
end
|
542
|
-
]
|
543
562
|
|
544
|
-
|
563
|
+
def location = @info
|
564
|
+
|
565
|
+
def args
|
566
|
+
argc = -1
|
567
|
+
@info.parameters.map do |kind, _|
|
568
|
+
argc += 1
|
569
|
+
CARG_TYPE[kind] || "arg#{argc}"
|
570
|
+
end
|
571
|
+
end
|
572
|
+
|
573
|
+
CARG_TYPE = {
|
574
|
+
rest: '*args',
|
575
|
+
keyrest: '**kwargs',
|
576
|
+
block: '&block'
|
577
|
+
}.compare_by_identity.freeze
|
578
|
+
end
|
579
|
+
private_constant :CCallLineStore
|
580
|
+
|
581
|
+
class RbCallLineStore < CCallLineStore
|
582
|
+
def initialize(prefix, info, include_location)
|
583
|
+
@ctx = info.binding
|
584
|
+
super
|
585
|
+
@ctx = nil
|
586
|
+
end
|
587
|
+
|
588
|
+
def location
|
589
|
+
locations = @ctx.eval('caller_locations(7,3)')
|
590
|
+
unless locations[1].path.start_with?('<internal:prelude')
|
591
|
+
return locations[1]
|
592
|
+
end
|
593
|
+
locations[0].path.start_with?('<internal:') ? locations[0] : locations[2]
|
594
|
+
end
|
595
|
+
|
596
|
+
def args
|
597
|
+
@info.parameters.map do |kind, name|
|
598
|
+
next name if ARG_SKIP.key?(name)
|
599
|
+
"#{ARG_TYPE[kind]}#{@ctx.local_variable_get(name).inspect}"
|
600
|
+
end
|
601
|
+
end
|
602
|
+
|
603
|
+
ARG_TYPE = { rest: :*, keyrest: :**, block: :& }.compare_by_identity.freeze
|
604
|
+
ARG_SKIP = ARG_TYPE.invert.compare_by_identity.freeze
|
605
|
+
end
|
606
|
+
private_constant :RbCallLineStore
|
607
|
+
|
608
|
+
@trace_calls =
|
609
|
+
TracePoint.new(:c_call, :call) do |tp|
|
610
|
+
next if !@trace.key?(tp.self) || tp.path == __FILE__
|
611
|
+
klass = tp.event == :c_call ? CCallLineStore : RbCallLineStore
|
612
|
+
@output << klass.new('>', tp, @caller_locations).to_s
|
613
|
+
end
|
614
|
+
|
615
|
+
@trace_results =
|
616
|
+
TracePoint.new(:c_return, :return) do |tp|
|
617
|
+
next if !@trace.key?(tp.self) || tp.path == __FILE__
|
618
|
+
klass = tp.event == :c_return ? CCallLineStore : RbCallLineStore
|
619
|
+
out = klass.new('<', tp, @caller_locations)
|
620
|
+
out << " = #{tp.return_value.inspect}"
|
621
|
+
@output << out.to_s
|
622
|
+
end
|
623
|
+
|
624
|
+
exception_support = RUBY_VERSION.to_f < 3.3 ? %i[raise] : %i[raise rescue]
|
545
625
|
@trace_exceptions =
|
546
|
-
TracePoint.new(*
|
547
|
-
ex = tp.raised_exception
|
548
|
-
|
549
|
-
|
550
|
-
)
|
551
|
-
|
626
|
+
TracePoint.new(*exception_support) do |tp|
|
627
|
+
ex = tp.raised_exception
|
628
|
+
mark, parent = tp.event == :rescue ? ['!', ex.cause] : 'x'
|
629
|
+
ex = ex.inspect
|
630
|
+
out = LineStore.new("#{mark} #{ex[0] == '#' ? ex[2..-2] : ex}")
|
631
|
+
while parent
|
632
|
+
ex = parent.inspect
|
633
|
+
out << " [#{ex[0] == '#' ? ex[2..-2] : ex}]"
|
634
|
+
parent = parent.cause
|
635
|
+
end
|
636
|
+
out << " #{tp.path}:#{tp.lineno}" if @exception_locations
|
637
|
+
@output << out.to_s
|
552
638
|
end
|
553
639
|
|
554
|
-
|
640
|
+
@timer = TimerStore.new { |title, location, time| @output << <<~TIMER_MSG }
|
641
|
+
T #{title}: #{time ? "#{time} sec." : 'created'}
|
642
|
+
#{location.path}:#{location.lineno}
|
643
|
+
TIMER_MSG
|
644
|
+
TimerStore.private_class_method(:new)
|
645
|
+
|
646
|
+
@fiber_support = !!defined?(Fiber.current.storage)
|
647
|
+
|
648
|
+
@output = STDERR
|
649
|
+
@trace = {}.compare_by_identity
|
650
|
+
@caller_locations = @exception_locations = true
|
651
|
+
self.trace_calls = self.trace_results = true
|
555
652
|
end
|
metadata
CHANGED
@@ -1,14 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: im-lost
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1
|
4
|
+
version: 1.2.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mike Blumtritt
|
8
|
-
autorequire:
|
9
8
|
bindir: bin
|
10
9
|
cert_chain: []
|
11
|
-
date:
|
10
|
+
date: 2025-01-30 00:00:00.000000000 Z
|
12
11
|
dependencies: []
|
13
12
|
description: |
|
14
13
|
If you have overlooked something again and don't really understand what
|
@@ -19,7 +18,6 @@ description: |
|
|
19
18
|
ImLost helps you by analyzing function calls of objects, informing you
|
20
19
|
about exceptions and logging your way through your code. In short, ImLost
|
21
20
|
is your debugging helper!
|
22
|
-
email:
|
23
21
|
executables: []
|
24
22
|
extensions: []
|
25
23
|
extra_rdoc_files:
|
@@ -42,7 +40,6 @@ metadata:
|
|
42
40
|
bug_tracker_uri: https://github.com/mblumtritt/im-lost/issues
|
43
41
|
documentation_uri: https://rubydoc.info/gems/im-lost
|
44
42
|
rubygems_mfa_required: 'true'
|
45
|
-
post_install_message:
|
46
43
|
rdoc_options: []
|
47
44
|
require_paths:
|
48
45
|
- lib
|
@@ -57,8 +54,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
57
54
|
- !ruby/object:Gem::Version
|
58
55
|
version: '0'
|
59
56
|
requirements: []
|
60
|
-
rubygems_version: 3.
|
61
|
-
signing_key:
|
57
|
+
rubygems_version: 3.6.3
|
62
58
|
specification_version: 4
|
63
59
|
summary: Your debugging helper.
|
64
60
|
test_files: []
|