File Coverage

blib/lib/Devel/MAT/Tool/Count.pm
Criterion Covered Total %
statement 26 92 28.2
branch 0 28 0.0
condition 0 25 0.0
subroutine 9 15 60.0
pod 0 2 0.0
total 35 162 21.6


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-2018 -- leonerd@leonerd.org.uk
5              
6             package Devel::MAT::Tool::Count 0.49;
7              
8 5     5   3242 use v5.14;
  5         17  
9 5     5   28 use warnings;
  5         11  
  5         158  
10 5     5   25 use base qw( Devel::MAT::Tool );
  5         8  
  5         433  
11              
12 5     5   27 use constant CMD => "count";
  5         13  
  5         277  
13 5     5   25 use constant CMD_DESC => "Count the various kinds of SV";
  5         16  
  5         249  
14              
15 5     5   28 use List::Util qw( sum );
  5         15  
  5         320  
16 5     5   26 use List::UtilsBy qw( rev_nsort_by );
  5         9  
  5         246  
17 5     5   31 use Struct::Dumb;
  5         8  
  5         47  
18              
19             =head1 NAME
20              
21             C - count the various kinds of SV
22              
23             =head1 DESCRIPTION
24              
25             This C tool counts the different kinds of SV in the heap.
26              
27             =cut
28              
29             =head1 COMMANDS
30              
31             =head2 count
32              
33             pmat> count
34             Kind Count (blessed) Bytes (blessed)
35             ARRAY 170 0 15.1 KiB
36             CODE 166 0 20.8 KiB
37              
38             Prints a summary of the count of each type of object.
39              
40             Takes the following named options:
41              
42             =over 4
43              
44             =item --blessed, -b
45              
46             Additionally classify blessed references per package
47              
48             =item --scalars, -S
49              
50             Additionally classify SCALAR SVs according to which fields they have present
51              
52             =item --struct
53              
54             Use the structural size to sum byte counts
55              
56             =item --owned
57              
58             Use the owned size to sum byte counts
59              
60             =back
61              
62             =cut
63              
64 5         4998 use constant CMD_OPTS => (
65             blessed => { help => "classify blessed references per package",
66             alias => "b" },
67             scalars => { help => "classify SCALARs according to present fields",
68             alias => "S" },
69             struct => { help => "sum SVs by structural size" },
70             owned => { help => "sum SVs by owned size" },
71 5     5   471 );
  5         20  
