File Coverage

blib/lib/Devel/MAT/Tool/Inrefs.pm
Criterion Covered Total %
statement 80 98 81.6
branch 24 36 66.6
condition 12 19 63.1
subroutine 16 20 80.0
pod 1 8 12.5
total 133 181 73.4


line stmt bran cond sub pod time code
1             # You may distribute under the terms of either the GNU General Public License
2             # or the Artistic License (the same terms as Perl itself)
3             #
4             # (C) Paul Evans, 2013-2017 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Tool::Inrefs 0.50;
7              
8 5     5   3737 use v5.14;
  5         19  
9 5     5   37 use warnings;
  5         10  
  5         141  
10 5     5   39 use base qw( Devel::MAT::Tool );
  5         21  
  5         532  
11              
12 5     5   37 use List::Util qw( any pairs );
  5         12  
  5         443  
13              
14             my %STRENGTH_TO_IDX = (
15             strong => 0,
16             weak => 1,
17             indirect => 2,
18             inferred => 3,
19             );
20             use constant {
21 5         5998 IDX_ROOTS_STRONG => 4,
22             IDX_ROOTS_WEAK => 5,
23             IDX_STACK => 6,
24 5     5   34 };
  5         10  
25              
26             =head1 NAME
27              
28             C - annotate which SVs are referred to by others
29              
30             =head1 DESCRIPTION
31              
32             This C tool annotates each SV with back-references from other SVs
33             that refer to it. It follows the C method of every heap SV and
34             annotates the referred SVs with back-references pointing back to the SVs that
35             refer to them.
36              
37             =cut
38              
39             sub init_tool
40             {
41 2     2 1 4 my $self = shift;
42              
43 2         12 my $df = $self->df;
44              
45 2         15 my $heap_total = scalar $df->heap;
46 2         4 my $count = 0;
47 2         7 foreach my $sv ( $df->heap ) {
48 163780         563241 foreach ( pairs $sv->outrefs( "NO_DESC" ) ) {
49 295670         526462 my ( $strength, $refsv ) = @$_;
50              
51 295670 100       594054 push @{ $refsv->{tool_inrefs}[ $STRENGTH_TO_IDX{ $strength } ] }, $sv->addr if !$refsv->immortal;
  293272         1243528  
52             }
53              
54 163780         308028 $count++;
55 163780 100       350300 $self->report_progress( sprintf "Patching refs in %d of %d (%.2f%%)",
56             $count, $heap_total, 100*$count / $heap_total ) if ($count % 10000) == 0
57             }
58              
59             # Most SVs are not roots or on the stack. To save time later on we'll make
60             # a note of those rare ones that are
61              
62 2         17682 foreach ( pairs $df->roots_strong ) {
63 118         160 my ( undef, $sv ) = @$_;
64 118 100       201 next unless $sv;
65 60         143 $sv->{tool_inrefs}[IDX_ROOTS_STRONG]++;
66             }
67              
68 2         48 foreach ( pairs $df->roots_weak ) {
69 14         26 my ( undef, $sv ) = @$_;
70 14 100       31 next unless $sv;
71 8         43 $sv->{tool_inrefs}[IDX_ROOTS_WEAK]++;
72             }
73              
74 2         24 foreach my $sv ( $df->stack ) {
75 4         14 $sv->{tool_inrefs}[IDX_STACK]++;
76             }
77              
78 2         16 $self->report_progress();
79             }
80              
81             =head1 SV METHODS
82              
83             This tool adds the following SV methods.
84              
85             =head2 inrefs
86              
87             @refs = $sv->inrefs
88              
89             Returns a list of Reference objects for each of the SVs that refer to this
90             one. This is formed by the inverse mapping along the SV graph from C.
91              
92             =head2 inrefs_strong
93              
94             =head2 inrefs_weak
95              
96             =head2 inrefs_direct
97              
98             =head2 inrefs_indirect
99              
100             =head2 inrefs_inferred
101              
102             @refs = $sv->inrefs_strong
103              
104             @refs = $sv->inrefs_weak
105              
106             @refs = $sv->inrefs_direct
107              
108             @refs = $sv->inrefs_indirect
109              
110             @refs = $sv->inrefs_inferred
111              
112             Returns lists of Reference objects filtered by type, analogous to the various
113             C methods.
114              
115             =cut
116              
117             sub Devel::MAT::SV::_inrefs
118             {
119 3394     3394   8234 my $self = shift;
120 3394         12296 my ( @strengths ) = @_;
121              
122             # In scalar context we don't need to return SVs or Reference instances,
123             # just count them. This allows a lot of optimisations.
124 3394         8137 my $just_count = !wantarray;
125              
126 3394   100     15979 $self->{tool_inrefs} ||= [];
127              
128 3394         10989 my $df = $self->df;
129 3394         6783 my @inrefs;
130 3394         7361 foreach my $strength ( @strengths ) {
131 13515         2890112 my %seen;
132 13515   100     22010 foreach my $addr ( @{ $self->{tool_inrefs}[ $STRENGTH_TO_IDX{$strength} ] // [] } ) {
  13515         86943  
133 16851 50       1130996 if( $just_count ) {
134 0         0 push @inrefs, 1;
135             }
136             else {
137 16851 100       100383 $seen{$addr}++ and next;
138              
139 16850         59243 my $sv = $df->sv_at( $addr );
140              
141             push @inrefs, Devel::MAT::SV::Reference( $_->name, $_->strength, $sv )
142 16850 100       66776 for grep { $_->strength eq $strength and $_->sv == $self } $sv->outrefs;
  11954093         100681185  
143             }
144             }
145             }
146              
147 3394 100 66     19060 if( $self->{tool_inrefs}[IDX_ROOTS_STRONG] and $strengths[0] eq "strong" ) {
148 4 50       18 if( $just_count ) {
149 0         0 push @inrefs, ( 1 ) x $self->{tool_inrefs}[IDX_ROOTS_STRONG];
150             }
151             else {
152 4         38 foreach ( pairs $df->roots_strong ) {
153 236         386 my ( $name, $sv ) = @$_;
154 236 100 100     645 push @inrefs, Devel::MAT::SV::Reference( $name, strong => undef )
155             if defined $sv and $sv == $self;
156             }
157             }
158             }
159              
160 3394 50 33 0   15528 if( $self->{tool_inrefs}[IDX_ROOTS_WEAK] and any { $_ eq "weak" } @strengths ) {
  0         0  
161 0 0       0 if( $just_count ) {
162 0         0 push @inrefs, ( 1 ) x $self->{tool_inrefs}[IDX_ROOTS_WEAK];
163             }
164             else {
165 0         0 foreach ( pairs $df->roots_weak ) {
166 0         0 my ( $name, $sv ) = @$_;
167 0 0 0     0 push @inrefs, Devel::MAT::SV::Reference( $name, weak => undef )
168             if defined $sv and $sv == $self;
169             }
170             }
171             }
172              
173 3394 100 66 2   13464 if( $self->{tool_inrefs}[IDX_STACK] and any { $_ eq "weak" } @strengths ) {
  2         40  
174 1 50       4 if( $just_count ) {
175 0         0 push @inrefs, ( 1 ) x $self->{tool_inrefs}[IDX_STACK];
176             }
177             else {
178 1         8 foreach my $stacksv ( $df->stack ) {
179 2 100       17 next unless $stacksv->addr == $self->addr;
180              
181 1         4 push @inrefs, Devel::MAT::SV::Reference( "a value on the stack", strong => undef );
182             }
183             }
184             }
185              
186 3394         24515 return @inrefs;
187             }
188              
189             # If 'strong' is included in these lists it must be first
190 3373     3373 0 29510 sub Devel::MAT::SV::inrefs { shift->_inrefs( qw( strong weak indirect inferred )) }
191 18     18 0 58 sub Devel::MAT::SV::inrefs_strong { shift->_inrefs( qw( strong )) }
192 0     0 0 0 sub Devel::MAT::SV::inrefs_weak { shift->_inrefs( qw( weak )) }
193 2     2 0 28 sub Devel::MAT::SV::inrefs_direct { shift->_inrefs( qw( strong weak )) }
194 1     1 0 687 sub Devel::MAT::SV::inrefs_indirect { shift->_inrefs( qw( indirect )) }
195 0     0 0   sub Devel::MAT::SV::inrefs_inferred { shift->_inrefs( qw( inferred )) }
196              
197             =head1 COMANDS
198              
199             =cut
200              
201             =head2 inrefs
202              
203             pmat> inrefs defstash
204             s the hash GLOB(%*) at 0x556e47243e40
205              
206             Shows the incoming references that refer to a given SV.
207              
208             Takes the following named options:
209              
210             =over 4
211              
212             =item --weak
213              
214             Include weak direct references in the output (by default only strong direct
215             ones will be included).
216              
217             =item --all
218              
219             Include both weak and indirect references in the output.
220              
221             =back
222              
223             =cut
224              
225 5     5   40 use constant CMD => "inrefs";
  5         15  
  5         307  
226 5     5   32 use constant CMD_DESC => "Show incoming references to a given SV";
  5         12  
  5         389  
227              
228 5         389 use constant CMD_OPTS => (
229             weak => { help => "include weak references" },
230             all => { help => "include weak and indirect references",
231             alias => "a" },
232 5     5   44 );
  5         17  
233              
234 5     5   39 use constant CMD_ARGS_SV => 1;
  5         9  
  5         830  
235              
236             sub run
237             {
238 0     0 0   my $self = shift;
239 0           my %opts = %{ +shift };
  0            
240 0           my ( $sv ) = @_;
241              
242             my $method = $opts{all} ? "inrefs" :
243 0 0         $opts{weak} ? "inrefs_direct" :
    0          
244             "inrefs_strong";
245              
246 0           require Devel::MAT::Tool::Outrefs;
247 0           Devel::MAT::Tool::Outrefs->show_refs_by_method( $method, $sv );
248             }
249              
250             =head1 AUTHOR
251              
252             Paul Evans
253              
254             =cut
255              
256             0x55AA;