File Coverage

blib/lib/Attribute/RecordCallers.pm
Criterion Covered Total %
statement 43 43 100.0
branch 4 4 100.0
condition 2 2 100.0
subroutine 13 13 100.0
pod 2 3 66.6
total 64 65 98.4


line stmt bran cond sub pod time code
1             package Attribute::RecordCallers;
2              
3 7     7   113741 use strict;
  7         13  
  7         293  
4 7     7   34 use warnings;
  7         10  
  7         184  
5 7     7   3840 use Attribute::Handlers;
  7         29974  
  7         33  
6 7     7   253 use Carp qw(carp);
  7         13  
  7         395  
7 7     7   4005 use Time::HiRes qw(time);
  7         9643  
  7         29  
8 7     7   1126 use Scalar::Util qw(set_prototype);
  7         10  
  7         1140  
9              
10             our $VERSION = '0.01';
11              
12             our @CARP_NOT = qw(Attribute::Handlers);
13             # arguably a bug in Carp, but Attribute::Handlers does
14             # nasty things with UNIVERSAL
15             @Attribute::Handlers::CARP_NOT = qw(attributes);
16              
17             our %callers;
18              
19             sub UNIVERSAL::RecordCallers :ATTR(CODE,BEGIN) {
20 8     8 0 5994 my ($pkg, $glob, $referent) = @_;
21 7     7   38 no strict 'refs';
  7         8  
  7         198  
22 7     7   30 no warnings qw(redefine once prototype);
  7         11  
  7         1180  
23 8         13 my $subname = *{$glob}{NAME};
  8         22  
24 8 100       33 if ($subname eq 'ANON') {
25 2         497 carp "Ignoring RecordCallers attribute on anonymous subroutine";
26 2         11 return;
27             }
28 6         13 $subname = $pkg . '::' . $subname;
29             *$subname = sub {
30 12   100 12   2245 push @{ $callers{$subname} ||= [] }, [ caller, time ];
  12         104  
31 12         34 goto &$referent;
32 6         30 };
33 6         10 my $proto = prototype $referent;
34 6 100       30 set_prototype(\&$subname, $proto) if defined $proto;
35 7     7   38 }
  7         9  
  7         37  
36              
37             sub clear {
38 3     3 1 5908 %callers = ();
39             }
40              
41             sub walk {
42 1     1 1 53 my $coderef = shift;
43 1         12 $coderef->($_, $callers{$_}) for sort keys %callers;
44             }
45              
46             1;
47              
48             =head1 NAME
49              
50             Attribute::RecordCallers - keep a record of who called a subroutine
51              
52             =head1 SYNOPSIS
53              
54             use Attribute::RecordCallers;
55             sub call_me_and_i_ll_tell_you : RecordCallers { ... }
56             ...
57             END {
58             use Data::Dumper;
59             print Dumper \%Attribute::RecordCallers::callers;
60             }
61              
62             =head1 DESCRIPTION
63              
64             This module defines a function attribute that will trigger collection of
65             callers for the designated functions.
66              
67             Each time a function with the C<:RecordCallers> attribute is run, a global
68             hash C<%Attribute::RecordCallers::caller> is populated with caller information.
69             The keys in the hash are the function names, and the elements are arrayrefs
70             containing lists of quadruplets:
71              
72             [ package, filename, line, timestamp ]
73              
74             The timestamp is obtained via C.
75              
76             =head1 FUNCTIONS
77              
78             =over 4
79              
80             =item clear()
81              
82             (not exported) This function will clear the C<%callers> global hash.
83              
84             =item walk(sub { ... })
85              
86             (not exported) Invokes the subroutine passed as argument once for each
87             item in the C<%callers> hash. The arguments passed to it are the
88             recorded subroutine name, and the arrayref of arrayrefs recording
89             all the calls.
90              
91             =back
92              
93             =head1 LIMITATIONS
94              
95             You cannot use the C<:RecordCaller> attribute on anonymous or lexical
96             subroutines, or or subroutines with any other attribute (such as
97             C<:lvalue>).
98              
99             =head1 LICENSE
100              
101             (c) Rafael Garcia-Suarez (rgs at consttype dot org) 2014
102              
103             This program is free software; you may redistribute it and/or modify it under
104             the same terms as Perl itself.
105              
106             A git repository for the sources is at L.
107              
108             =cut