File Coverage

blib/lib/Devel/TimeStats.pm
Criterion Covered Total %
statement 96 97 98.9
branch 33 38 86.8
condition 10 11 90.9
subroutine 16 16 100.0
pod 7 7 100.0
total 162 169 95.8


line stmt bran cond sub pod time code
1             package Devel::TimeStats;
2              
3             our $VERSION = '0.04';
4              
5 3     3   68505 use Moo;
  3         67425  
  3         20  
6 3     3   10063 use namespace::autoclean;
  3         65101  
  3         20  
7 3     3   5309 use Time::HiRes qw/gettimeofday tv_interval/;
  3         5568  
  3         30  
8 3     3   6332 use Text::UnicodeTable::Simple;
  3         148084  
  3         147  
9 3     3   3524 use Term::ExtendedColor qw(:all);
  3         7606  
  3         939  
10 3     3   2324 use Tree::Simple qw/use_weak_refs/;
  3         6285  
  3         25  
11 3     3   3372 use Tree::Simple::Visitor::FindByUID;
  3         8328  
  3         4448  
12              
13             has enable => (is => 'rw', required => 1, default => sub{ 1 });
14              
15             has tree => (
16             is => 'ro',
17             required => 1,
18             default => sub{ Tree::Simple->new({t => [gettimeofday]}) },
19             handles => [qw/ accept traverse /],
20             );
21             has stack => (
22             is => 'ro',
23             required => 1,
24             lazy => 1,
25             default => sub { [ shift->tree ] }
26             );
27              
28             has color_map => (
29             is => 'ro',
30             isa => sub{ ref $_ eq 'HASH' },
31             default => sub{{
32             '0.01' => 'yellow3',
33             '0.05' => 'yellow1',
34             '0.1' => 'red3',
35             '0.5' => 'red1',
36             }}
37             );
38              
39             has percentage_decimal_precision => (is => 'ro', required => 1, default => sub { 0 } );
40              
41              
42             sub profile {
43 28     28 1 3766 my $self = shift;
44              
45 28 100       118 return unless $self->enable;
46              
47 27         34 my %params;
48 27 100       84 if (@_ <= 1) {
    50          
49 6   50     24 $params{comment} = shift || "";
50             }
51             elsif (@_ % 2 != 0) {
52 0         0 die "profile() requires a single comment parameter or a list of name-value pairs; found "
53             . (scalar @_) . " values: " . join(", ", @_);
54             }
55             else {
56 21         71 (%params) = @_;
57 21   100     120 $params{comment} ||= "";
58             }
59              
60 27         33 my $parent;
61             my $prev;
62 27         60 my $t = [ gettimeofday ];
63 27         769 my $stack = $self->stack;
64              
65 27 100       270 if ($params{end}) {
66             # parent is on stack; search for matching block and splice out
67 8         37 for (my $i = $#{$stack}; $i > 0; $i--) {
  8         37  
68 12 100       70 if ($stack->[$i]->getNodeValue->{action} eq $params{end}) {
69 8         49 my ($node) = splice(@{$stack}, $i, 1);
  8         19  
70             # Adjust elapsed on partner node
71 8         22 my $v = $node->getNodeValue;
72 8         56 $v->{elapsed} = tv_interval($v->{t}, $t);
73 8         81 return $node->getUID;
74             }
75             }
76             # if partner not found, fall through to treat as non-closing call
77             }
78 19 100       45 if ($params{parent}) {
79             # parent is explicitly defined
80 1         6 $prev = $parent = $self->_get_uid($params{parent});
81             }
82 19 100       67 if (!$parent) {
83             # Find previous node, which is either previous sibling or parent, for ref time.
84 18 50       83 $prev = $parent = $stack->[-1] or return undef;
85 18         69 my $n = $parent->getChildCount;
86 18 100       153 $prev = $parent->getChild($n - 1) if $n > 0;
87             }
88              
89 19   100     140 my $node = Tree::Simple->new({
90             action => $params{begin} || "",
91             t => $t,
92             elapsed => tv_interval($prev->getNodeValue->{t}, $t),
93             comment => $params{comment},
94             });
95 19 100       918 $node->setUID($params{uid}) if $params{uid};
96              
97 19         61 $parent->addChild($node);
98 19 100       1778 push(@{$stack}, $node) if $params{begin};
  9         20  
99              
100 19         55 return $node->getUID;
101             }
102              
103             sub created {
104 1     1 1 829 return @{ shift->{tree}->getNodeValue->{t} };
  1         6  
105             }
106              
107             sub elapsed {
108 1     1 1 3315 return tv_interval(shift->{tree}->getNodeValue->{t});
109             }
110              
111             sub report {
112 4     4 1 39 my $self = shift;
113            
114 4         8 my $total_duration = 0;
115 4         19 $total_duration += $_->getNodeValue->{elapsed} for $self->tree->getAllChildren;
116              
117 4         88 my $t = Text::UnicodeTable::Simple->new(ansi_color => 1);
118 4         108 $t->set_header(qw/ Action Time % /);
119              
120 4         604 my @results;
121             $self->traverse(
122             sub {
123 19     19   1690 my $action = shift;
124 19         54 my $stat = $action->getNodeValue;
125 19 100 100     94 my @r = ( $action->getDepth,
    100 100        
    100          
126             ($stat->{action} || "") .
127             ($stat->{action} && $stat->{comment} ? " " : "") . ($stat->{comment} ? '- ' . $stat->{comment} : ""),
128             $stat->{elapsed},
129             $stat->{action} ? 1 : 0,
130             ($stat->{elapsed} * 100) / $total_duration
131             );
132             # Trim down any times >= 10 to avoid ugly Text::Simple line wrapping
133 19         399 my $elapsed = substr(sprintf("%f", $stat->{elapsed}), 0, 8) . "s";
134            
135 19         27 my $color = '';
136 19         23 foreach my $key (sort { $a <=> $b } keys %{$self->color_map}) {
  95         184  
  19         101  
137 76 100       267 $color = $self->color_map->{$key} if $stat->{elapsed} >= $key;
138             }
139            
140             # format %
141 19         163 my $share = sprintf "%2.".$self->percentage_decimal_precision."f%%", $r[4];
142            
143 19         23 my @rows;
144 19 50       72 for my $value (( q{ } x $r[0] ) . $r[1], defined $r[2] ? $elapsed : '??', $share) {
145 57         2000 push @rows, fg('bold', fg($color, $value));
146             }
147 19         914 $t->add_row(@rows);
148              
149 19         2312 push(@results, \@r);
150             }
151 4         84 );
152 4 100       177 return wantarray ? @results : $t->draw;
153             }
154              
155             sub _get_uid {
156 1     1   2 my ($self, $uid) = @_;
157              
158 1         11 my $visitor = Tree::Simple::Visitor::FindByUID->new;
159 1         44 $visitor->searchForUID($uid);
160 1         12 $self->accept($visitor);
161 1         1111 return $visitor->getResult;
162             }
163              
164             sub addChild {
165 1     1 1 86 my $self = shift;
166 1         2 my $node = $_[ 0 ];
167              
168 1         5 my $stat = $node->getNodeValue;
169              
170             # do we need to fake $stat->{ t } ?
171 1 50       9 if( $stat->{ elapsed } ) {
172             # remove the "s" from elapsed time
173 1         6 $stat->{ elapsed } =~ s{s$}{};
174             }
175              
176 1         22 $self->tree->addChild( @_ );
177             }
178              
179             sub setNodeValue {
180 1     1 1 81 my $self = shift;
181 1         3 my $stat = $_[ 0 ];
182              
183             # do we need to fake $stat->{ t } ?
184 1 50       6 if( $stat->{ elapsed } ) {
185             # remove the "s" from elapsed time
186 1         7 $stat->{ elapsed } =~ s{s$}{};
187             }
188              
189 1         9 $self->tree->setNodeValue( @_ );
190             }
191              
192             sub getNodeValue {
193 1     1 1 76 my $self = shift;
194 1         8 $self->tree->getNodeValue( @_ )->{ t };
195             }
196              
197              
198              
199              
200             1;
201              
202              
203             __END__