Mercurial > pub > Impl
comparison lib/IMPL/Profiler.pm @ 407:c6e90e02dd17 ref20150831
renamed Lib->lib
author | cin |
---|---|
date | Fri, 04 Sep 2015 19:40:23 +0300 |
parents | |
children |
comparison
equal
deleted
inserted
replaced
406:f23fcb19d3c1 | 407:c6e90e02dd17 |
---|---|
1 package IMPL::Profiler; | |
2 | |
3 use strict; | |
4 use warnings; | |
5 use Time::HiRes; | |
6 require Scalar::Util; | |
7 | |
8 our $Enabled; | |
9 our %TrappedModules; | |
10 our %InvokeInfo; | |
11 our $InvokeTime = 0; | |
12 our @TrapQueue; | |
13 our $Filter ||= qr//; | |
14 my $level; | |
15 | |
16 BEGIN { | |
17 $level = 0; | |
18 if ($Enabled) { | |
19 warn "profiler enabled"; | |
20 | |
21 unshift @INC, sub { | |
22 my ($self,$filename) = @_; | |
23 | |
24 (my $module = $filename) =~ s/\//::/g; | |
25 $module =~ s/\.\w+$//; | |
26 | |
27 return unless $module =~ $Filter; | |
28 | |
29 foreach my $dir (@INC) { | |
30 my $fullName = "$dir/$filename"; | |
31 if (-f $fullName) { | |
32 open my $hmod, $fullName or die "$fullName: $!" if $!; | |
33 | |
34 | |
35 | |
36 my @source; | |
37 local $/ = "\n"; | |
38 while (my $line = <$hmod>) { | |
39 last if $line =~ /^\s*__END__/; | |
40 push @source, $line; | |
41 } | |
42 | |
43 undef $hmod; | |
44 | |
45 push @source, | |
46 "IMPL::Profiler::trap_all(__PACKAGE__);\n", | |
47 "1;\n"; | |
48 | |
49 | |
50 return (sub { | |
51 if (@source) { | |
52 $_ = shift @source; | |
53 return 1; | |
54 } else { | |
55 return 0; | |
56 } | |
57 }, undef ); | |
58 } | |
59 } | |
60 }; | |
61 | |
62 no warnings 'once'; | |
63 *CORE::GLOBAL::caller = sub { | |
64 my $target = (shift || 0)+1; | |
65 my $realFrame = 1; | |
66 | |
67 for (my $i = 1; $i<$target; $i++) { | |
68 $realFrame ++; | |
69 my $caller = CORE::caller($realFrame-1) or return; | |
70 $realFrame ++ if $caller eq 'IMPL::Profiler::Proxy'; #current frame is proxy | |
71 } | |
72 | |
73 my @frame = CORE::caller($realFrame) or return; | |
74 if ( $frame[0] eq 'IMPL::Profiler::Proxy' ) { | |
75 my @next = CORE::caller($realFrame+1) or return; | |
76 @frame[0..2] = @next[0..2]; | |
77 } | |
78 | |
79 #warn " "x$level,"$frame[0] - $frame[3]"; | |
80 return wantarray ? @frame : $frame[0]; | |
81 }; | |
82 } | |
83 } | |
84 | |
85 sub trap_all { | |
86 return if not $Enabled; | |
87 no strict 'refs'; | |
88 foreach my $class (@_) { | |
89 next if $TrappedModules{$class}; | |
90 $TrappedModules{$class} = 1; | |
91 | |
92 eval "warn 'load $class'; require $class;" if not %{"${class}::"}; | |
93 die $@ if $@; | |
94 | |
95 no strict 'refs'; | |
96 my $table = \%{"${class}::"}; | |
97 trap($class,$_) foreach (grep eval { *{$table->{$_}}{CODE} }, keys %$table); # here can be a not a GLOB reference | |
98 } | |
99 } | |
100 | |
101 sub trap { | |
102 my ($class,$method) = @_; | |
103 | |
104 return if not $Enabled; | |
105 | |
106 return if $method eq 'import'; | |
107 | |
108 no strict 'refs'; | |
109 my $prevCode = \&{"${class}::${method}"}; | |
110 my $proto = prototype $prevCode; | |
111 | |
112 if (defined $proto and not $proto) { | |
113 return; | |
114 } | |
115 { | |
116 package IMPL::Profiler::Proxy; | |
117 no warnings 'redefine'; | |
118 my $sub = sub { | |
119 my $t0 = [Time::HiRes::gettimeofday]; | |
120 my @arr; | |
121 my $scalar; | |
122 my $entry = $prevCode; | |
123 my ($timeOwn,$timeTotal); | |
124 my $context = wantarray; | |
125 { | |
126 local $InvokeTime = 0; | |
127 #warn " "x$level,"enter ${class}::$method"; | |
128 $level ++; | |
129 if ($context) { | |
130 @arr = &$entry(@_); | |
131 } else { | |
132 if (defined $context) { | |
133 $scalar = &$entry(@_); | |
134 } else { | |
135 &$entry(@_); | |
136 } | |
137 } | |
138 $timeTotal = Time::HiRes::tv_interval($t0); | |
139 $timeOwn = $timeTotal - $InvokeTime; | |
140 } | |
141 $InvokeInfo{"${class}::${method}"}{Count} ++; | |
142 $InvokeInfo{"${class}::${method}"}{Total} += $timeTotal; | |
143 $InvokeInfo{"${class}::${method}"}{Own} += $timeOwn; | |
144 $InvokeTime += $timeTotal; | |
145 $level --; | |
146 #warn " "x$level,"leave ${class}::$method"; | |
147 return $context ? @arr : $scalar; | |
148 }; | |
149 if ($proto) { | |
150 Scalar::Util::set_prototype($sub => $proto); | |
151 } | |
152 *{"${class}::${method}"} = $sub; | |
153 } | |
154 | |
155 } | |
156 | |
157 sub PrintStatistics { | |
158 my $hout = shift || *STDERR; | |
159 print $hout "-- modules --\n"; | |
160 print $hout "$_\n" foreach sort keys %TrappedModules; | |
161 print $hout "\n-- stats --\n"; | |
162 print $hout | |
163 pad($_,50), | |
164 pad("$InvokeInfo{$_}{Count}",10), | |
165 pad(sprintf("%.3f",$InvokeInfo{$_}{Own}),10), | |
166 pad(sprintf("%.3f",$InvokeInfo{$_}{Total}),10), | |
167 "\n" | |
168 foreach sort { $InvokeInfo{$b}{Own} <=> $InvokeInfo{$a}{Own} } keys %InvokeInfo; | |
169 } | |
170 | |
171 sub ResetStatistics { | |
172 $InvokeTime = 0; | |
173 %InvokeInfo = (); | |
174 } | |
175 | |
176 sub pad { | |
177 my ($str,$len) = @_; | |
178 if (length $str < $len) { | |
179 return $str.(' 'x ($len- length $str)); | |
180 } else { | |
181 return $str; | |
182 } | |
183 } | |
184 1; |