File Coverage

blib/lib/Devel/NYTProf/SubInfo.pm
Criterion Covered Total %
statement 204 215 94.8
branch 61 92 66.3
condition 33 47 70.2
subroutine 41 42 97.6
pod 0 28 0.0
total 339 424 79.9


line stmt bran cond sub pod time code
1             package Devel::NYTProf::SubInfo; # sub_subinfo
2              
3 48     48   360 use strict;
  48         109  
  48         1475  
4 48     48   272 use warnings;
  48         102  
  48         1244  
5 48     48   251 use Carp;
  48         101  
  48         2703  
6              
7 48     48   307 use List::Util qw(min max);
  48         102  
  48         2917  
8 48     48   994 use Data::Dumper;
  48         9103  
  48         2797  
9              
10 48         3557 use Devel::NYTProf::Util qw(
11             trace_level
12 48     48   336 );
  48         116  
13 48         6353 use Devel::NYTProf::Constants qw(
14             NYTP_SIi_FID NYTP_SIi_FIRST_LINE NYTP_SIi_LAST_LINE
15             NYTP_SIi_CALL_COUNT NYTP_SIi_INCL_RTIME NYTP_SIi_EXCL_RTIME
16             NYTP_SIi_SUB_NAME NYTP_SIi_PROFILE
17             NYTP_SIi_REC_DEPTH NYTP_SIi_RECI_RTIME NYTP_SIi_CALLED_BY
18             NYTP_SIi_elements
19              
20             NYTP_SCi_CALL_COUNT
21             NYTP_SCi_INCL_RTIME NYTP_SCi_EXCL_RTIME NYTP_SCi_RECI_RTIME
22             NYTP_SCi_REC_DEPTH NYTP_SCi_CALLING_SUB
23             NYTP_SCi_elements
24 48     48   318 );
  48         93  
25              
26             # extra constants for private elements
27             use constant {
28 48         148805 NYTP_SIi_meta => NYTP_SIi_elements + 1,
29             NYTP_SIi_cache => NYTP_SIi_elements + 2,
30 48     48   326 };
  48         150  
31              
32              
33 3346 100   3346 0 23190 sub fid { shift->[NYTP_SIi_FID] || 0 }
34              
35 2376     2376 0 7032 sub first_line { shift->[NYTP_SIi_FIRST_LINE] }
36              
37 2270     2270 0 6443 sub last_line { shift->[NYTP_SIi_LAST_LINE] }
38              
39 6899     6899 0 94118 sub calls { shift->[NYTP_SIi_CALL_COUNT] }
40              
41 7     7 0 2760 sub incl_time { shift->[NYTP_SIi_INCL_RTIME] }
42              
43 1     1 0 7 sub excl_time { shift->[NYTP_SIi_EXCL_RTIME] }
44              
45 11488     11488 0 142308 sub subname { shift->[NYTP_SIi_SUB_NAME] }
46              
47             sub subname_without_package {
48 1     1 0 537 my $subname = shift->[NYTP_SIi_SUB_NAME];
49 1         9 $subname =~ s/.*:://;
50 1         6 return $subname;
51             }
52              
53 2452     2452 0 6012 sub profile { shift->[NYTP_SIi_PROFILE] }
54              
55 1     1 0 6 sub package { (my $pkg = shift->subname) =~ s/^(.*)::.*/$1/; return $pkg }
  1         5  
