File Coverage

blib/lib/Devel/KYTProf.pm
Criterion Covered Total %
statement 119 138 86.2
branch 30 54 55.5
condition 6 33 18.1
subroutine 17 18 94.4
pod 0 5 0.0
total 172 248 69.3


line stmt bran cond sub pod time code
1             package Devel::KYTProf;
2 6     6   324419 use strict;
  6         42  
  6         142  
3 6     6   24 use warnings;
  6         9  
  6         337  
4              
5             our $VERSION = '0.9994';
6              
7             my $Applied = {};
8              
9             use Class::Data::Lite (
10 6         65 rw => {
11             namespace_regex => undef,
12             ignore_class_regex => undef,
13             context_classes_regex => undef,
14             logger => undef,
15             threshold => undef,
16             remove_linefeed => undef,
17             remove_escape_sequences => undef,
18              
19             color_time => 'red',
20             color_module => 'cyan',
21             color_info => 'blue',
22             color_call => 'green',
23              
24             _orig_code => {},
25             _prof_code => {},
26             },
27 6     6   2276 );
  6         2900  
28              
29 6     6   3267 use Module::Load ();
  6         5410  
  6         106  
30 6     6   2394 use Time::HiRes;
  6         6344  
  6         20  
31 6     6   3533 use Term::ANSIColor;
  6         40195  
  6         5592  