72              
73             struct Counts => [qw( svs bytes blessed_svs blessed_bytes )];
74              
75             sub run
76             {
77 0     0 0   my $self = shift;
78              
79 0           $self->count_svs( %{ +shift } );
  0            
80             }
81              
82             sub count_svs
83             {
84 0     0 0   my $self = shift;
85 0           my %opts = @_;
86              
87             # TODO: consider options for
88             # sorting
89             # filtering
90              
91             my $size_meth = $opts{owned} ? "owned_size" :
92 0 0         $opts{struct} ? "structure_size" :
    0          
93             "size";
94              
95             # Options for bin/pmat-counts
96             my $emit_count = $opts{emit_count} //
97 0 0 0 0     sub { ( !$_[1] || $_[2] ) ? $_[2] : "" };
  0   0        
98             my $emit_bytes = $opts{emit_bytes} //
99 0 0 0 0     sub { ( !$_[1] || $_[2] ) ? Devel::MAT::Cmd->format_bytes( $_[2] ) : "" };
  0   0        
100              
101 0           my %counts;
102             my %counts_SCALAR;
103 0           my %counts_per_package;
104              
105 0           foreach my $sv ( $self->df->heap ) {
106 0   0       my $c = $counts{ref $sv} //= Counts( ( 0 ) x 4 );
107 0           my $bytes = $sv->$size_meth;
108              
109 0           $c->svs++;
110 0           $c->bytes += $bytes;
111              
112 0 0         if( $sv->blessed ) {
113 0           $c->blessed_svs++;
114 0           $c->blessed_bytes += $bytes;
115             }
116              
117 0 0 0       if( $opts{scalars} and $sv->isa( "Devel::MAT::SV::SCALAR" ) ) {
118 0           my $desc = $sv->desc;
119              
120 0   0       $c = $counts_SCALAR{$desc} //= Counts( ( 0 ) x 4 );
121              
122 0           $c->svs++;
123 0           $c->bytes += $bytes;
124              
125 0 0         if( $sv->blessed ) {
126 0           $c->blessed_svs++;
127 0           $c->blessed_bytes += $bytes;
128             }
129             }
130              
131 0 0         $opts{blessed} or next;
132              
133 0 0         if( $sv->blessed ) {
134 0   0       $c = $counts_per_package{ref $sv}{ $sv->blessed->stashname } //= Counts( ( 0 ) x 4 );
135 0           $c->blessed_svs++;
136 0           $c->blessed_bytes += $bytes;
137             }
138             }
139              
140 0           my @table;
141              
142 0           foreach ( sort keys %counts ) {
143 0           my $kind = $_ =~ s/^Devel::MAT::SV:://r;
144 0           my $c = $counts{$_};
145              
146 0           push @table, [ $kind,
147             $emit_count->( $kind, 0, $c->svs ),
148             $emit_count->( $kind, 1, $c->blessed_svs ),
149             $emit_bytes->( $kind, 0, $c->bytes ),
150             $emit_bytes->( $kind, 1, $c->blessed_bytes ) ];
151              
152 0 0         push @table, _gen_package_breakdown( $counts_per_package{$_}, $emit_count, $emit_bytes ) if $opts{blessed};
153              
154 0 0 0       if( $kind eq "SCALAR" and $opts{scalars} ) {
155 0           foreach ( sort keys %counts_SCALAR ) {
156 0           my $c = $counts_SCALAR{$_};
157              
158 0           push @table, [ " $_",
159             $emit_count->( $_, 0, $c->svs ),
160             $emit_count->( $_, 1, $c->blessed_svs ),
161             $emit_bytes->( $_, 0, $c->bytes ),
162             $emit_bytes->( $_, 1, $c->blessed_bytes ) ];
163             }
164             }
165             }
166              
167 0           push @table, []; # HR
168              
169 0           my $total = Counts( ( 0 ) x 4 );
170 0           foreach my $method (qw( svs bytes blessed_svs blessed_bytes )) {
171 0           $total->$method = sum map { $_->$method } values %counts;
  0            
172             }
173              
174 0           push @table, [ "(total)",
175             $emit_count->( "(total)", 0, $total->svs ),
176             $emit_count->( "(total)", 1, $total->blessed_svs ),
177             $emit_bytes->( "(total)", 0, $total->bytes ),
178             $emit_bytes->( "(total)", 1, $total->blessed_bytes ) ];
179              
180             Devel::MAT::Cmd->print_table( \@table,
181             indent => 2,
182             headings => [ "Kind", "Count", "(blessed)", "Bytes", "(blessed)" ],
183             sep => [ " ", " ", " ", " " ],
184             align => [ undef, "right", "right", "right", "right" ],
185 0 0         %{ $opts{table_args} || {} },
  0            
186             );
187             }
188              
189             sub _gen_package_breakdown
190             {
191 0     0     my ( $counts, $emit_count, $emit_bytes ) = @_;
192              
193 0     0     my @packages = rev_nsort_by { $counts->{$_}->blessed_svs } sort keys %$counts;
  0            
194              
195 0           my @ret;
196              
197             my $count;
198 0           while( @packages ) {
199 0           my $package = shift @packages;
200              
201             push @ret,
202             [
203             " " . Devel::MAT::Cmd->format_symbol( $package ),
204             $emit_count->( $package, 0, 0 ),
205             $emit_count->( $package, 1, $counts->{$package}->blessed_svs ),
206             $emit_bytes->( $package, 0, 0 ),
207 0           $emit_bytes->( $package, 1, $counts->{$package}->blessed_bytes ),
208             ];
209              
210 0           $count++;
211 0 0         last if $count >= 10;
212             }
213              
214 0           my $remaining = Counts( ( 0 ) x 4 );
215 0           foreach my $method (qw( blessed_svs blessed_bytes )) {
216 0           $remaining->$method = sum map { $counts->{$_}->$method } @packages;
  0            
217             }
218              
219 0 0         push @ret,
220             [ " " . Devel::MAT::Cmd->format_note( "(others)" ),
221             $emit_count->( "(others)", 0, 0 ),
222             $emit_count->( "(others)", 1, $remaining->blessed_svs ),
223             $emit_bytes->( "(others)", 0, 0 ),
224             $emit_bytes->( "(others)", 1, $remaining->blessed_bytes ),
225             ] if @packages;
226              
227 0           return @ret;
228             }
229              
230             =head1 AUTHOR
231              
232             Paul Evans
233              
234             =cut
235              
236             0x55AA;