| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | package Devel::NYTProf::FileInfo;    # fid_fileinfo | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | =head1 NAME | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | Devel::NYTProf::FileInfo | 
| 6 |  |  |  |  |  |  |  | 
| 7 |  |  |  |  |  |  | =head1 METHODS | 
| 8 |  |  |  |  |  |  |  | 
| 9 |  |  |  |  |  |  | =cut | 
| 10 |  |  |  |  |  |  |  | 
| 11 | 48 |  |  | 48 |  | 334 | use strict; | 
|  | 48 |  |  |  |  | 99 |  | 
|  | 48 |  |  |  |  | 1420 |  | 
| 12 |  |  |  |  |  |  |  | 
| 13 | 48 |  |  | 48 |  | 260 | use Carp; | 
|  | 48 |  |  |  |  | 117 |  | 
|  | 48 |  |  |  |  | 2737 |  | 
| 14 | 48 |  |  | 48 |  | 294 | use Config; | 
|  | 48 |  |  |  |  | 118 |  | 
|  | 48 |  |  |  |  | 1811 |  | 
| 15 | 48 |  |  | 48 |  | 342 | use List::Util qw(sum); | 
|  | 48 |  |  |  |  | 129 |  | 
|  | 48 |  |  |  |  | 3295 |  | 
| 16 |  |  |  |  |  |  |  | 
| 17 | 48 |  |  | 48 |  | 21577 | use Devel::NYTProf::Util qw(strip_prefix_from_paths trace_level); | 
|  | 48 |  |  |  |  | 592 |  | 
|  | 48 |  |  |  |  | 4101 |  | 
| 18 |  |  |  |  |  |  |  | 
| 19 | 48 |  |  |  |  | 8407 | use Devel::NYTProf::Constants qw( | 
| 20 |  |  |  |  |  |  | NYTP_FIDf_HAS_SRC NYTP_FIDf_SAVE_SRC NYTP_FIDf_IS_FAKE NYTP_FIDf_IS_PMC | 
| 21 |  |  |  |  |  |  | NYTP_FIDf_IS_EVAL | 
| 22 |  |  |  |  |  |  |  | 
| 23 |  |  |  |  |  |  | NYTP_FIDi_FILENAME NYTP_FIDi_EVAL_FID NYTP_FIDi_EVAL_LINE NYTP_FIDi_FID | 
| 24 |  |  |  |  |  |  | NYTP_FIDi_FLAGS NYTP_FIDi_FILESIZE NYTP_FIDi_FILEMTIME NYTP_FIDi_PROFILE | 
| 25 |  |  |  |  |  |  | NYTP_FIDi_EVAL_FI NYTP_FIDi_HAS_EVALS NYTP_FIDi_SUBS_DEFINED NYTP_FIDi_SUBS_CALLED | 
| 26 |  |  |  |  |  |  | NYTP_FIDi_elements | 
| 27 |  |  |  |  |  |  |  | 
| 28 |  |  |  |  |  |  | NYTP_SCi_CALL_COUNT NYTP_SCi_INCL_RTIME NYTP_SCi_EXCL_RTIME NYTP_SCi_RECI_RTIME | 
| 29 |  |  |  |  |  |  | NYTP_SCi_REC_DEPTH NYTP_SCi_CALLING_SUB | 
| 30 |  |  |  |  |  |  | NYTP_SCi_elements | 
| 31 | 48 |  |  | 48 |  | 20009 | ); | 
|  | 48 |  |  |  |  | 141 |  | 
| 32 |  |  |  |  |  |  |  | 
| 33 |  |  |  |  |  |  | # extra constants for private elements | 
| 34 |  |  |  |  |  |  | use constant { | 
| 35 | 48 |  |  |  |  | 193690 | NYTP_FIDi_meta            => NYTP_FIDi_elements + 1, | 
| 36 |  |  |  |  |  |  | NYTP_FIDi_cache           => NYTP_FIDi_elements + 2, | 
| 37 | 48 |  |  | 48 |  | 424 | }; | 
|  | 48 |  |  |  |  | 231 |  | 
| 38 |  |  |  |  |  |  |  | 
| 39 | 17928 |  |  | 17928 | 0 | 96654 | sub filename  { shift->[NYTP_FIDi_FILENAME()] } | 
| 40 | 465 |  |  | 465 | 0 | 2867 | sub eval_fid  { shift->[NYTP_FIDi_EVAL_FID()] } | 
| 41 | 3825 |  |  | 3825 | 0 | 26769 | sub eval_line { shift->[NYTP_FIDi_EVAL_LINE()] } | 
| 42 | 22222 |  |  | 22222 | 0 | 118720 | sub fid       { shift->[NYTP_FIDi_FID()] } | 
| 43 | 1 |  |  | 1 | 0 | 5 | sub size      { shift->[NYTP_FIDi_FILESIZE()] } | 
| 44 | 1 |  |  | 1 | 0 | 10 | sub mtime     { shift->[NYTP_FIDi_FILEMTIME()] } | 
| 45 | 2636 |  |  | 2636 | 0 | 9174 | sub profile   { shift->[NYTP_FIDi_PROFILE()] } | 
| 46 | 387 |  |  | 387 | 0 | 1512 | sub flags     { shift->[NYTP_FIDi_FLAGS()] } | 
| 47 |  |  |  |  |  |  |  | 
| 48 |  |  |  |  |  |  | # if an eval then return fileinfo obj for the fid that executed the eval | 
| 49 | 18 |  |  | 18 | 0 | 108 | sub eval_fi   { shift->[NYTP_FIDi_EVAL_FI()] } | 
| 50 |  |  |  |  |  |  | #   is_eval is true only for simple string evals (doesn't consider NYTP_FIDf_IS_EVAL) | 
| 51 | 30 | 50 |  | 30 | 0 | 181 | sub is_eval   { shift->[NYTP_FIDi_EVAL_FI()] ? 1 : 0 } | 
| 52 |  |  |  |  |  |  |  | 
| 53 | 23 |  |  | 23 | 0 | 71 | sub is_fake   { shift->flags & NYTP_FIDf_IS_FAKE } | 
| 54 |  |  |  |  |  |  | sub is_file   { | 
| 55 | 4 |  |  | 4 | 0 | 9 | my $self = shift; | 
| 56 | 4 |  | 33 |  |  | 11 | return not ($self->is_fake or $self->is_eval or $self->flags & NYTP_FIDf_IS_EVAL()); | 
| 57 |  |  |  |  |  |  | } | 
| 58 |  |  |  |  |  |  |  | 
| 59 |  |  |  |  |  |  | # general purpose hash - mainly a hack to help kill off Reader.pm | 
| 60 | 646 |  | 100 | 646 | 0 | 8533 | sub meta      { shift->[NYTP_FIDi_meta()] ||= {} } | 
| 61 |  |  |  |  |  |  | # general purpose cache | 
| 62 | 928 |  | 100 | 928 | 0 | 8894 | sub cache     { shift->[NYTP_FIDi_cache()] ||= {} } | 
| 63 |  |  |  |  |  |  |  | 
| 64 |  |  |  |  |  |  | # array of fileinfo's for each string eval in the file | 
| 65 |  |  |  |  |  |  | sub has_evals { | 
| 66 | 4078 |  |  | 4078 | 0 | 10653 | my ($self, $include_nested) = @_; | 
| 67 |  |  |  |  |  |  |  | 
| 68 | 4078 | 100 |  |  |  | 19403 | my $eval_fis = $self->[NYTP_FIDi_HAS_EVALS()] | 
| 69 |  |  |  |  |  |  | or return; | 
| 70 | 848 | 100 |  |  |  | 5106 | return @$eval_fis if !$include_nested; | 
| 71 |  |  |  |  |  |  |  | 
| 72 | 96 |  |  |  |  | 325 | my @eval_fis = @$eval_fis; | 
| 73 |  |  |  |  |  |  | # walk down tree of nested evals, adding them to @fi | 
| 74 | 96 |  |  |  |  | 361 | for (my $i=0; my $fi = $eval_fis[$i]; ++$i) { | 
| 75 | 112 |  |  |  |  | 448 | push @eval_fis, $fi->has_evals(0); | 
| 76 |  |  |  |  |  |  | } | 
| 77 |  |  |  |  |  |  |  | 
| 78 | 96 |  |  |  |  | 354 | return @eval_fis; | 
| 79 |  |  |  |  |  |  | } | 
| 80 |  |  |  |  |  |  |  | 
| 81 |  |  |  |  |  |  |  | 
| 82 |  |  |  |  |  |  | sub sibling_evals { | 
| 83 | 1 |  |  | 1 | 0 | 5 | my ($self) = @_; | 
| 84 | 1 | 50 |  |  |  | 3 | my $parent_fi = $self->eval_fi | 
| 85 |  |  |  |  |  |  | or return; # not an eval | 
| 86 | 0 |  |  |  |  | 0 | my $eval_line = $self->eval_line; | 
| 87 | 0 |  |  |  |  | 0 | return grep { $_->eval_line == $eval_line } $parent_fi->has_evals; | 
|  | 0 |  |  |  |  | 0 |  | 
| 88 |  |  |  |  |  |  | } | 
| 89 |  |  |  |  |  |  |  | 
| 90 |  |  |  |  |  |  |  | 
| 91 |  |  |  |  |  |  | sub _nullify { | 
| 92 | 224 |  |  | 224 |  | 575 | my $self = shift; | 
| 93 | 224 |  |  |  |  | 1557 | @$self = (); # Zap! | 
| 94 |  |  |  |  |  |  | } | 
| 95 |  |  |  |  |  |  |  | 
| 96 |  |  |  |  |  |  |  | 
| 97 |  |  |  |  |  |  |  | 
| 98 |  |  |  |  |  |  | # return subs defined as list of SubInfo objects | 
| 99 |  |  |  |  |  |  | sub subs_defined { | 
| 100 | 1561 |  |  | 1561 | 0 | 4418 | my ($self, $incl_nested_evals) = @_; | 
| 101 |  |  |  |  |  |  |  | 
| 102 | 1561 | 50 |  |  |  | 4396 | return map { $_->subs_defined(0) } $self, $self->has_evals(1) | 
|  | 0 |  |  |  |  | 0 |  | 
| 103 |  |  |  |  |  |  | if $incl_nested_evals; | 
| 104 |  |  |  |  |  |  |  | 
| 105 | 1561 |  |  |  |  | 2975 | return values %{ $self->[NYTP_FIDi_SUBS_DEFINED()] }; | 
|  | 1561 |  |  |  |  | 14909 |  | 
| 106 |  |  |  |  |  |  | } | 
| 107 |  |  |  |  |  |  |  | 
| 108 |  |  |  |  |  |  | sub subs_defined_sorted { | 
| 109 | 1139 |  |  | 1139 | 0 | 3980 | my ($self, $incl_nested_evals) = @_; | 
| 110 | 1139 |  |  |  |  | 4722 | return sort { $a->subname cmp $b->subname } $self->subs_defined($incl_nested_evals); | 
|  | 3357 |  |  |  |  | 11054 |  | 
| 111 |  |  |  |  |  |  | } | 
| 112 |  |  |  |  |  |  |  | 
| 113 |  |  |  |  |  |  | sub _remove_sub_defined { | 
| 114 | 256 |  |  | 256 |  | 751 | my ($self, $si) = @_; | 
| 115 | 256 |  |  |  |  | 1026 | my $subname = $si->subname; | 
| 116 | 256 | 50 |  |  |  | 1286 | delete $self->[NYTP_FIDi_SUBS_DEFINED()]->{$subname} | 
| 117 |  |  |  |  |  |  | or carp sprintf "_remove_sub_defined: sub %s wasn't defined in %d %s", | 
| 118 |  |  |  |  |  |  | $subname, $self->fid, $self->filename; | 
| 119 |  |  |  |  |  |  | } | 
| 120 |  |  |  |  |  |  |  | 
| 121 |  |  |  |  |  |  | sub _add_new_sub_defined { | 
| 122 | 240 |  |  | 240 |  | 952 | my ($self, $subinfo) = @_; | 
| 123 | 240 |  |  |  |  | 1063 | my $subname = $subinfo->subname; | 
| 124 | 240 |  | 50 |  |  | 1048 | my $subs_defined = $self->[NYTP_FIDi_SUBS_DEFINED()] ||= {}; | 
| 125 | 240 |  |  |  |  | 665 | my $existing_si = $subs_defined->{$subname}; | 
| 126 | 240 | 50 |  |  |  | 731 | croak sprintf "sub %s already defined in fid %d %s", | 
| 127 |  |  |  |  |  |  | $subname, $self->fid, $self->filename | 
| 128 |  |  |  |  |  |  | if $existing_si; | 
| 129 |  |  |  |  |  |  |  | 
| 130 | 240 |  |  |  |  | 1148 | $subs_defined->{$subname} = $subinfo; | 
| 131 |  |  |  |  |  |  | } | 
| 132 |  |  |  |  |  |  |  | 
| 133 |  |  |  |  |  |  |  | 
| 134 |  |  |  |  |  |  | =head2 sub_call_lines | 
| 135 |  |  |  |  |  |  |  | 
| 136 |  |  |  |  |  |  | $hash = $fi->sub_call_lines; | 
| 137 |  |  |  |  |  |  |  | 
| 138 |  |  |  |  |  |  | Returns a reference to a hash containing information about subroutine calls | 
| 139 |  |  |  |  |  |  | made at individual lines within the source file. | 
| 140 |  |  |  |  |  |  | Returns undef if no subroutine calling information is available. | 
| 141 |  |  |  |  |  |  |  | 
| 142 |  |  |  |  |  |  | The keys of the returned hash are line numbers. The values are references to | 
| 143 |  |  |  |  |  |  | hashes with fully qualified subroutine names as keys. Each hash value is an | 
| 144 |  |  |  |  |  |  | reference to an array containing an integer call count (how many times the sub | 
| 145 |  |  |  |  |  |  | was called from that line of that file) and an inclusive time (how much time | 
| 146 |  |  |  |  |  |  | was spent inside the sub when it was called from that line of that file). | 
| 147 |  |  |  |  |  |  |  | 
| 148 |  |  |  |  |  |  | For example, if the following was line 42 of a file C: | 
| 149 |  |  |  |  |  |  |  | 
| 150 |  |  |  |  |  |  | ++$wiggle if foo(24) == bar(42); | 
| 151 |  |  |  |  |  |  |  | 
| 152 |  |  |  |  |  |  | that line was executed once, and foo and bar were imported from pkg1, then | 
| 153 |  |  |  |  |  |  | sub_call_lines() would return something like: | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | { | 
| 156 |  |  |  |  |  |  | 42 => { | 
| 157 |  |  |  |  |  |  | 'pkg1::foo' => [ 1, 0.02093 ], | 
| 158 |  |  |  |  |  |  | 'pkg1::bar' => [ 1, 0.00154 ], | 
| 159 |  |  |  |  |  |  | }, | 
| 160 |  |  |  |  |  |  | } | 
| 161 |  |  |  |  |  |  |  | 
| 162 |  |  |  |  |  |  | =cut | 
| 163 |  |  |  |  |  |  |  | 
| 164 | 2504 |  |  | 2504 | 1 | 11573 | sub sub_call_lines  { shift->[NYTP_FIDi_SUBS_CALLED()] } | 
| 165 |  |  |  |  |  |  |  | 
| 166 |  |  |  |  |  |  |  | 
| 167 |  |  |  |  |  |  | =head2 evals_by_line | 
| 168 |  |  |  |  |  |  |  | 
| 169 |  |  |  |  |  |  | # { line => { fid_of_eval_at_line => $fi, ... }, ... } | 
| 170 |  |  |  |  |  |  | $hash = $fi->evals_by_line; | 
| 171 |  |  |  |  |  |  |  | 
| 172 |  |  |  |  |  |  | Returns a reference to a hash containing information about string evals | 
| 173 |  |  |  |  |  |  | executed at individual lines within a source file. | 
| 174 |  |  |  |  |  |  |  | 
| 175 |  |  |  |  |  |  | The keys of the returned hash are line numbers. The values are references to | 
| 176 |  |  |  |  |  |  | hashes with file id integers as keys and FileInfo objects as values. | 
| 177 |  |  |  |  |  |  |  | 
| 178 |  |  |  |  |  |  | =cut | 
| 179 |  |  |  |  |  |  |  | 
| 180 |  |  |  |  |  |  | sub evals_by_line { | 
| 181 | 19 |  |  | 19 | 1 | 118 | my ($self) = @_; | 
| 182 |  |  |  |  |  |  |  | 
| 183 |  |  |  |  |  |  | # find all fids that have this fid as an eval_fid | 
| 184 |  |  |  |  |  |  | # { line => { fid_of_eval_at_line => $fi, ... } } | 
| 185 |  |  |  |  |  |  |  | 
| 186 | 19 |  |  |  |  | 44 | my %evals_by_line; | 
| 187 | 19 |  |  |  |  | 58 | for my $fi ($self->has_evals) { | 
| 188 | 0 |  |  |  |  | 0 | $evals_by_line{ $fi->eval_line }->{ $fi->fid } = $fi; | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  |  | 
| 191 | 19 |  |  |  |  | 186 | return \%evals_by_line; | 
| 192 |  |  |  |  |  |  | } | 
| 193 |  |  |  |  |  |  |  | 
| 194 |  |  |  |  |  |  |  | 
| 195 |  |  |  |  |  |  | sub line_time_data { | 
| 196 | 468 |  |  | 468 | 0 | 1400 | my ($self, $levels) = @_; | 
| 197 | 468 |  | 100 |  |  | 4093 | $levels ||= [ 'line' ]; | 
| 198 |  |  |  |  |  |  | # XXX this can be optimized once the fidinfo contains directs refs to the data | 
| 199 | 468 |  |  |  |  | 1412 | my $profile = $self->profile; | 
| 200 | 468 |  |  |  |  | 1214 | my $fid = $self->fid; | 
| 201 | 468 |  |  |  |  | 1536 | for my $level (@$levels) { | 
| 202 | 468 |  |  |  |  | 3413 | my $fid_ary = $profile->get_fid_line_data($level); | 
| 203 | 468 | 50 | 33 |  |  | 3816 | return $fid_ary->[$fid] if $fid_ary && $fid_ary->[$fid]; | 
| 204 |  |  |  |  |  |  | } | 
| 205 | 0 |  |  |  |  | 0 | return undef; | 
| 206 |  |  |  |  |  |  | } | 
| 207 |  |  |  |  |  |  |  | 
| 208 |  |  |  |  |  |  | sub excl_time { # total exclusive time for fid | 
| 209 | 1 |  |  | 1 | 0 | 2 | my $self = shift; | 
| 210 | 1 |  | 50 |  |  | 5 | my $line_data = $self->line_time_data([qw(sub block line)]) | 
| 211 |  |  |  |  |  |  | || return undef; | 
| 212 | 1 |  |  |  |  | 2 | my $excl_time = 0; | 
| 213 | 1 |  |  |  |  | 5 | for (@$line_data) { | 
| 214 | 19 | 100 |  |  |  | 41 | next unless $_; | 
| 215 | 6 |  |  |  |  | 10 | $excl_time += $_->[0]; | 
| 216 |  |  |  |  |  |  | } | 
| 217 | 1 |  |  |  |  | 3 | return $excl_time; | 
| 218 |  |  |  |  |  |  | } | 
| 219 |  |  |  |  |  |  |  | 
| 220 |  |  |  |  |  |  |  | 
| 221 |  |  |  |  |  |  | sub sum_of_stmts_count { | 
| 222 | 16 |  |  | 16 | 0 | 9138 | my ($self, $incl_nested_evals) = @_; | 
| 223 |  |  |  |  |  |  |  | 
| 224 | 16 | 50 |  |  |  | 56 | return sum(map { $_->sum_of_stmts_count(0) } $self, $self->has_evals(1)) | 
|  | 0 |  |  |  |  | 0 |  | 
| 225 |  |  |  |  |  |  | if $incl_nested_evals; | 
| 226 |  |  |  |  |  |  |  | 
| 227 | 16 |  |  |  |  | 93 | my $ref = \$self->cache->{NYTP_FIDi_sum_stmts_count}; | 
| 228 | 16 | 50 |  |  |  | 91 | $$ref = $self->_sum_of_line_time_data(1) | 
| 229 |  |  |  |  |  |  | if not defined $$ref; | 
| 230 |  |  |  |  |  |  |  | 
| 231 | 16 |  |  |  |  | 142 | return $$ref; | 
| 232 |  |  |  |  |  |  | } | 
| 233 |  |  |  |  |  |  |  | 
| 234 |  |  |  |  |  |  | sub sum_of_stmts_time { | 
| 235 | 16 |  |  | 16 | 0 | 49 | my ($self, $incl_nested_evals) = @_; | 
| 236 |  |  |  |  |  |  |  | 
| 237 | 16 | 50 |  |  |  | 57 | return sum(map { $_->sum_of_stmts_time(0) } $self, $self->has_evals(1)) | 
|  | 0 |  |  |  |  | 0 |  | 
| 238 |  |  |  |  |  |  | if $incl_nested_evals; | 
| 239 |  |  |  |  |  |  |  | 
| 240 | 16 |  |  |  |  | 37 | my $ref = \$self->cache->{NYTP_FIDi_sum_stmts_times}; | 
| 241 | 16 | 50 |  |  |  | 118 | $$ref = $self->_sum_of_line_time_data(0) | 
| 242 |  |  |  |  |  |  | if not defined $$ref; | 
| 243 |  |  |  |  |  |  |  | 
| 244 | 16 |  |  |  |  | 114 | return $$ref; | 
| 245 |  |  |  |  |  |  | } | 
| 246 |  |  |  |  |  |  |  | 
| 247 |  |  |  |  |  |  | sub _sum_of_line_time_data { | 
| 248 | 32 |  |  | 32 |  | 103 | my ($self, $idx) = @_; | 
| 249 | 32 |  |  |  |  | 65 | my $line_time_data = $self->line_time_data; | 
| 250 | 32 |  |  |  |  | 65 | my $sum = 0; | 
| 251 | 32 |  | 100 |  |  | 361 | $sum += $_->[$idx]||0 for @$line_time_data; | 
| 252 | 32 |  |  |  |  | 95 | return $sum; | 
| 253 |  |  |  |  |  |  | } | 
| 254 |  |  |  |  |  |  |  | 
| 255 |  |  |  |  |  |  |  | 
| 256 |  |  |  |  |  |  | sub outer { | 
| 257 | 1 |  |  | 1 | 0 | 5 | my ($self, $recurse) = @_; | 
| 258 | 1 | 50 |  |  |  | 3 | my $fi  = $self->eval_fi | 
| 259 |  |  |  |  |  |  | or return; | 
| 260 | 0 |  |  |  |  | 0 | my $prev = $self; | 
| 261 |  |  |  |  |  |  |  | 
| 262 | 0 |  | 0 |  |  | 0 | while ($recurse and my $eval_fi = $fi->eval_fi) { | 
| 263 | 0 |  |  |  |  | 0 | $prev = $fi; | 
| 264 | 0 |  |  |  |  | 0 | $fi = $eval_fi; | 
| 265 |  |  |  |  |  |  | } | 
| 266 | 0 | 0 |  |  |  | 0 | return $fi unless wantarray; | 
| 267 | 0 |  |  |  |  | 0 | return ($fi, $prev->eval_line); | 
| 268 |  |  |  |  |  |  | } | 
| 269 |  |  |  |  |  |  |  | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | sub is_pmc { | 
| 272 | 359 |  |  | 359 | 0 | 1342 | return (shift->flags & NYTP_FIDf_IS_PMC()); | 
| 273 |  |  |  |  |  |  | } | 
| 274 |  |  |  |  |  |  |  | 
| 275 |  |  |  |  |  |  |  | 
| 276 |  |  |  |  |  |  | sub collapse_sibling_evals { | 
| 277 | 192 |  |  | 192 | 0 | 1112 | my ($self, $survivor_fi, @donors) = @_; | 
| 278 | 192 |  |  |  |  | 797 | my $profile = $self->profile; | 
| 279 |  |  |  |  |  |  |  | 
| 280 |  |  |  |  |  |  | die "Can't collapse_sibling_evals of non-sibling evals" | 
| 281 | 192 | 50 |  |  |  | 629 | if grep { $_->eval_fid  != $survivor_fi->eval_fid or | 
|  | 224 | 50 |  |  |  | 885 |  | 
| 282 |  |  |  |  |  |  | $_->eval_line != $survivor_fi->eval_line | 
| 283 |  |  |  |  |  |  | } @donors; | 
| 284 |  |  |  |  |  |  |  | 
| 285 | 192 |  |  |  |  | 1569 | my $s_ltd = $survivor_fi->line_time_data; # XXX line only | 
| 286 | 192 |  |  |  |  | 854 | my $s_scl = $survivor_fi->sub_call_lines; | 
| 287 | 192 |  |  |  |  | 436 | my %donor_fids; | 
| 288 |  |  |  |  |  |  |  | 
| 289 | 192 |  |  |  |  | 1247 | for my $donor_fi (@donors) { | 
| 290 |  |  |  |  |  |  | # copy data from donor to survivor_fi then delete donor | 
| 291 | 224 |  |  |  |  | 894 | my $donor_fid = $donor_fi->fid; | 
| 292 | 224 |  |  |  |  | 1825 | $donor_fids{$donor_fid} = $donor_fi; | 
| 293 |  |  |  |  |  |  |  | 
| 294 | 224 | 50 |  |  |  | 1308 | warn sprintf "collapse_sibling_evals: processing donor fid %d: %s\n", | 
| 295 |  |  |  |  |  |  | $donor_fid, $donor_fi->filename | 
| 296 |  |  |  |  |  |  | if trace_level() >= 3; | 
| 297 |  |  |  |  |  |  |  | 
| 298 |  |  |  |  |  |  | # XXX nested evals not handled yet | 
| 299 | 224 | 50 |  |  |  | 716 | warn sprintf "collapse_sibling_evals: nested evals in %s not handled", | 
| 300 |  |  |  |  |  |  | $donor_fi->filename | 
| 301 |  |  |  |  |  |  | if $donor_fi->has_evals; | 
| 302 |  |  |  |  |  |  |  | 
| 303 |  |  |  |  |  |  | # for each sub defined in the donor, | 
| 304 |  |  |  |  |  |  | # move the sub definition to the survivor | 
| 305 | 224 | 100 |  |  |  | 1041 | if (my @subs_defined = $donor_fi->subs_defined) { | 
| 306 |  |  |  |  |  |  |  | 
| 307 | 128 |  |  |  |  | 452 | for my $si (@subs_defined) { | 
| 308 | 128 | 50 |  |  |  | 560 | warn sprintf " - moving from fid %d: sub %s\n", | 
| 309 |  |  |  |  |  |  | $donor_fid, $si->subname | 
| 310 |  |  |  |  |  |  | if trace_level() >= 4; | 
| 311 | 128 |  |  |  |  | 1640 | $si->_alter_fileinfo($donor_fi, $survivor_fi); | 
| 312 | 128 | 50 |  |  |  | 765 | warn sprintf " - moving done\n" | 
| 313 |  |  |  |  |  |  | if trace_level() >= 4; | 
| 314 |  |  |  |  |  |  | } | 
| 315 |  |  |  |  |  |  | } | 
| 316 |  |  |  |  |  |  |  | 
| 317 |  |  |  |  |  |  | # for each sub call made by the donor, | 
| 318 |  |  |  |  |  |  | # move the sub calls to the survivor | 
| 319 |  |  |  |  |  |  | # 1 => { 'main::foo' => [ 1, '1.38e-05', '1.24e-05', ..., { 'main::RUNTIME' => undef } ] } | 
| 320 | 224 | 50 |  |  |  | 741 | if (my $sub_call_lines = $donor_fi->sub_call_lines) { | 
| 321 |  |  |  |  |  |  |  | 
| 322 | 224 |  |  |  |  | 457 | my %subnames_called_by_donor; | 
| 323 |  |  |  |  |  |  |  | 
| 324 |  |  |  |  |  |  | # merge details of subs called from $donor_fi | 
| 325 | 224 |  |  |  |  | 1315 | while ( my ($line, $sc_hash) = each %$sub_call_lines ) { | 
| 326 | 176 |  | 50 |  |  | 775 | my $s_sc_hash = $s_scl->{$line} ||= {}; | 
| 327 | 176 |  |  |  |  | 766 | for my $subname (keys %$sc_hash ) { | 
| 328 | 176 |  | 100 |  |  | 1214 | my $s_sc_info = $s_sc_hash->{$subname} ||= []; | 
| 329 | 176 |  |  |  |  | 601 | my $sc_info   = delete $sc_hash->{$subname}; | 
| 330 | 176 |  |  |  |  | 2666 | Devel::NYTProf::SubInfo::_merge_in_caller_info($s_sc_info, $sc_info, | 
| 331 |  |  |  |  |  |  | tag => "line $line calls to $subname", | 
| 332 |  |  |  |  |  |  | ); | 
| 333 |  |  |  |  |  |  |  | 
| 334 | 176 |  |  |  |  | 1972 | $subnames_called_by_donor{$subname}++; | 
| 335 |  |  |  |  |  |  | } | 
| 336 |  |  |  |  |  |  | } | 
| 337 | 224 |  |  |  |  | 781 | %$sub_call_lines = (); # zap | 
| 338 |  |  |  |  |  |  |  | 
| 339 |  |  |  |  |  |  | # update subinfo (NYTP_SIi_CALLED_BY) | 
| 340 |  |  |  |  |  |  | $profile->subinfo_of($_)->_alter_called_by_fileinfo($donor_fi, $survivor_fi) | 
| 341 | 224 |  |  |  |  | 1699 | for keys %subnames_called_by_donor; | 
| 342 |  |  |  |  |  |  | } | 
| 343 |  |  |  |  |  |  |  | 
| 344 |  |  |  |  |  |  | # copy line time data | 
| 345 | 224 |  | 50 |  |  | 982 | my $d_ltd = $donor_fi->line_time_data || []; # XXX line only | 
| 346 | 224 |  |  |  |  | 1449 | for my $line (0..@$d_ltd-1) { | 
| 347 | 544 | 100 |  |  |  | 1743 | my $d_tld_l = $d_ltd->[$line] or next; | 
| 348 | 320 |  | 50 |  |  | 1200 | my $s_tld_l = $s_ltd->[$line] ||= []; | 
| 349 | 320 |  |  |  |  | 1240 | $s_tld_l->[$_] += $d_tld_l->[$_] for (0..@$d_tld_l-1); | 
| 350 | 320 |  |  |  |  | 693 | warn sprintf "%d:%d: @$s_tld_l from @$d_tld_l fid:%d\n", | 
| 351 |  |  |  |  |  |  | $survivor_fi->fid, $line, $donor_fid if 0; | 
| 352 |  |  |  |  |  |  | } | 
| 353 |  |  |  |  |  |  |  | 
| 354 | 224 |  |  |  |  | 476 | push @{ $survivor_fi->meta->{merged_fids} }, $donor_fid; | 
|  | 224 |  |  |  |  | 1016 |  | 
| 355 |  |  |  |  |  |  | ++$survivor_fi->meta->{merged_fids_src_varied} | 
| 356 | 224 | 50 |  |  |  | 877 | if $donor_fi->src_digest ne $survivor_fi->src_digest; | 
| 357 |  |  |  |  |  |  |  | 
| 358 | 224 |  |  |  |  | 1085 | $donor_fi->_nullify; | 
| 359 |  |  |  |  |  |  | } | 
| 360 |  |  |  |  |  |  |  | 
| 361 |  |  |  |  |  |  | # remove donors from parent NYTP_FIDi_HAS_EVALS | 
| 362 | 192 | 50 |  |  |  | 1445 | if (my $eval_fis = $self->[NYTP_FIDi_HAS_EVALS()]) { | 
| 363 | 192 |  |  |  |  | 723 | my %donors = map { +"$_" => 1 } @donors; | 
|  | 224 |  |  |  |  | 2813 |  | 
| 364 | 192 |  |  |  |  | 738 | my $count = @$eval_fis; | 
| 365 | 192 |  |  |  |  | 596 | @$eval_fis = grep { !$donors{$_} } @$eval_fis; | 
|  | 722 |  |  |  |  | 2524 |  | 
| 366 | 192 | 50 |  |  |  | 1678 | warn "_delete_eval mismatch" | 
| 367 |  |  |  |  |  |  | if @$eval_fis != $count - @donors; | 
| 368 |  |  |  |  |  |  | } | 
| 369 |  |  |  |  |  |  |  | 
| 370 |  |  |  |  |  |  | # update sawampersand_fid if it's one of the now-dead donors | 
| 371 | 192 | 50 | 50 |  |  | 1148 | if ($donor_fids{ $profile->attributes->{sawampersand_fid} || 0 }) { | 
| 372 | 0 |  |  |  |  | 0 | $profile->attributes->{sawampersand_fid} = $survivor_fi->fid; | 
| 373 |  |  |  |  |  |  | } | 
| 374 |  |  |  |  |  |  |  | 
| 375 |  |  |  |  |  |  | # now the fid merging is complete... | 
| 376 |  |  |  |  |  |  | # look for any anon subs that are effectively duplicates | 
| 377 |  |  |  |  |  |  | # (ie have the same name except for eval seqn) | 
| 378 |  |  |  |  |  |  | # if more than one for any given name we merge them | 
| 379 | 192 | 100 |  |  |  | 1285 | if (my @subs_defined = $survivor_fi->subs_defined_sorted) { | 
| 380 |  |  |  |  |  |  |  | 
| 381 |  |  |  |  |  |  | # bucket anon subs by normalized name | 
| 382 | 112 |  |  |  |  | 345 | my %newname; | 
| 383 | 112 |  |  |  |  | 434 | for my $si (@subs_defined) { | 
| 384 | 240 | 50 |  |  |  | 1375 | next unless $si->is_anon; | 
| 385 | 240 |  |  |  |  | 913 | (my $newname = $si->subname) =~ s/ \( ((?:re_)?) eval \s \d+ \) /(${1}eval 0)/xg; | 
| 386 | 240 |  |  |  |  | 706 | push @{ $newname{$newname} }, $si; | 
|  | 240 |  |  |  |  | 1540 |  | 
| 387 |  |  |  |  |  |  | } | 
| 388 |  |  |  |  |  |  |  | 
| 389 | 112 |  |  |  |  | 806 | while ( my ($newname, $to_merge) = each %newname ) { | 
| 390 | 112 |  |  |  |  | 385 | my $survivor_si = shift @$to_merge; | 
| 391 | 112 | 50 |  |  |  | 488 | next unless @$to_merge; # nothing to do | 
| 392 |  |  |  |  |  |  |  | 
| 393 | 112 |  |  |  |  | 427 | my $survivor_subname = $survivor_si->subname; | 
| 394 |  |  |  |  |  |  | warn sprintf "collapse_sibling_evals: merging %d subs into %s: %s\n", | 
| 395 |  |  |  |  |  |  | scalar @$to_merge, $survivor_subname, | 
| 396 | 112 | 50 |  |  |  | 653 | join ", ", map { $_->subname } @$to_merge | 
|  | 0 |  |  |  |  | 0 |  | 
| 397 |  |  |  |  |  |  | if trace_level() >= 3; | 
| 398 |  |  |  |  |  |  |  | 
| 399 | 112 |  |  |  |  | 379 | for my $delete_si (@$to_merge) { | 
| 400 | 128 |  |  |  |  | 368 | my $delete_subname = $delete_si->subname; | 
| 401 |  |  |  |  |  |  |  | 
| 402 |  |  |  |  |  |  | # for every file that called this sub, find the lines that made the calls | 
| 403 |  |  |  |  |  |  | # and change the name to the new sub | 
| 404 | 128 |  |  |  |  | 3258 | for my $caller_fid ($delete_si->caller_fids) { | 
| 405 | 128 |  |  |  |  | 968 | my $caller_fi = $profile->fileinfo_of($caller_fid); | 
| 406 |  |  |  |  |  |  | # sub_call_lines ==> { line => { sub => ... } } | 
| 407 | 128 |  |  |  |  | 291 | for my $subs_called_on_line (values %{ $caller_fi->sub_call_lines }) { | 
|  | 128 |  |  |  |  | 389 |  | 
| 408 | 704 | 100 |  |  |  | 2039 | my $sc_info = delete $subs_called_on_line->{$delete_subname} | 
| 409 |  |  |  |  |  |  | or next; | 
| 410 | 128 |  | 50 |  |  | 492 | my $s_sc_info = $subs_called_on_line->{$survivor_subname} ||= []; | 
| 411 | 128 |  |  |  |  | 752 | Devel::NYTProf::SubInfo::_merge_in_caller_info($s_sc_info, $sc_info, | 
| 412 |  |  |  |  |  |  | tag => "collapse eval $delete_subname", | 
| 413 |  |  |  |  |  |  | ); | 
| 414 |  |  |  |  |  |  | } | 
| 415 |  |  |  |  |  |  | } | 
| 416 |  |  |  |  |  |  |  | 
| 417 | 128 |  |  |  |  | 771 | $survivor_si->merge_in($delete_si); | 
| 418 | 128 |  |  |  |  | 511 | $survivor_fi->_remove_sub_defined($delete_si); | 
| 419 | 128 |  |  |  |  | 682 | $profile->_disconnect_subinfo($delete_si); | 
| 420 |  |  |  |  |  |  | } | 
| 421 |  |  |  |  |  |  | } | 
| 422 |  |  |  |  |  |  | } | 
| 423 |  |  |  |  |  |  |  | 
| 424 | 192 | 50 |  |  |  | 1424 | warn sprintf "collapse_sibling_evals done for ".$survivor_fi->filename."\n" | 
| 425 |  |  |  |  |  |  | if trace_level() >= 2; | 
| 426 |  |  |  |  |  |  |  | 
| 427 | 192 |  |  |  |  | 777 | return $survivor_fi; | 
| 428 |  |  |  |  |  |  | } | 
| 429 |  |  |  |  |  |  |  | 
| 430 |  |  |  |  |  |  |  | 
| 431 |  |  |  |  |  |  | # Should return the filename that the application used when loading the file | 
| 432 |  |  |  |  |  |  | # For evals should remove the @INC portion from within the "(eval N)[$path]" | 
| 433 |  |  |  |  |  |  | # and similarly for Class::MOP #line evals "... defined at $path". | 
| 434 |  |  |  |  |  |  | # This is a bit of a fudge. Filename handling should be improved in the profiler. | 
| 435 |  |  |  |  |  |  | sub filename_without_inc { | 
| 436 | 989 |  |  | 989 | 0 | 2726 | my $self = shift; | 
| 437 | 989 |  |  |  |  | 3015 | my $f    = [$self->filename]; | 
| 438 | 989 |  |  |  |  | 3417 | strip_prefix_from_paths([$self->profile->inc], $f, | 
| 439 |  |  |  |  |  |  | qr/(?: ^ | \[ | \sdefined\sat\s )/x | 
| 440 |  |  |  |  |  |  | ); | 
| 441 | 989 |  |  |  |  | 6590 | return $f->[0]; | 
| 442 |  |  |  |  |  |  | } | 
| 443 |  |  |  |  |  |  |  | 
| 444 |  |  |  |  |  |  |  | 
| 445 |  |  |  |  |  |  | sub abs_filename { | 
| 446 | 343 |  |  | 343 | 0 | 807 | my $self = shift; | 
| 447 |  |  |  |  |  |  |  | 
| 448 | 343 |  |  |  |  | 1306 | my $filename = $self->filename; | 
| 449 |  |  |  |  |  |  |  | 
| 450 |  |  |  |  |  |  | # strip of autosplit annotation, if any | 
| 451 | 343 |  |  |  |  | 1251 | $filename =~ s/ \(autosplit into .*//; | 
| 452 |  |  |  |  |  |  |  | 
| 453 |  |  |  |  |  |  | # if it's a .pmc then assume that's the file we want to look at | 
| 454 |  |  |  |  |  |  | # (because the main use for .pmc's are related to perl6) | 
| 455 | 343 | 50 |  |  |  | 1235 | $filename .= "c" if $self->is_pmc; | 
| 456 |  |  |  |  |  |  |  | 
| 457 |  |  |  |  |  |  | # search profile @INC if filename is not absolute | 
| 458 | 343 |  |  |  |  | 1118 | my @files = ($filename); | 
| 459 | 343 | 100 |  |  |  | 1192 | if ($filename !~ m/^\//) { | 
| 460 | 340 |  |  |  |  | 843 | my @inc = $self->profile->inc; | 
| 461 | 340 |  |  |  |  | 1212 | @files = map { "$_/$filename" } @inc; | 
|  | 4420 |  |  |  |  | 12379 |  | 
| 462 |  |  |  |  |  |  | } | 
| 463 |  |  |  |  |  |  |  | 
| 464 | 343 |  |  |  |  | 1207 | for my $file (@files) { | 
| 465 | 4423 | 50 |  |  |  | 49220 | return $file if -f $file; | 
| 466 |  |  |  |  |  |  | } | 
| 467 |  |  |  |  |  |  |  | 
| 468 |  |  |  |  |  |  | # returning the still-relative filename is better than returning an undef | 
| 469 | 343 |  |  |  |  | 2073 | return $filename; | 
| 470 |  |  |  |  |  |  | } | 
| 471 |  |  |  |  |  |  |  | 
| 472 |  |  |  |  |  |  | # has source code stored within the profile data file | 
| 473 |  |  |  |  |  |  | sub has_savesrc { | 
| 474 | 630 |  |  | 630 | 0 | 1354 | my $self = shift; | 
| 475 | 630 |  |  |  |  | 1877 | return $self->profile->{fid_srclines}[ $self->fid ]; | 
| 476 |  |  |  |  |  |  | } | 
| 477 |  |  |  |  |  |  |  | 
| 478 |  |  |  |  |  |  | sub srclines_array { | 
| 479 | 627 |  |  | 627 | 0 | 9495 | my $self = shift; | 
| 480 |  |  |  |  |  |  |  | 
| 481 | 627 | 100 |  |  |  | 1956 | if (my $srclines = $self->has_savesrc) { | 
| 482 | 300 |  |  |  |  | 1219 | my $copy = [ @$srclines ]; # shallow clone | 
| 483 | 300 |  |  |  |  | 771 | shift @$copy; # line 0 not used | 
| 484 | 300 |  |  |  |  | 1221 | return $copy; | 
| 485 |  |  |  |  |  |  | } | 
| 486 |  |  |  |  |  |  |  | 
| 487 | 327 |  |  |  |  | 1292 | my $filename = $self->abs_filename; | 
| 488 | 327 | 50 |  |  |  | 7720 | if (open my $fh, "<", $filename) { | 
| 489 | 0 |  |  |  |  | 0 | return [ <$fh> ]; | 
| 490 |  |  |  |  |  |  | } | 
| 491 |  |  |  |  |  |  |  | 
| 492 | 327 |  |  |  |  | 3570 | return undef; | 
| 493 |  |  |  |  |  |  | } | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | sub src_digest { | 
| 496 | 896 |  |  | 896 | 0 | 2097 | my $self = shift; | 
| 497 | 896 |  | 100 |  |  | 2410 | return $self->cache->{src_digest} ||= do { | 
| 498 | 608 |  | 100 |  |  | 2148 | my $srclines_array = $self->srclines_array || []; | 
| 499 | 608 |  |  |  |  | 2451 | my $src = join "\n", @$srclines_array; | 
| 500 |  |  |  |  |  |  | # return empty string for digest if there's no src | 
| 501 | 608 | 100 |  |  |  | 5644 | ($src) ? join ",", ( | 
| 502 |  |  |  |  |  |  | scalar @$srclines_array, # number of lines | 
| 503 |  |  |  |  |  |  | length $src,             # total length | 
| 504 |  |  |  |  |  |  | unpack("%32C*",$src) )   # 32-bit checksum | 
| 505 |  |  |  |  |  |  | : ''; | 
| 506 |  |  |  |  |  |  | }; | 
| 507 |  |  |  |  |  |  | } | 
| 508 |  |  |  |  |  |  |  | 
| 509 |  |  |  |  |  |  |  | 
| 510 |  |  |  |  |  |  | sub normalize_for_test { | 
| 511 | 963 |  |  | 963 | 0 | 3115 | my $self = shift; | 
| 512 |  |  |  |  |  |  |  | 
| 513 |  |  |  |  |  |  | # normalize eval sequence numbers in 'file' names to 0 | 
| 514 |  |  |  |  |  |  | $self->[NYTP_FIDi_FILENAME] =~ s/ \( ((?:re_)?) eval \s \d+ \) /(${1}eval 0)/xg | 
| 515 | 963 | 100 |  |  |  | 10030 | if not $ENV{NYTPROF_TEST_SKIP_EVAL_NORM}; | 
| 516 |  |  |  |  |  |  |  | 
| 517 |  |  |  |  |  |  | # normalize flags to avoid failures due to savesrc and perl version | 
| 518 | 963 |  |  |  |  | 3440 | $self->[NYTP_FIDi_FLAGS] &= ~(NYTP_FIDf_HAS_SRC|NYTP_FIDf_SAVE_SRC); | 
| 519 |  |  |  |  |  |  |  | 
| 520 |  |  |  |  |  |  | # '1' => { 'main::foo' => [ 1, '1.38e-05', '1.24e-05', ..., { 'main::RUNTIME' => undef } ] } | 
| 521 | 963 |  |  |  |  | 2053 | for my $subscalled (values %{ $self->sub_call_lines }) { | 
|  | 963 |  |  |  |  | 5396 |  | 
| 522 |  |  |  |  |  |  |  | 
| 523 | 2283 |  |  |  |  | 7379 | for my $subname (keys %$subscalled) { | 
| 524 | 2395 |  |  |  |  | 4941 | my $sc = $subscalled->{$subname}; | 
| 525 | 2395 |  |  |  |  | 6344 | $sc->[NYTP_SCi_INCL_RTIME] = | 
| 526 |  |  |  |  |  |  | $sc->[NYTP_SCi_EXCL_RTIME] = | 
| 527 |  |  |  |  |  |  | $sc->[NYTP_SCi_RECI_RTIME] = 0; | 
| 528 |  |  |  |  |  |  |  | 
| 529 | 2395 | 100 |  |  |  | 6366 | if (not $ENV{NYTPROF_TEST_SKIP_EVAL_NORM}) { | 
| 530 |  |  |  |  |  |  | # normalize eval sequence numbers in anon sub names to 0 | 
| 531 | 2235 |  |  |  |  | 5917 | (my $newname = $subname) =~ s/ \( ((?:re_)?) eval \s \d+ \) /(${1}eval 0)/xg; | 
| 532 | 2235 | 100 |  |  |  | 8432 | if ($newname ne $subname) { | 
| 533 |  |  |  |  |  |  | warn "Normalizing $subname to $newname overwrote other called-by data\n" | 
| 534 | 48 | 50 |  |  |  | 166 | if $subscalled->{$newname}; | 
| 535 | 48 |  |  |  |  | 328 | $subscalled->{$newname} = delete $subscalled->{$subname}; | 
| 536 |  |  |  |  |  |  | } | 
| 537 |  |  |  |  |  |  | } | 
| 538 |  |  |  |  |  |  | } | 
| 539 |  |  |  |  |  |  |  | 
| 540 |  |  |  |  |  |  | } | 
| 541 |  |  |  |  |  |  | } | 
| 542 |  |  |  |  |  |  |  | 
| 543 |  |  |  |  |  |  |  | 
| 544 |  |  |  |  |  |  | sub summary { | 
| 545 | 1 |  |  | 1 | 0 | 5 | my ($fi) = @_; | 
| 546 | 1 |  |  |  |  | 5 | return sprintf "fid%d: %s", | 
| 547 |  |  |  |  |  |  | $fi->fid, $fi->filename_without_inc; | 
| 548 |  |  |  |  |  |  | } | 
| 549 |  |  |  |  |  |  |  | 
| 550 |  |  |  |  |  |  | sub dump { | 
| 551 | 962 |  |  | 962 | 0 | 4594 | my ($self, $separator, $fh, $path, $prefix, $opts) = @_; | 
| 552 |  |  |  |  |  |  |  | 
| 553 | 962 |  |  |  |  | 2496 | my @values = @{$self}[ | 
|  | 962 |  |  |  |  | 4832 |  | 
| 554 |  |  |  |  |  |  | NYTP_FIDi_FILENAME, NYTP_FIDi_EVAL_FID, NYTP_FIDi_EVAL_LINE, NYTP_FIDi_FID, | 
| 555 |  |  |  |  |  |  | NYTP_FIDi_FLAGS, NYTP_FIDi_FILESIZE, NYTP_FIDi_FILEMTIME | 
| 556 |  |  |  |  |  |  | ]; | 
| 557 | 962 |  |  |  |  | 5824 | $values[0] = $self->filename_without_inc; | 
| 558 |  |  |  |  |  |  | # also remove possible remaining perl version seen in some cpantesters | 
| 559 |  |  |  |  |  |  | # http://www.cpantesters.org/cpan/report/bf913910-bfdd-11df-a657-c9f38a00995b | 
| 560 | 962 |  |  |  |  | 5282 | $values[0] =~ s!^$Config{version}/!!o; | 
| 561 |  |  |  |  |  |  |  | 
| 562 | 962 | 50 |  |  |  | 3554 | printf $fh "%s[ %s ]\n", $prefix, join(" ", map { defined($_) ? $_ : 'undef' } @values); | 
|  | 6734 |  |  |  |  | 20937 |  | 
| 563 |  |  |  |  |  |  |  | 
| 564 | 962 | 100 |  |  |  | 5294 | if (not $opts->{skip_internal_details}) { | 
| 565 |  |  |  |  |  |  |  | 
| 566 | 946 |  |  |  |  | 4700 | for my $si ($self->subs_defined_sorted) { | 
| 567 | 2252 |  |  |  |  | 8031 | my ($fl, $ll) = ($si->first_line, $si->last_line); | 
| 568 | 2252 |  | 100 |  |  | 9177 | defined $_ or $_ = 'undef' for ($fl, $ll); | 
| 569 | 2252 |  |  |  |  | 6394 | printf $fh "%s%s%s%s%s%s-%s\n", | 
| 570 |  |  |  |  |  |  | $prefix, 'sub', $separator, | 
| 571 |  |  |  |  |  |  | $si->subname(' and '),  $separator, | 
| 572 |  |  |  |  |  |  | $fl, $ll; | 
| 573 |  |  |  |  |  |  | } | 
| 574 |  |  |  |  |  |  |  | 
| 575 |  |  |  |  |  |  | # { line => { subname => [...] }, ... } | 
| 576 | 946 |  |  |  |  | 3462 | my $sub_call_lines = $self->sub_call_lines; | 
| 577 | 946 |  |  |  |  | 3727 | for my $line (sort { $a <=> $b } keys %$sub_call_lines) { | 
|  | 3352 |  |  |  |  | 6713 |  | 
| 578 | 2146 |  |  |  |  | 4779 | my $subs_called = $sub_call_lines->{$line}; | 
| 579 |  |  |  |  |  |  |  | 
| 580 | 2146 |  |  |  |  | 5627 | for my $subname (sort keys %$subs_called) { | 
| 581 | 2210 |  |  |  |  | 3611 | my @sc = @{$subs_called->{$subname}}; | 
|  | 2210 |  |  |  |  | 6287 |  | 
| 582 | 2210 |  |  |  |  | 4153 | $sc[NYTP_SCi_CALLING_SUB] = join "|", sort keys %{ $sc[NYTP_SCi_CALLING_SUB] }; | 
|  | 2210 |  |  |  |  | 7330 |  | 
| 583 |  |  |  |  |  |  |  | 
| 584 |  |  |  |  |  |  | printf $fh "%s%s%s%s%s%s%s[ %s ]\n", | 
| 585 |  |  |  |  |  |  | $prefix, 'call', $separator, | 
| 586 |  |  |  |  |  |  | $line,  $separator, $subname, $separator, | 
| 587 | 2210 | 50 |  |  |  | 6041 | join(" ", map { defined($_) ? $_ : 'undef' } @sc) | 
|  | 17680 |  |  |  |  | 47308 |  | 
| 588 |  |  |  |  |  |  | } | 
| 589 |  |  |  |  |  |  | } | 
| 590 |  |  |  |  |  |  |  | 
| 591 |  |  |  |  |  |  | # string evals, group by the line the eval is on | 
| 592 | 946 |  |  |  |  | 2367 | my %eval_lines; | 
| 593 | 946 |  |  |  |  | 2890 | for my $eval_fi ($self->has_evals(0)) { | 
| 594 | 416 |  |  |  |  | 832 | push @{ $eval_lines{ $eval_fi->eval_line } }, $eval_fi; | 
|  | 416 |  |  |  |  | 1033 |  | 
| 595 |  |  |  |  |  |  | } | 
| 596 | 946 |  |  |  |  | 7510 | for my $line (sort { $a <=> $b } keys %eval_lines) { | 
|  | 100 |  |  |  |  | 347 |  | 
| 597 | 368 |  |  |  |  | 933 | my $eval_fis = $eval_lines{$line}; | 
| 598 |  |  |  |  |  |  |  | 
| 599 | 368 |  |  |  |  | 847 | my @has_evals = map { $_->has_evals(1) } @$eval_fis; | 
|  | 416 |  |  |  |  | 968 |  | 
| 600 | 368 | 100 |  |  |  | 913 | my @merged_fids = map { @{ $_->meta->{merged_fids}||[]} } @$eval_fis; | 
|  | 416 |  |  |  |  | 663 |  | 
|  | 416 |  |  |  |  | 1263 |  | 
| 601 |  |  |  |  |  |  |  | 
| 602 | 368 |  |  |  |  | 1474 | printf $fh "%s%s%s%d%s[ count %d nested %d merged %d ]\n", | 
| 603 |  |  |  |  |  |  | $prefix, 'eval', $separator, | 
| 604 |  |  |  |  |  |  | $eval_fis->[0]->eval_line, $separator, | 
| 605 |  |  |  |  |  |  | scalar @$eval_fis, # count of evals executed on this line | 
| 606 |  |  |  |  |  |  | scalar @has_evals, # count of nested evals they executed | 
| 607 |  |  |  |  |  |  | scalar @merged_fids, # count of evals merged (collapsed) away | 
| 608 |  |  |  |  |  |  | } | 
| 609 |  |  |  |  |  |  |  | 
| 610 |  |  |  |  |  |  | } | 
| 611 |  |  |  |  |  |  |  | 
| 612 |  |  |  |  |  |  | } | 
| 613 |  |  |  |  |  |  |  | 
| 614 |  |  |  |  |  |  | # vim: ts=8:sw=4:et | 
| 615 |  |  |  |  |  |  | 1; |