56              
57 1     1 0 6 sub recur_max_depth { shift->[NYTP_SIi_REC_DEPTH] }
58              
59 1     1 0 5 sub recur_incl_time { shift->[NYTP_SIi_RECI_RTIME] }
60              
61              
62             # general purpose hash - mainly a hack to help kill off Reader.pm
63 2390   100 2390 0 21686 sub meta { shift->[NYTP_SIi_meta()] ||= {} }
64             # general purpose cache
65 1   50 1 0 10 sub cache { shift->[NYTP_SIi_cache()] ||= {} }
66              
67              
68             # { fid => { line => [ count, incl_time ] } }
69             sub caller_fid_line_places {
70 2558     2558 0 75729 my ($self, $merge_evals) = @_;
71 2558 50       6093 carp "caller_fid_line_places doesn't merge evals yet" if $merge_evals;
72             # shallow clone to remove fid 0 is_sub hack
73 2558 100       4040 my %tmp = %{ $self->[NYTP_SIi_CALLED_BY] || {} };
  2558         14898  
74 2558         6365 delete $tmp{0};
75 2558         9063 return \%tmp;
76             }
77              
78             sub called_by_subnames {
79 96     96 0 37795 my ($self) = @_;
80 96   50     468 my $callers = $self->caller_fid_line_places || {};
81              
82 96         263 my %subnames;
83 96         397 for my $sc (map { values %$_ } values %$callers) {
  128         941  
84 144         394 my $caller_subnames = $sc->[NYTP_SCi_CALLING_SUB];
85 144         725 @subnames{ keys %$caller_subnames } = (); # viv keys
86             }
87              
88 96         498 return \%subnames;
89             }
90              
91             sub is_xsub {
92 15     15 0 19 my $self = shift;
93              
94             # XXX should test == 0 but some xsubs still have undef first_line etc
95             # XXX shouldn't include opcode
96 15         26 my $first = $self->first_line;
97 15 50       27 return undef if not defined $first;
98 15 100 66     30 return 1 if $first == 0 && $self->last_line == 0;
99 12         24 return 0;
100             }
101              
102             sub is_opcode {
103 18     18 0 28 my $self = shift;
104 18 100 66     26 return 0 if $self->first_line or $self->last_line;
105 6 100       13 return 1 if $self->subname =~ m/(?:^CORE::|::CORE:)\w+$/;
106 3         8 return 0;
107             }
108              
109             sub is_anon {
110 336     336 0 1155 shift->subname =~ m/::__ANON__\b/;
111             }
112              
113             sub kind {
114 18     18 0 27 my $self = shift;
115 18 100       26 return 'opcode' if $self->is_opcode;
116 15 100       26 return 'xsub' if $self->is_xsub;
117 12         22 return 'perl';
118             }
119              
120             sub fileinfo {
121 1     1 0 3 my $self = shift;
122 1         4 my $fid = $self->fid;
123 1 50       4 if (!$fid) {
124 0         0 return undef; # sub not have a known fid
125             }
126 1         3 $self->profile->fileinfo_of($fid);
127             }
128              
129             sub clone { # shallow
130 2     2 0 6 my $self = shift;
131 2         9 return bless [ @$self ] => ref $self;
132             }
133              
134             sub _min {
135 138     138   389 my ($a, $b) = @_;
136 138 50       382 $a = $b if not defined $a;
137 138 50       338 $b = $a if not defined $b;
138             # either both are defined or both are undefined here
139 138 50       282 return undef unless defined $a;
140 138         563 return min($a, $b);
141             }
142              
143             sub _max {
144 138     138   329 my ($a, $b) = @_;
145 138 50       357 $a = $b if not defined $a;
146 138 50       368 $b = $a if not defined $b;
147             # either both are defined or both are undefined here
148 138 50       290 return undef unless defined $a;
149 138         345 return max($a, $b);
150             }
151              
152              
153             sub _alter_fileinfo {
154 240     240   1234 my ($self, $remove_fi, $new_fi) = @_;
155 240 100       1141 my $remove_fid = ($remove_fi) ? $remove_fi->fid : 0;
156 240 50       1064 my $new_fid = ( $new_fi) ? $new_fi->fid : 0;
157              
158 240 50       850 if ($self->fid == $remove_fid) {
159 240         730 $self->[NYTP_SIi_FID] = $new_fid;
160              
161 240 100       1233 $remove_fi->_remove_sub_defined($self) if $remove_fi;
162 240 50       2700 $new_fi->_add_new_sub_defined($self) if $new_fi;
163             }
164             }
165              
166              
167             sub _alter_called_by_fileinfo {
168 176     176   672 my ($self, $remove_fi, $new_fi) = @_;
169 176 50       756 my $remove_fid = ($remove_fi) ? $remove_fi->fid : 0;
170 176 50       851 my $new_fid = ( $new_fi) ? $new_fi->fid : 0;
171              
172             # remove mentions of $remove_fid from called-by details
173             # { fid => { line => [ count, incl, excl, ... ] } }
174 176 50       652 if (my $called_by = $self->[NYTP_SIi_CALLED_BY]) {
175 176         1115 my $cb = delete $called_by->{$remove_fid};
176              
177 176 50 33     1776 if ($cb && $new_fid) {
178 176   100     1093 my $new_cb = $called_by->{$new_fid} ||= {};
179              
180 176 50       895 warn sprintf "_alter_called_by_fileinfo: %s from fid %d to fid %d\n",
181             $self->subname, $remove_fid, $new_fid
182             if trace_level() >= 4;
183              
184             # merge $cb into $new_cb
185 176         1143 while ( my ($line, $cb_li) = each %$cb ) {
186 176   100     805 my $dst_line_info = $new_cb->{$line} ||= [];
187 176         1242 _merge_in_caller_info($dst_line_info, delete $cb->{$line},
188             tag => "$line:".$self->subname,
189             );
190             }
191              
192             }
193             }
194              
195             }
196              
197              
198              
199              
200             # merge details of another sub into this one
201             # there are very few cases where this is sane thing to do
202             # it's meant for merging things like anon-subs in evals
203             # e.g., "PPI::Node::__ANON__[(eval 286)[PPI/Node.pm:642]:4]"
204             sub merge_in {
205 138     138 0 402 my ($self, $donor, %opts) = @_;
206 138         354 my $self_subname = $self->subname;
207 138         351 my $donor_subname = $donor->subname;
208              
209 138 50       495 warn sprintf "Merging sub %s into %s (%s)\n",
210             $donor_subname, $self_subname, join(" ", %opts)
211             if trace_level() >= 4;
212              
213             # see also "case NYTP_TAG_SUB_CALLERS:" in load_profile_data_from_stream()
214 138         287 push @{ $self->meta->{merged_sub_names} }, $donor->subname;
  138         548  
215              
216 138         676 $self->[NYTP_SIi_FIRST_LINE] = _min($self->[NYTP_SIi_FIRST_LINE], $donor->[NYTP_SIi_FIRST_LINE]);
217 138         487 $self->[NYTP_SIi_LAST_LINE] = _max($self->[NYTP_SIi_LAST_LINE], $donor->[NYTP_SIi_LAST_LINE]);
218 138         411 $self->[NYTP_SIi_CALL_COUNT] += $donor->[NYTP_SIi_CALL_COUNT];
219 138         258 $self->[NYTP_SIi_INCL_RTIME] += $donor->[NYTP_SIi_INCL_RTIME];
220 138         249 $self->[NYTP_SIi_EXCL_RTIME] += $donor->[NYTP_SIi_EXCL_RTIME];
221 138         362 $self->[NYTP_SIi_REC_DEPTH] = max($self->[NYTP_SIi_REC_DEPTH], $donor->[NYTP_SIi_REC_DEPTH]);
222             # adding reci_rtime is correct only if one sub doesn't call the other
223 138         294 $self->[NYTP_SIi_RECI_RTIME] += $donor->[NYTP_SIi_RECI_RTIME]; # XXX
224              
225             # { fid => { line => [ count, incl_time, ... ] } }
226 138   100     535 my $dst_called_by = $self ->[NYTP_SIi_CALLED_BY] ||= {};
227 138   100     438 my $src_called_by = $donor->[NYTP_SIi_CALLED_BY] || {};
228              
229 138   33     1718 $opts{opts} ||= "merge in $donor_subname";
230              
231             # iterate over src and merge into dst
232 138         711 while (my ($fid, $src_line_hash) = each %$src_called_by) {
233              
234 137         306 my $dst_line_hash = $dst_called_by->{$fid};
235              
236             # merge lines in %$src_line_hash into %$dst_line_hash
237 137         780 for my $line (keys %$src_line_hash) {
238 143   100     562 my $dst_line_info = $dst_line_hash->{$line} ||= [];
239 143         272 my $src_line_info = $src_line_hash->{$line};
240 143 100       476 delete $src_line_hash->{$line} unless $opts{src_keep};
241 143         457 _merge_in_caller_info($dst_line_info, $src_line_info, %opts);
242             }
243             }
244              
245 138         456 return;
246             }
247              
248              
249             sub _merge_in_caller_info {
250 623     623   2782 my ($dst_line_info, $src_line_info, %opts) = @_;
251 623 100       3267 my $tag = ($opts{tag}) ? " $opts{tag}" : "";
252              
253 623 50       1820 if (!@$src_line_info) {
254 0 0       0 carp sprintf "_merge_in_caller_info%s skipped (empty donor)", $tag
255             if trace_level();
256 0         0 return;
257             }
258              
259 623 50       2390 if (trace_level() >= 5) {
260 0         0 carp sprintf "_merge_in_caller_info%s merging from $src_line_info -> $dst_line_info:", $tag;
261 0         0 warn sprintf " . %s\n", _fmt_sc($src_line_info);
262 0         0 warn sprintf " + %s\n", _fmt_sc($dst_line_info);
263             }
264 623 100       1722 if (!@$dst_line_info) {
265 79         258 @$dst_line_info = (0) x NYTP_SCi_elements;
266 79         175 $dst_line_info->[NYTP_SCi_CALLING_SUB] = undef;
267             }
268              
269             # merge @$src_line_info into @$dst_line_info
270 623         3052 $dst_line_info->[$_] += $src_line_info->[$_] for (
271             NYTP_SCi_CALL_COUNT, NYTP_SCi_INCL_RTIME, NYTP_SCi_EXCL_RTIME,
272             );
273 623         2408 $dst_line_info->[NYTP_SCi_REC_DEPTH] = max($dst_line_info->[NYTP_SCi_REC_DEPTH],
274             $src_line_info->[NYTP_SCi_REC_DEPTH]);
275             # ug, we can't really combine recursive incl_time, but this is better than undef
276 623         1526 $dst_line_info->[NYTP_SCi_RECI_RTIME] = max($dst_line_info->[NYTP_SCi_RECI_RTIME],
277             $src_line_info->[NYTP_SCi_RECI_RTIME]);
278              
279 623   50     2024 my $src_cs = $src_line_info->[NYTP_SCi_CALLING_SUB]|| {};
280 623   100     1963 my $dst_cs = $dst_line_info->[NYTP_SCi_CALLING_SUB]||={};
281 623         2501 $dst_cs->{$_} = $src_cs->{$_} for keys %$src_cs;
282              
283 623 50       2206 warn sprintf " = %s\n", _fmt_sc($dst_line_info)
284             if trace_level() >= 5;
285              
286 623         3383 return;
287             }
288              
289             sub _fmt_sc {
290 0     0   0 my ($sc) = @_;
291 0 0       0 return "(empty)" if !@$sc;
292 0   0     0 my $dst_cs = $sc->[NYTP_SCi_CALLING_SUB]||{};
293 0         0 my $by = join " & ", sort keys %$dst_cs;
294 0 0       0 sprintf "calls %d%s",
295             $sc->[NYTP_SCi_CALL_COUNT], ($by) ? ", by $by" : "";
296             }
297              
298              
299             sub caller_fids {
300 128     128 0 395 my ($self, $merge_evals) = @_;
301 128   50     746 my $callers = $self->caller_fid_line_places($merge_evals) || {};
302 128         587 my @fids = keys %$callers;
303 128         737 return @fids; # count in scalar context
304             }
305              
306 1     1 0 8 sub caller_count { return scalar shift->caller_places; } # XXX deprecate later
307              
308             # array of [ $fid, $line, $sub_call_info ], ...
309             sub caller_places {
310 2254     2254 0 5074 my ($self, $merge_evals) = @_;
311 2254   50     7530 my $callers = $self->caller_fid_line_places || {};
312              
313 2254         3906 my @callers;
314 2254         10493 for my $fid (sort { $a <=> $b } keys %$callers) {
  196         760  
315 1416         3390 my $lines_hash = $callers->{$fid};
316 1416         6150 for my $line (sort { $a <=> $b } keys %$lines_hash) {
  1157         3396  
317 2210         7530 push @callers, [ $fid, $line, $lines_hash->{$line} ];
318             }
319             }
320              
321 2254         8380 return @callers; # scalar: number of distinct calling locations
322             }
323              
324             sub normalize_for_test {
325 2450     2450 0 5532 my $self = shift;
326 2450         7567 my $profile = $self->profile;
327              
328             # normalize eval sequence numbers in anon sub names to 0
329             $self->[NYTP_SIi_SUB_NAME] =~ s/ \( ((?:re_)?) eval \s \d+ \) /(${1}eval 0)/xg
330             if $self->[NYTP_SIi_SUB_NAME] =~ m/__ANON__/
331 2450 100 100     13900 && not $ENV{NYTPROF_TEST_SKIP_EVAL_NORM};
332              
333             # zero subroutine inclusive time
334 2450         6249 $self->[NYTP_SIi_INCL_RTIME] = 0;
335 2450         6002 $self->[NYTP_SIi_EXCL_RTIME] = 0;
336 2450         5264 $self->[NYTP_SIi_RECI_RTIME] = 0;
337              
338             # { fid => { line => [ count, incl, excl, ... ] } }
339 2450   100     12880 my $callers = $self->[NYTP_SIi_CALLED_BY] || {};
340              
341             # calls from modules shipped with perl cause problems for tests
342             # because the line numbers vary between perl versions, so here we
343             # edit the line number of calls from these modules
344 2450         11489 for my $fid (keys %$callers) {
345 2015 100       6160 next if not $fid;
346 1548 50       6284 my $fileinfo = $profile->fileinfo_of($fid) or next;
347 1548 100       4816 next if $fileinfo->filename !~ /(AutoLoader|Exporter)\.pm$/;
348              
349             # normalize the lines X,Y,Z to 1,2,3
350 144         267 my %lines = %{ delete $callers->{$fid} };
  144         650  
351 144         787 my @lines = @lines{sort { $a <=> $b } keys %lines};
  32         310  
352 144         362 $callers->{$fid} = { map { $_ => shift @lines } 1..@lines };
  176         950  
353             }
354              
355 2450         8440 for my $sc (map { values %$_ } values %$callers) {
  2015         8279  
356             # zero per-call-location subroutine inclusive time
357 2395         7881 $sc->[NYTP_SCi_INCL_RTIME] =
358             $sc->[NYTP_SCi_EXCL_RTIME] =
359             $sc->[NYTP_SCi_RECI_RTIME] = 0;
360              
361 2395 100       7100 if (not $ENV{NYTPROF_TEST_SKIP_EVAL_NORM}) {
362             # normalize eval sequence numbers in anon sub names to 0
363 2235   50     6474 my $names = $sc->[NYTP_SCi_CALLING_SUB]||{};
364 2235         7089 for my $subname (keys %$names) {
365 2235         6286 (my $newname = $subname) =~ s/ \( ((?:re_)?) eval \s \d+ \) /(${1}eval 0)/xg;
366 2235 100       8016 next if $newname eq $subname;
367             warn "Normalizing $subname to $newname overwrote other calling-sub data\n"
368 16 50       103 if $names->{$newname};
369 16         102 $names->{$newname} = delete $names->{$subname};
370             }
371             }
372              
373             }
374 2450         9682 return $self->[NYTP_SIi_SUB_NAME];
375             }
376              
377             sub dump {
378 2252     2252 0 7146 my ($self, $separator, $fh, $path, $prefix) = @_;
379              
380 2252         4137 my ($fid, $l1, $l2, $calls) = @{$self}[
  2252         6171  
381             NYTP_SIi_FID, NYTP_SIi_FIRST_LINE, NYTP_SIi_LAST_LINE, NYTP_SIi_CALL_COUNT
382             ];
383 2252         4764 my @values = @{$self}[
  2252         5746  
384             NYTP_SIi_INCL_RTIME, NYTP_SIi_EXCL_RTIME,
385             NYTP_SIi_REC_DEPTH, NYTP_SIi_RECI_RTIME
386             ];
387             printf $fh "%s[ %s:%s-%s calls %s times %s ]\n",
388             $prefix,
389 9008 100       18854 map({ defined($_) ? $_ : 'undef' } $fid, $l1, $l2, $calls),
390 2252 50       5608 join(" ", map { defined($_) ? $_ : 'undef' } @values);
  9008         28674  
391              
392 2252         8725 my @caller_places = $self->caller_places;
393 2252         6717 for my $cp (@caller_places) {
394 2210         5585 my ($fid, $line, $sc) = @$cp;
395 2210         5246 my @sc = @$sc;
396 2210         3693 $sc[NYTP_SCi_CALLING_SUB] = join "|", sort keys %{ $sc[NYTP_SCi_CALLING_SUB] };
  2210         6846  
397             printf $fh "%s%s%s%d:%d%s[ %s ]\n",
398             $prefix,
399             'called_by', $separator,
400             $fid, $line, $separator,
401 2210 50       5524 join(" ", map { defined($_) ? $_ : 'undef' } @sc);
  17680         45791  
402             }
403              
404             # where a sub has had others merged into it, list them
405 2252   100     7063 my $merge_subs = $self->meta->{merged_sub_names} || [];
406 2252         12778 for my $ms (sort @$merge_subs) {
407 80         543 printf $fh "%s%s%s%s\n",
408             $prefix, 'merge_donor', $separator, $ms;
409             }
410             }
411              
412             # vim:ts=8:sw=4:et
413             1;