File Coverage

blib/lib/Aspect/Library/ZoneTimer.pm
Criterion Covered Total %
statement 73 75 97.3
branch 8 10 80.0
condition 4 6 66.6
subroutine 12 12 100.0
pod 0 1 0.0
total 97 104 93.2


line stmt bran cond sub pod time code
1             package Aspect::Library::ZoneTimer;
2              
3 3     3   4307 use 5.008002;
  3         11  
  3         95  
4 3     3   12 use strict;
  3         6  
  3         80  
5 3     3   14 use warnings;
  3         4  
  3         92  
6 3     3   13 use Carp ();
  3         6  
  3         59  
7 3     3   5711 use Params::Util 1.00 ();
  3         3100  
  3         74  
8 3     3   1593 use Aspect::Modular 1.00 ();
  3         2633  
  3         49  
9 3     3   973 use Aspect::Advice::Around ();
  3         10982  
  3         58  
10 3     3   16 use Time::HiRes 1.9718 ();
  3         53  
  3         55  
11              
12 3     3   12 use vars qw{$VERSION @ISA};
  3         7  
  3         176  
13             BEGIN {
14 3     3   5 $VERSION = '1.10';
15 3         1523 @ISA = 'Aspect::Modular';
16             }
17              
18             sub get_advice {
19 2     2 0 71 my $self = shift;
20 2         5 my %params = @_;
21 2         4 my $zones = $params{zones};
22 2         4 my $handler = $params{handler};
23              
24             # Check params
25 2 50       8 unless ( Params::Util::_HASH($zones) ) {
26 0         0 Carp::croak("Did not provide a set of zones");
27             }
28 2 50       8 unless ( Params::Util::_CODELIKE($handler) ) {
29 0         0 Carp::croak("Did not provide a handler function");
30             }
31              
32             # Variables to be shared between all advice closures
33 2         3 my @STACK = (); # Storage for timing data
34 2         3 my $DISABLE = 0; # Prevent recursion in the report handler
35              
36             # Create one advice for each zone
37 2         3 my @advice = ();
38 2         11 foreach ( sort keys %$zones ) {
39 4         179318 my $zone = $_;
40 4         11 my $pointcut = $zones->{$zone};
41             push @advice, Aspect::Advice::Around->new(
42             lexical => $self->lexical,
43             pointcut => $pointcut,
44             code => sub {
45             # Shortcut if we are inside the same zone
46             # or inside the handler.
47 12 100 66 12   1667 if ( $DISABLE or @STACK and $STACK[-1]->[0] eq $zone ) {
      66        
48 3         16 $_->proceed;
49 3         600814 return;
50             }
51              
52             # Execute the function and capture timing
53 9         18 my $error = '';
54 9         13 SCOPE: {
55 9         13 local $@;
56 9         30 push @STACK, [ $zone, { } ];
57 9         53 my @start = Time::HiRes::gettimeofday();
58 9         17 eval {
59 9         41 $_->proceed;
60             };
61 9         804850 $error = $@;
62 9         60 my @stop = Time::HiRes::gettimeofday();
63 9         28 my $frame = pop @STACK;
64 9         32 my $total = $frame->[1];
65              
66             # Use our own interval math, generating a value
67             # in integer microseconds, to avoid potential
68             # floating point bugs in Time::HiRes::tv_interval.
69 9         87 my $interval = ( $stop[0] * 1000000 + $stop[1] )
70             - ( $start[0] * 1000000 + $start[1] );
71              
72 9 100       35 if ( @STACK ) {
73             # Calculate the exclusive time for the
74             # current stack frame and merge up to
75             # the totals already in our parent.
76 5         47 my $parent = $STACK[-1]->[1];
77 5         27 foreach my $z ( keys %$total ) {
78 2         6 $interval -= $total->{$z};
79 2         7 $parent->{$z} += $total->{$z};
80             }
81 5         33 $parent->{$zone} += $interval;
82              
83             } else {
84             # Calculate the exclusive time for the current
85             # zone and add it to any reentered zone
86             # beneath us.
87 4         320 foreach my $z ( keys %$total ) {
88 5         14 $interval -= $total->{$z};
89             }
90 4         15 $total->{$zone} += $interval;
91              
92             # Send the report to the handler, including
93             # our start and stop times in case they are
94             # handy for the report.
95 4         11 $DISABLE++;
96 4         10 eval {
97 4         28 $handler->(
98             $zone,
99             \@start,
100             \@stop,
101             $total,
102             );
103             };
104 4         89 $DISABLE--;
105             }
106             }
107              
108             # Pass through any exceptions
109 9 100       77 die $error if $error;
110 6         239 return;
111             },
112 4         76 );
113             }
114              
115 2         209143 return @advice;
116             }
117              
118             1;
119              
120             __END__