im-lost 1.2.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 +1 -1
- data/lib/im-lost/version.rb +1 -1
- data/lib/im-lost.rb +115 -115
- 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|
|
data/lib/im-lost/version.rb
CHANGED
data/lib/im-lost.rb
CHANGED
@@ -54,7 +54,7 @@ module ImLost
|
|
54
54
|
return @output = value if defined?(value.<<)
|
55
55
|
raise(
|
56
56
|
NoMethodError,
|
57
|
-
"undefined method
|
57
|
+
"undefined method '<<' for an instance of #{
|
58
58
|
Kernel.instance_method(:class).bind(value).call
|
59
59
|
}"
|
60
60
|
)
|
@@ -73,18 +73,18 @@ module ImLost
|
|
73
73
|
# @attribute [r] trace_calls
|
74
74
|
# @return [Boolean] whether method calls will be traced
|
75
75
|
#
|
76
|
-
def trace_calls = @trace_calls
|
76
|
+
def trace_calls = @trace_calls.enabled?
|
77
77
|
|
78
78
|
def trace_calls=(value)
|
79
79
|
if value
|
80
|
-
@trace_calls.
|
80
|
+
@trace_calls.enable unless trace_calls
|
81
81
|
elsif trace_calls
|
82
|
-
@trace_calls.
|
82
|
+
@trace_calls.disable
|
83
83
|
end
|
84
84
|
end
|
85
85
|
|
86
86
|
#
|
87
|
-
# Traces
|
87
|
+
# Traces exceptions raised within a given block.
|
88
88
|
#
|
89
89
|
# @example Trace exception and rescue handling
|
90
90
|
# ImLost.trace_exceptions do
|
@@ -101,7 +101,7 @@ module ImLost
|
|
101
101
|
# # x RuntimeError: something went wrong!
|
102
102
|
# # /examples/test.rb:4
|
103
103
|
#
|
104
|
-
# @param with_locations [Boolean]
|
104
|
+
# @param with_locations [Boolean] whether the locations should be included
|
105
105
|
# into the exception trace information
|
106
106
|
# @yieldreturn [Object] return result
|
107
107
|
#
|
@@ -118,19 +118,19 @@ module ImLost
|
|
118
118
|
end
|
119
119
|
|
120
120
|
#
|
121
|
-
# Enables/disables tracing of returned
|
121
|
+
# Enables/disables tracing of returned values of method calls.
|
122
122
|
# This is enabled by default.
|
123
123
|
#
|
124
124
|
# @attribute [r] trace_results
|
125
125
|
# @return [Boolean] whether return values will be traced
|
126
126
|
#
|
127
|
-
def trace_results = @trace_results
|
127
|
+
def trace_results = @trace_results.enabled?
|
128
128
|
|
129
129
|
def trace_results=(value)
|
130
130
|
if value
|
131
|
-
@trace_results.
|
131
|
+
@trace_results.enable unless trace_results
|
132
132
|
elsif trace_results
|
133
|
-
@trace_results.
|
133
|
+
@trace_results.disable
|
134
134
|
end
|
135
135
|
end
|
136
136
|
|
@@ -259,7 +259,7 @@ module ImLost
|
|
259
259
|
#
|
260
260
|
# Inspect internal variables of a given object.
|
261
261
|
#
|
262
|
-
# @note The
|
262
|
+
# @note The dedicated handling of `Fiber` is platform dependent!
|
263
263
|
#
|
264
264
|
# @example Inspect current instance variables
|
265
265
|
# @a = 22
|
@@ -303,19 +303,18 @@ module ImLost
|
|
303
303
|
# @return [Object] the given object
|
304
304
|
#
|
305
305
|
def vars(object)
|
306
|
-
out =
|
306
|
+
out = LineStore.new
|
307
307
|
traced = @trace.delete(object)
|
308
308
|
return _local_vars(out, object) if Binding === object
|
309
|
-
location
|
310
|
-
out << "* #{location.path}:#{location.lineno}"
|
309
|
+
out.location(Kernel.caller_locations(1, 1)[0])
|
311
310
|
return _thread_vars(out, object) if Thread === object
|
312
|
-
return _fiber_vars(out, object) if @
|
311
|
+
return _fiber_vars(out, object) if @fiber_support && Fiber === object
|
313
312
|
return _instance_vars(out, object) if defined?(object.instance_variables)
|
314
313
|
out << ' !!! unable to retrieve vars'
|
315
314
|
object
|
316
315
|
ensure
|
317
316
|
@trace[traced] = traced if traced
|
318
|
-
out.
|
317
|
+
@output << out.to_s
|
319
318
|
end
|
320
319
|
|
321
320
|
private
|
@@ -329,10 +328,7 @@ module ImLost
|
|
329
328
|
arg
|
330
329
|
end
|
331
330
|
|
332
|
-
def _trace_all(args)
|
333
|
-
args.each { |arg| @trace[arg] = arg if _can_trace?(arg) }
|
334
|
-
args
|
335
|
-
end
|
331
|
+
def _trace_all(args) = args.each { @trace[_1] = _1 if _can_trace?(_1) }
|
336
332
|
|
337
333
|
def _trace_b(arg)
|
338
334
|
return yield(arg) if @trace.key?(arg) || !_can_trace?(arg)
|
@@ -390,14 +386,11 @@ module ImLost
|
|
390
386
|
object
|
391
387
|
end
|
392
388
|
|
393
|
-
def _thread_identifier(
|
394
|
-
|
395
|
-
|
396
|
-
|
397
|
-
|
398
|
-
thread.__id__
|
399
|
-
end
|
400
|
-
} #{thread.name}".rstrip
|
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
|
401
394
|
end
|
402
395
|
end
|
403
396
|
|
@@ -456,12 +449,12 @@ module ImLost
|
|
456
449
|
def count = ids.size
|
457
450
|
|
458
451
|
# @attribute [r] empty?
|
459
|
-
# @return [Boolean]
|
452
|
+
# @return [Boolean] whether the timer store is empty or not
|
460
453
|
def empty? = ids.empty?
|
461
454
|
|
462
455
|
# @attribute [r] ids
|
463
456
|
# @return [Array<Integer>] IDs of all registered timers
|
464
|
-
def ids = (@ll.keys.keep_if {
|
457
|
+
def ids = (@ll.keys.keep_if { Integer === _1 })
|
465
458
|
|
466
459
|
#
|
467
460
|
# Create and register a new named or anonymous timer.
|
@@ -487,7 +480,7 @@ module ImLost
|
|
487
480
|
#
|
488
481
|
def delete(*id_or_names)
|
489
482
|
id_or_names.flatten.each do |id|
|
490
|
-
if id
|
483
|
+
if Integer === id
|
491
484
|
del = @ll.delete(id)
|
492
485
|
@ll.delete(del[0]) if del
|
493
486
|
else
|
@@ -508,10 +501,10 @@ module ImLost
|
|
508
501
|
# identifier or name
|
509
502
|
#
|
510
503
|
def [](id_or_name)
|
511
|
-
|
512
|
-
timer = @ll[
|
504
|
+
now = self.class.now
|
505
|
+
timer = @ll[Integer === id_or_name ? id_or_name : id_or_name.to_s]
|
513
506
|
raise(ArgumentError, "not a timer - #{id_or_name.inspect}") unless timer
|
514
|
-
@cb[timer[0], Kernel.caller_locations(1, 1)[0],
|
507
|
+
@cb[timer[0], Kernel.caller_locations(1, 1)[0], (now - timer[1]).round(4)]
|
515
508
|
timer.__id__
|
516
509
|
end
|
517
510
|
|
@@ -528,125 +521,132 @@ module ImLost
|
|
528
521
|
nil
|
529
522
|
end
|
530
523
|
|
531
|
-
|
524
|
+
private
|
525
|
+
|
532
526
|
def initialize(&block)
|
533
527
|
@cb = block
|
534
528
|
@ll = {}
|
535
529
|
end
|
536
530
|
end
|
537
531
|
|
538
|
-
class
|
532
|
+
class LineStore
|
539
533
|
def initialize(*lines) = (@lines = lines)
|
534
|
+
def to_s = (@lines << nil).join("\n")
|
540
535
|
def <<(str) = @lines << str
|
541
|
-
def location(loc) = @lines << "
|
542
|
-
|
536
|
+
def location(loc) = @lines << "* #{loc.path}:#{loc.lineno}"
|
537
|
+
|
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}" }
|
542
|
+
end
|
543
|
+
end
|
544
|
+
private_constant :LineStore
|
543
545
|
|
544
|
-
|
545
|
-
|
546
|
-
@
|
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
|
547
551
|
when Class, Module
|
548
|
-
"#{prefix} #{info.self}.#{
|
552
|
+
super("#{prefix} #{info.self}.#{sig}")
|
549
553
|
else
|
550
|
-
"#{prefix} #{info.defined_class}##{
|
554
|
+
super("#{prefix} #{info.defined_class}##{sig}")
|
551
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}"
|
552
561
|
end
|
553
562
|
|
554
|
-
def
|
555
|
-
|
556
|
-
|
557
|
-
|
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
|
558
571
|
end
|
572
|
+
|
573
|
+
CARG_TYPE = {
|
574
|
+
rest: '*args',
|
575
|
+
keyrest: '**kwargs',
|
576
|
+
block: '&block'
|
577
|
+
}.compare_by_identity.freeze
|
559
578
|
end
|
560
|
-
private_constant :
|
579
|
+
private_constant :CCallLineStore
|
561
580
|
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
|
567
|
-
|
568
|
-
private_constant :ARG_SIG, :NO_NAME, :THREAD_STATE
|
581
|
+
class RbCallLineStore < CCallLineStore
|
582
|
+
def initialize(prefix, info, include_location)
|
583
|
+
@ctx = info.binding
|
584
|
+
super
|
585
|
+
@ctx = nil
|
586
|
+
end
|
569
587
|
|
570
|
-
|
571
|
-
|
572
|
-
|
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
|
573
595
|
|
574
|
-
|
575
|
-
|
576
|
-
|
577
|
-
|
578
|
-
|
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
|
579
602
|
|
580
|
-
|
581
|
-
|
582
|
-
|
583
|
-
|
584
|
-
|
585
|
-
|
586
|
-
|
587
|
-
end,
|
588
|
-
TracePoint.new(:call) do |tp|
|
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|
|
589
610
|
next if !@trace.key?(tp.self) || tp.path == __FILE__
|
590
|
-
|
591
|
-
|
592
|
-
out.sig(
|
593
|
-
'>',
|
594
|
-
tp,
|
595
|
-
tp.parameters.map do |kind, name|
|
596
|
-
next name if NO_NAME.key?(name)
|
597
|
-
"#{ARG_SIG[kind]}#{ctx.local_variable_get(name).inspect}"
|
598
|
-
end
|
599
|
-
)
|
600
|
-
out.location(ctx.eval('caller_locations(4,1)')[0]) if @caller_locations
|
601
|
-
out.flush(@output)
|
611
|
+
klass = tp.event == :c_call ? CCallLineStore : RbCallLineStore
|
612
|
+
@output << klass.new('>', tp, @caller_locations).to_s
|
602
613
|
end
|
603
|
-
]
|
604
614
|
|
605
|
-
@trace_results =
|
606
|
-
TracePoint.new(:c_return) do |tp|
|
615
|
+
@trace_results =
|
616
|
+
TracePoint.new(:c_return, :return) do |tp|
|
607
617
|
next if !@trace.key?(tp.self) || tp.path == __FILE__
|
608
|
-
|
609
|
-
out.
|
610
|
-
out.location(tp) if @caller_locations
|
618
|
+
klass = tp.event == :c_return ? CCallLineStore : RbCallLineStore
|
619
|
+
out = klass.new('<', tp, @caller_locations)
|
611
620
|
out << " = #{tp.return_value.inspect}"
|
612
|
-
out.
|
613
|
-
end,
|
614
|
-
TracePoint.new(:return) do |tp|
|
615
|
-
next if !@trace.key?(tp.self) || tp.path == __FILE__
|
616
|
-
ctx = tp.binding
|
617
|
-
out = Out.new
|
618
|
-
out.sig(
|
619
|
-
'<',
|
620
|
-
tp,
|
621
|
-
tp.parameters.map do |kind, name|
|
622
|
-
next name if NO_NAME.key?(name)
|
623
|
-
"#{ARG_SIG[kind]}#{ctx.local_variable_get(name).inspect}"
|
624
|
-
end
|
625
|
-
)
|
626
|
-
out.location(ctx.eval('caller_locations(4,1)')[0]) if @caller_locations
|
627
|
-
out << " = #{tp.return_value.inspect}"
|
628
|
-
out.flush(@output)
|
621
|
+
@output << out.to_s
|
629
622
|
end
|
630
|
-
]
|
631
623
|
|
632
|
-
|
624
|
+
exception_support = RUBY_VERSION.to_f < 3.3 ? %i[raise] : %i[raise rescue]
|
633
625
|
@trace_exceptions =
|
634
|
-
TracePoint.new(*
|
626
|
+
TracePoint.new(*exception_support) do |tp|
|
635
627
|
ex = tp.raised_exception
|
636
628
|
mark, parent = tp.event == :rescue ? ['!', ex.cause] : 'x'
|
637
629
|
ex = ex.inspect
|
638
|
-
out =
|
630
|
+
out = LineStore.new("#{mark} #{ex[0] == '#' ? ex[2..-2] : ex}")
|
639
631
|
while parent
|
640
632
|
ex = parent.inspect
|
641
633
|
out << " [#{ex[0] == '#' ? ex[2..-2] : ex}]"
|
642
634
|
parent = parent.cause
|
643
635
|
end
|
644
|
-
out.
|
645
|
-
out.
|
636
|
+
out << " #{tp.path}:#{tp.lineno}" if @exception_locations
|
637
|
+
@output << out.to_s
|
646
638
|
end
|
647
639
|
|
648
|
-
@
|
649
|
-
|
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)
|
650
647
|
|
648
|
+
@output = STDERR
|
649
|
+
@trace = {}.compare_by_identity
|
650
|
+
@caller_locations = @exception_locations = true
|
651
651
|
self.trace_calls = self.trace_results = true
|
652
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.2.
|
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: []
|