File Coverage

blib/lib/Devel/Memalyzer.pm
Criterion Covered Total %
statement 82 83 98.8
branch 22 30 73.3
condition 7 8 87.5
subroutine 17 17 100.0
pod 7 10 70.0
total 135 148 91.2


line stmt bran cond sub pod time code
1             package Devel::Memalyzer;
2 9     9   4355 use strict;
  9         22  
  9         367  
3 9     9   66 use warnings;
  9         27  
  9         295  
4              
5 9     9   49 use base 'Devel::Memalyzer::Base';
  9         9  
  9         835  
6 9     9   49 use Carp;
  9         19  
  9         777  
7 9     9   49 use IO qw/Handle File Pipe/;
  9         22  
  9         80  
8              
9             our $VERSION = 0.001;
10             our $SINGLETON;
11              
12             __PACKAGE__->gen_accessors(qw/ output columns headers /);
13              
14             sub import {
15 9     9   48 my $class = shift;
16 9 50       166 return unless @_;
17 0         0 $class->init( @_ );
18             }
19              
20 15     15 1 2500 sub singleton { $SINGLETON }
21              
22             sub init {
23 5     5 1 10705 my $class = shift;
24 5 50       25 carp( "re-initializing $class, this will destroy your old one and is probably not what you want" )
25             if $SINGLETON;
26 5         55 $SINGLETON = $class->new( @_ );
27 5         20 return $class->singleton;
28             }
29              
30             sub record {
31 1     1 1 33 my $self = shift;
32 1         31 my ( $pid ) = @_;
33 1         100 my %data = (
34             timestamp => time,
35 1         23 (map { $_->collect( $pid ) } $self->plugins),
36             $self->collect_columns( $pid ),
37             );
38              
39 1         33 $self->sync_headers( \%data );
40              
41 1         15 my ( $raw ) = $self->output_handles;
42 1         3 print $raw join( ',', @data{ @{ $self->headers }}) . "\n";
  1         6  
43             }
44              
45             sub plugins {
46 21     21 1 5039 my $self = shift;
47 21 50 66     181 return unless $self->{ _plugins } || $self->{ plugins };
48 10 100       845 $self->{ _plugins } ||= [ map {
49             eval "require $_; 1" || die( $@ );
50 5         80 $_->new;
51 21   100     89 } @{ $self->{ plugins }}];
  10         35  
52 16         22 return @{ $self->{ _plugins }};
  16         165  
53             }
54              
55             sub add_column {
56 17     17 1 42 my $self = shift;
57 17         30 my ( $name, $sub ) = @_;
58 17 100       80 $self->columns({}) unless $self->columns();
59 17         46 $self->columns->{ $name } = $sub;
60             }
61              
62             sub del_column {
63 5     5 1 10 my $self = shift;
64 5         15 my ( $name ) = @_;
65 5         20 my $columns = $self->columns;
66 5         30 delete $columns->{ $name };
67             }
68              
69             sub collect_columns {
70 6     6 0 155 my $self = shift;
71 6         455 my ( $pid ) = @_;
72              
73 6         17 my %data;
74 6 50       14 for my $column ( keys %{ $self->columns || {}}) {
  6         42  
75 13         114 my $sub = $self->columns->{ $column };
76 13         57 $data{ $column } = $sub->( $pid );
77             }
78 6         148 return %data;
79             }
80              
81             sub sync_headers {
82 4     4 0 66 my $self = shift;
83 4         83 my ( $data ) = @_;
84              
85 4         212 my $old = $self->headers;
86 4         279 my $new = [ reverse sort keys %$data ];
87              
88             # return if headers are unchanged
89 4 100 100     390 return if $old && join('', @$old) eq join('', @$new);
90              
91 3         30 $self->headers( $new );
92              
93 3         82 my ( $raw, $headers )= $self->output_handles;
94 3 100       126 print $raw "\n" if $old;
95 3         187 print $headers join(',', @$new ). "\n";
96             }
97              
98             sub output_handles {
99 34     34 0 430 my $self = shift;
100              
101 34 100       104 unless( $self->{ output_handles }) {
102 10         410 my $file = $self->output;
103              
104 10 100       460 die( "Refusing to override exisiting output file: '$file'" )
105             if -e $file;
106              
107 5 50       45 open( my $raw, '>', $self->output . ".raw" ) || die( "Error opening output file: $!" );
108 5 50       50 open( my $headers, '>', $self->output . ".head" ) || die( "Error opening output file: $!" );
109 5         65 $raw->autoflush( 1 );
110 5         420 $headers->autoflush( 1 );
111 5         250 $self->{ output_handles } = [ $raw, $headers ];
112             }
113              
114 29         32 return @{ $self->{ output_handles }};
  29         219  
115             }
116              
117             sub finish {
118 10     10 1 21 my $self = shift;
119 10 100       280 return unless $self->{ output_handles };
120 5         37 my ($raw, $headers) = @{ $self->{ output_handles }};
  5         21  
121 5 50       88 close( $raw ) if $raw;
122 5 50       1942 close( $headers ) if $headers;
123             }
124              
125             sub DESTROY {
126 10     10   912 my $self = shift;
127 10         39 $self->finish;
128             }
129              
130             1;
131              
132             __END__