32              
33             sub import {
34 7     7   71 __PACKAGE__->apply_prof('DBI');
35 7         25 __PACKAGE__->apply_prof('LWP::UserAgent');
36 7         22 __PACKAGE__->apply_prof('Cache::Memcached::Fast');
37 7         22 __PACKAGE__->apply_prof('MogileFS::Client');
38 7         21 __PACKAGE__->apply_prof('Furl::HTTP');
39 7         2842 1;
40             }
41              
42             sub apply_prof {
43 35     35 0 68 my ($class, $pkg, $prof_pkg, @args) = @_;
44 35         46 eval { Module::Load::load($pkg) };
  35         65  
45 35 100       98571 return if $@;
46              
47 7   33     53 $prof_pkg ||= "Devel::KYTProf::Profiler::$pkg";
48 7         10 eval {Module::Load::load($prof_pkg)};
  7         20  
49 7 50       118 if ($@) {
50 0         0 die qq{failed to load profiler package "$prof_pkg" for "$pkg": $@\n};
51             }
52 7 50       80 unless ($prof_pkg->can('apply')) {
53 0         0 die qq{"$prof_pkg" has no `apply` method. A profiler package should implement it.\n};
54             }
55 7 100       30 return if ++$Applied->{$prof_pkg} > 1; # skip if already applied
56 6         23 $prof_pkg->apply(@args);
57             }
58              
59             sub add_profs {
60 7     7 0 91 my ($class, $module, $methods, $callback, $sampler) = @_;
61 7         11 eval {Module::Load::load($module)};
  7         25  
62 7 50       1661 if ($methods eq ':all') {
63 0         0 eval { Module::Load::load('Class/Inspector.pm') };
  0         0  
64 0 0       0 return if $@;
65 0         0 $methods = [];
66 0         0 @$methods = @{Class::Inspector->methods($module, 'public')};
  0         0  
67             }
68 7         19 for my $method (@$methods) {
69 26         59 $class->add_prof($module, $method, $callback, $sampler);
70             }
71             }
72              
73             sub add_prof {
74 40     40 0 239 my ($class, $module, $method, $callback, $sampler) = @_;
75 40         51 eval {Module::Load::load($module)};
  40         84  
76 40         8079 my $orig = $class->_orig_code->{$module}{$method};
77 40 50       236 unless ($orig) {
78 40 50       307 $orig = $module->can($method) or return;
79 40         118 $class->_orig_code->{$module}->{$method} = $orig;
80             }
81              
82             my $code = sub {
83 12 100   12   4906 if ($sampler) {
84 3         8 my $is_sample = $sampler->($orig, @_);
85 3 50       14 unless ($is_sample) {
86 0         0 return $orig->(@_);
87             }
88             }
89              
90 12         18 my ($package, $file, $line, $level);
91 12         43 my $namespace_regex = $class->namespace_regex;
92 12         64 my $ignore_class_regex = $class->ignore_class_regex;
93 12         49 my $context_classes_regex = $class->context_classes_regex;
94 12         55 my $threshold = $class->threshold;
95 12 50 33     72 if ($namespace_regex || $context_classes_regex) {
96 0         0 for my $i (1..30) {
97 0 0       0 my ($p, $f, $l) = caller($i) or next;
98 0 0 0     0 if (
      0        
      0        
      0        
99             $namespace_regex
100             &&
101             !$package
102             &&
103             $p =~ /^($namespace_regex)/
104             &&
105             (! $ignore_class_regex || $p !~ /$ignore_class_regex/)
106             ) {
107 0         0 ($package, $file, $line) = ($p, $f, $l);
108             }
109              
110 0 0 0     0 if ($context_classes_regex && !$level && $p =~ /^($context_classes_regex)$/) {
      0        
111 0         0 $level = $i;
112             }
113             }
114             } else {
115 12         30 for my $i (1..30) {
116 360 50       600 my ($p, $f, $l) = caller($i) or next;
117 0 0       0 if ($p !~ /^($module)/) {
118 0         0 ($package, $file, $line) = ($p, $f, $l);
119 0         0 last;
120             }
121             }
122             }
123 12 50       24 unless ($package) {
124 12         34 ($package, $file, $line) = caller;
125             }
126 12         59 my $start = [ Time::HiRes::gettimeofday ];
127 12         21 my ($res, @res);
128 12 50       21 if (wantarray) {
129 0         0 @res = $orig->(@_);
130             } else {
131 12         32 $res = $orig->(@_);
132             }
133 12         1823 my $ns = Time::HiRes::tv_interval($start) * 1000;
134 12 50 33     185 if (!$threshold || $ns >= $threshold) {
135 12         20 my $message = "";
136 12         135 $message .= colored(sprintf('% 9.3f ms ', $ns), $class->color_time);
137 12   50     278 $message .= colored(sprintf(' [%s] ', ref $_[0] || $_[0] || ''), $class->color_module);
138 12         158 my $cb_info;
139             my $cb_data;
140 12 100       26 if ($callback) {
141 7         27 my $v = $callback->($orig, @_);
142 7 100       34 if (ref $v eq "ARRAY") {
143 6         9 $cb_info = sprintf $v->[0], map { $v->[2]->{$_} } @{$v->[1]};
  15         35  
  6         14  
144 6         17 $cb_data = $v->[2];
145             } else {
146 1         2 $cb_info = $v;
147 1         1 $cb_data = {};
148             }
149             } else {
150 5         7 $cb_info = $method;
151 5         7 $cb_data = {};
152             }
153 12 100       29 $cb_info =~ s/[[:cntrl:]]//smg if $class->remove_escape_sequences;
154 12         79 $message .= colored(sprintf(' %s ', $cb_info), $class->color_info);
155 12         159 $message .= ' | ';
156 12   50     54 $message .= colored(sprintf('%s:%d', $package || '', $line || 0), $class->color_call);
      50        
157 12 50       161 $message =~ s/\n/ /g if $class->remove_linefeed;
158 12         52 $message .= "\n";
159 12 50       22 $class->logger ? $class->logger->log(
160             level => 'debug',
161             message => $message,
162             module => $module,
163             method => $method,
164             time => $ns,
165             package => $package,
166             file => $file,
167             line => $line,
168             data => $cb_data,
169             ) : print STDERR $message;
170             }
171 12 50       110 return wantarray ? @res : $res;
172 40         429 };
173 40         111 $class->_prof_code->{$module}->{$method} = $code;
174              
175 40         210 $class->_inject_code($module, $method, $code);
176             }
177              
178             sub _inject_code {
179 46     46   130 my ($class, $module, $method, $code) = @_;
180 6     6   40 no strict 'refs';
  6         10  
  6         226  
181 6     6   31 no warnings qw/redefine prototype/;
  6         9  
  6         1328  
182 46         55 *{"$module\::$method"} = $code;
  46         246  
183             }
184              
185             sub mute {
186 2     2 0 1177 my ($class, $module, @methods) = @_;
187              
188 2 100       5 if (scalar(@methods)) {
189 1         3 for my $method (@methods) {
190 1         3 $class->_inject_code($module, $method, $class->_orig_code->{$module}->{$method});
191             }
192             } else {
193 1         2 for my $method (keys %{$class->_orig_code->{$module}}) {
  1         3  
194 2         11 $class->_inject_code($module, $method, $class->_orig_code->{$module}->{$method});
195             }
196             }
197             }
198              
199             sub unmute {
200 2     2 0 884 my ($class, $module, @methods) = @_;
201              
202 2 100       7 if (scalar(@methods)) {
203 1         2 for my $method (@methods) {
204 1         4 $class->_inject_code($module, $method, $class->_prof_code->{$module}->{$method});
205             }
206             } else {
207 1         2 for my $method (keys %{$class->_prof_code->{$module}}) {
  1         3  
208 2         9 $class->_inject_code($module, $method, $class->_prof_code->{$module}->{$method});
209             }
210             }
211             }
212              
213             {
214 6     6   52 no warnings 'redefine';
  6         9  
  6         335  
215       0     *DB::DB = sub {};
216             }
217              
218             1;
219              
220             __END__