File Coverage

blib/lib/Devel/Events/Handler/ObjectTracker.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             #!/usr/bin/perl
2              
3             package Devel::Events::Handler::ObjectTracker;
4 1     1   39225 use Moose;
  0            
  0            
5              
6             with qw/Devel::Events::Handler/;
7              
8             use Scalar::Util qw/refaddr weaken/;
9             use Tie::RefHash::Weak;
10              
11             has live_objects => (
12             isa => "HashRef",
13             is => "ro",
14             default => sub {
15             tie my %hash, 'Tie::RefHash::Weak';
16             \%hash;
17             },
18             );
19              
20             has object_to_class => (
21             isa => "HashRef",
22             is => "ro",
23             default => sub { +{} },
24             );
25              
26             has class_counters => (
27             isa => "HashRef",
28             is => "ro",
29             default => sub { +{} },
30             );
31              
32             sub new_event {
33             my ( $self, $type, @data ) = @_;
34              
35             if ( $self->can( my $method = "handle_$type" ) ) { # FIXME pattern match? i want erlang =)
36             $self->$method( @data );
37             }
38             }
39              
40             sub handle_object_bless {
41             my ( $self, %args ) = @_;
42              
43             return unless $args{tracked}; # don't keep track of objects that can't be garbage collected (shared code refs for instance)
44              
45             my $object = $args{object};
46             my $class = $args{class};
47              
48             my $class_counters = $self->class_counters;
49              
50             $class_counters->{$class}++;
51              
52             if ( defined(my $old_class = $args{old_class}) ) {
53             # rebless
54             $class_counters->{$old_class}--;
55             } else {
56             # new object
57             my $entry = $self->event_to_entry( %args );
58             ( tied %{ $self->live_objects } )->STORE( $object, $entry ); # FIXME hash access triggers overload +0
59             }
60              
61             # we need this because in object_destroy it's not blessed anymore
62             #( tied %{ $self->object_to_class } )->STORE( $object, $class );
63             $self->object_to_class->{refaddr($object)} = $class;
64             }
65              
66             sub event_to_entry {
67             my ( $self, %entry ) = @_;
68              
69             weaken($entry{object});
70              
71             return \%entry;
72             }
73              
74             sub handle_object_destroy {
75             my ( $self, %args ) = @_;
76            
77             my $object = $args{object};
78              
79             if ( defined( my $class = delete($self->object_to_class->{refaddr($object)}) ) ) {
80             $self->class_counters->{$class}--;
81             }
82             }
83              
84             __PACKAGE__;
85              
86             __END__
87              
88             =pod
89              
90             =head1 NAME
91              
92             Devel::Events::Handler::ObjectTracker - A L<Devel::Events> that tracks leaks
93              
94             =head1 SYNOPSIS
95              
96             use Devel::Events::Handler::ObjectTracker;
97             use Devel::Events::Generator::Objects;
98              
99             my $tracker = Devel::Events::Handler::ObjectTracker->new();
100              
101             my $gen = Devel::Events::Generator::Objects->new(
102             handler => $tracker,
103             );
104              
105             $gen->enable(); # start generating events
106              
107             $code->();
108              
109             $gen->disable();
110              
111             use Data::Dumper;
112             warn Dumper($tracker->live_objects);
113              
114             =head1 DESCRIPTION
115              
116             This object will keep track of every object created and every object destroyed
117             based on the C<object_bless> and C<object_destroy> events. Reblessing is
118             accounted for.
119              
120             This handler doesn't perform any magical stuff,
121             L<Devel::Events::Generator::Objects> is responsible for raising the proper
122             events.
123              
124             =head1 ATTRIBUTES
125              
126             =over 4
127              
128             =item live_objects
129              
130             A L<Tie::RefHash::Weak> hash that keeps an index of every live object and the
131             C<object_bless> event that created it.
132              
133             =item class_counters
134              
135             Keeps a count of the live instances per class, much like
136             L<Devel::Leak::Object>.
137              
138             =item object_to_class
139              
140             USed to maintain the C<class_counters> hash.
141              
142             =back
143              
144             =head1 METHODS
145              
146             =over 4
147              
148             =item new_event @event
149              
150             Delegates to C<handle_object_bless> or C<handle_object_destroy>
151              
152             =item handle_object_bless @event
153              
154             Adds an entry in the C<live_objects> table.
155              
156             =item event_to_entry @event
157              
158             Munges event data into an entry for the C<live_objects> table.
159              
160             =item handle_object_destroy
161              
162             Decrements the C<class_counters> counter.
163              
164             =back
165              
166             =cut
167              
168