line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
# vim: ts=8 sw=4 expandtab: |
2
|
|
|
|
|
|
|
########################################################## |
3
|
|
|
|
|
|
|
# This script is part of the Devel::NYTProf distribution |
4
|
|
|
|
|
|
|
# |
5
|
|
|
|
|
|
|
# Copyright, contact and other information can be found |
6
|
|
|
|
|
|
|
# at the bottom of this file, or by going to: |
7
|
|
|
|
|
|
|
# http://metacpan.org/release/Devel-NYTProf/ |
8
|
|
|
|
|
|
|
# |
9
|
|
|
|
|
|
|
########################################################### |
10
|
|
|
|
|
|
|
package Devel::NYTProf::Util; |
11
|
|
|
|
|
|
|
|
12
|
|
|
|
|
|
|
=head1 NAME |
13
|
|
|
|
|
|
|
|
14
|
|
|
|
|
|
|
Devel::NYTProf::Util - general utility functions for L |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
=head1 SYNOPSIS |
17
|
|
|
|
|
|
|
|
18
|
|
|
|
|
|
|
use Devel::NYTProf::Util qw(strip_prefix_from_paths); |
19
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
=head1 DESCRIPTION |
21
|
|
|
|
|
|
|
|
22
|
|
|
|
|
|
|
Contains general utility functions for L |
23
|
|
|
|
|
|
|
|
24
|
|
|
|
|
|
|
B The documentation for this module is currently incomplete and out of date. |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
=head1 FUNCTIONS |
27
|
|
|
|
|
|
|
|
28
|
|
|
|
|
|
|
=encoding ISO8859-1 |
29
|
|
|
|
|
|
|
|
30
|
|
|
|
|
|
|
=cut |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
|
33
|
49
|
|
|
49
|
|
65759
|
use warnings; |
|
49
|
|
|
|
|
115
|
|
|
49
|
|
|
|
|
1665
|
|
34
|
49
|
|
|
49
|
|
281
|
use strict; |
|
49
|
|
|
|
|
94
|
|
|
49
|
|
|
|
|
1078
|
|
35
|
|
|
|
|
|
|
|
36
|
49
|
|
|
49
|
|
243
|
use base qw'Exporter'; |
|
49
|
|
|
|
|
106
|
|
|
49
|
|
|
|
|
4558
|
|
37
|
|
|
|
|
|
|
|
38
|
49
|
|
|
49
|
|
313
|
use Carp; |
|
49
|
|
|
|
|
111
|
|
|
49
|
|
|
|
|
2754
|
|
39
|
49
|
|
|
49
|
|
289
|
use Cwd qw(getcwd); |
|
49
|
|
|
|
|
123
|
|
|
49
|
|
|
|
|
2378
|
|
40
|
49
|
|
|
49
|
|
288
|
use List::Util qw(sum); |
|
49
|
|
|
|
|
170
|
|
|
49
|
|
|
|
|
2481
|
|
41
|
49
|
|
|
49
|
|
732
|
use Devel::NYTProf::Core; |
|
49
|
|
|
|
|
139
|
|
|
49
|
|
|
|
|
78848
|
|
42
|
|
|
|
|
|
|
|
43
|
|
|
|
|
|
|
our $VERSION = '6.13'; |
44
|
|
|
|
|
|
|
|
45
|
|
|
|
|
|
|
our @EXPORT_OK = qw( |
46
|
|
|
|
|
|
|
fmt_float |
47
|
|
|
|
|
|
|
fmt_time |
48
|
|
|
|
|
|
|
fmt_incl_excl_time |
49
|
|
|
|
|
|
|
make_path_strip_editor |
50
|
|
|
|
|
|
|
strip_prefix_from_paths |
51
|
|
|
|
|
|
|
calculate_median_absolute_deviation |
52
|
|
|
|
|
|
|
get_alternation_regex |
53
|
|
|
|
|
|
|
get_abs_paths_alternation_regex |
54
|
|
|
|
|
|
|
html_safe_filename |
55
|
|
|
|
|
|
|
trace_level |
56
|
|
|
|
|
|
|
_dumper |
57
|
|
|
|
|
|
|
); |
58
|
|
|
|
|
|
|
|
59
|
|
|
|
|
|
|
|
60
|
|
|
|
|
|
|
sub get_alternation_regex { |
61
|
36
|
|
|
36
|
0
|
2141
|
my ($strings, $suffix_regex) = @_; |
62
|
36
|
100
|
|
|
|
144
|
$suffix_regex = '' unless defined $suffix_regex; |
63
|
|
|
|
|
|
|
|
64
|
|
|
|
|
|
|
# sort longest string first |
65
|
36
|
|
|
|
|
593
|
my @strings = sort { length $b <=> length $a } @$strings; |
|
1181
|
|
|
|
|
2146
|
|
66
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
# build string regex for each string |
68
|
36
|
|
|
|
|
207
|
my $regex = join "|", map { quotemeta($_) . $suffix_regex } @strings; |
|
466
|
|
|
|
|
1400
|
|
69
|
|
|
|
|
|
|
|
70
|
36
|
|
|
|
|
10277
|
return qr/(?:$regex)/; |
71
|
|
|
|
|
|
|
} |
72
|
|
|
|
|
|
|
|
73
|
|
|
|
|
|
|
|
74
|
|
|
|
|
|
|
sub get_abs_paths_alternation_regex { |
75
|
36
|
|
|
36
|
0
|
1966
|
my ($inc, $cwd) = @_; |
76
|
36
|
100
|
|
|
|
536
|
my @inc = @$inc or croak "No paths"; |
77
|
|
|
|
|
|
|
|
78
|
|
|
|
|
|
|
# rewrite relative directories to be absolute |
79
|
|
|
|
|
|
|
# the logic here should match that in get_file_id() |
80
|
35
|
50
|
|
|
|
388
|
my $abs_path_regex = ($^O eq "MSWin32") ? qr,^\w:/, : qr,^/,; |
81
|
35
|
|
|
|
|
139
|
for (@inc) { |
82
|
452
|
100
|
|
|
|
2092
|
next if $_ =~ $abs_path_regex; # already absolute |
83
|
128
|
|
|
|
|
1325
|
$_ =~ s/^\.\///; # remove a leading './' |
84
|
128
|
|
66
|
|
|
1529
|
$cwd ||= getcwd(); |
85
|
128
|
100
|
|
|
|
641
|
$_ = ($_ eq '.') ? $cwd : "$cwd/$_"; |
86
|
|
|
|
|
|
|
} |
87
|
|
|
|
|
|
|
|
88
|
35
|
|
|
|
|
368
|
return get_alternation_regex(\@inc, '/?'); |
89
|
|
|
|
|
|
|
} |
90
|
|
|
|
|
|
|
|
91
|
|
|
|
|
|
|
|
92
|
|
|
|
|
|
|
sub make_path_strip_editor { |
93
|
991
|
|
|
991
|
0
|
3750
|
my ($inc_ref, $anchor, $replacement) = @_; |
94
|
991
|
100
|
|
|
|
2831
|
$anchor = '^' if not defined $anchor; |
95
|
991
|
50
|
|
|
|
4549
|
$replacement = '' if not defined $replacement; |
96
|
|
|
|
|
|
|
|
97
|
991
|
|
|
|
|
6341
|
my @inc = @$inc_ref; |
98
|
|
|
|
|
|
|
|
99
|
991
|
|
|
|
|
2069
|
our %make_path_strip_editor_cache; |
100
|
991
|
|
|
|
|
6951
|
my $key = join "\t", $anchor, $replacement, @inc; |
101
|
|
|
|
|
|
|
|
102
|
991
|
|
66
|
|
|
9741
|
return $make_path_strip_editor_cache{$key} ||= do { |
103
|
|
|
|
|
|
|
|
104
|
33
|
|
|
|
|
297
|
my $inc_regex = get_abs_paths_alternation_regex(\@inc); |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# anchor at start, capture anchor |
107
|
33
|
|
|
|
|
8469
|
$inc_regex = qr{($anchor)$inc_regex}; |
108
|
|
|
|
|
|
|
|
109
|
33
|
|
|
991
|
|
912
|
sub { $_[0] =~ s{$inc_regex}{$1$replacement} }; |
|
991
|
|
|
|
|
20922
|
|
110
|
|
|
|
|
|
|
}; |
111
|
|
|
|
|
|
|
} |
112
|
|
|
|
|
|
|
|
113
|
|
|
|
|
|
|
|
114
|
|
|
|
|
|
|
# edit @$paths in-place to remove specified absolute path prefixes |
115
|
|
|
|
|
|
|
sub strip_prefix_from_paths { |
116
|
995
|
|
|
995
|
0
|
11278
|
my ($inc_ref, $paths, $anchor, $replacement) = @_; |
117
|
|
|
|
|
|
|
|
118
|
995
|
100
|
|
|
|
4785
|
croak "strip_prefix_from_paths() first argument must be array ref" |
119
|
|
|
|
|
|
|
unless ref($inc_ref) eq 'ARRAY'; |
120
|
994
|
100
|
|
|
|
2004
|
return unless @{$inc_ref}; |
|
994
|
|
|
|
|
3172
|
|
121
|
|
|
|
|
|
|
|
122
|
993
|
100
|
|
|
|
3196
|
return if not defined $paths; |
123
|
992
|
100
|
|
|
|
3716
|
croak "strip_prefix_from_paths() second argument must be array ref" |
124
|
|
|
|
|
|
|
unless ref($paths) eq 'ARRAY'; |
125
|
|
|
|
|
|
|
|
126
|
991
|
50
|
|
|
|
6382
|
my $editor = make_path_strip_editor($inc_ref, $anchor, $replacement) |
127
|
|
|
|
|
|
|
or return; |
128
|
|
|
|
|
|
|
|
129
|
|
|
|
|
|
|
# strip off prefix using regex, skip any empty/undef paths |
130
|
991
|
50
|
|
|
|
4319
|
if (UNIVERSAL::isa($paths, 'ARRAY')) { |
131
|
991
|
|
|
|
|
5250
|
for my $path (@$paths) { |
132
|
991
|
50
|
|
|
|
5405
|
if (ref $path) { # recurse to process deeper data |
|
|
50
|
|
|
|
|
|
133
|
0
|
|
|
|
|
0
|
strip_prefix_from_paths($inc_ref, $path, $anchor, $replacement); |
134
|
|
|
|
|
|
|
} |
135
|
|
|
|
|
|
|
elsif ($path) { |
136
|
991
|
|
|
|
|
3482
|
$editor->($path); |
137
|
|
|
|
|
|
|
} |
138
|
|
|
|
|
|
|
} |
139
|
|
|
|
|
|
|
} |
140
|
|
|
|
|
|
|
# There are no instances in test suite where $paths is a hash ref |
141
|
|
|
|
|
|
|
# elsif (UNIVERSAL::isa($paths, 'HASH')) { |
142
|
|
|
|
|
|
|
# for my $orig (keys %$paths) { |
143
|
|
|
|
|
|
|
# $editor->(my $new = $orig) |
144
|
|
|
|
|
|
|
# or next; |
145
|
|
|
|
|
|
|
# my $value = delete $paths->{$orig}; |
146
|
|
|
|
|
|
|
# warn "Stripping prefix from $orig overwrites existing $new" |
147
|
|
|
|
|
|
|
# if defined $paths->{$new}; |
148
|
|
|
|
|
|
|
# $paths->{$new} = $value; |
149
|
|
|
|
|
|
|
# } |
150
|
|
|
|
|
|
|
# } |
151
|
|
|
|
|
|
|
else { |
152
|
0
|
|
|
|
|
0
|
croak "Can't strip_prefix_from_paths of $paths"; |
153
|
|
|
|
|
|
|
} |
154
|
|
|
|
|
|
|
|
155
|
991
|
|
|
|
|
3758
|
return; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
|
|
|
|
|
|
|
159
|
|
|
|
|
|
|
# eg normalize the width/precision so that the tables look good. |
160
|
|
|
|
|
|
|
sub fmt_float { |
161
|
17
|
|
|
17
|
0
|
11528
|
my ($val, $precision) = @_; |
162
|
17
|
|
100
|
|
|
72
|
$precision ||= 5; |
163
|
17
|
100
|
66
|
|
|
119
|
if ($val < 10 ** -($precision - 1) and $val > 0) { |
|
|
100
|
|
|
|
|
|
164
|
|
|
|
|
|
|
# Give the same width as a larger value formatted with the %f below. |
165
|
|
|
|
|
|
|
# This gives us 2 digits of precision for $precision == 5 |
166
|
1
|
|
|
|
|
9
|
$val = sprintf("%." . ($precision - 4) . "e", $val); |
167
|
|
|
|
|
|
|
# But our exponents will always be e-05 to e-09, never e-10 or smaller |
168
|
|
|
|
|
|
|
# so remove the leading zero to make these small numbers stand out less |
169
|
|
|
|
|
|
|
# on the table. |
170
|
1
|
|
|
|
|
22
|
$val =~ s/e-0+/e-/; |
171
|
|
|
|
|
|
|
} |
172
|
|
|
|
|
|
|
elsif ($val != int($val)) { |
173
|
14
|
|
|
|
|
145
|
$val = sprintf("%.${precision}f", $val); |
174
|
|
|
|
|
|
|
} |
175
|
17
|
|
|
|
|
57
|
return $val; |
176
|
|
|
|
|
|
|
} |
177
|
|
|
|
|
|
|
|
178
|
|
|
|
|
|
|
|
179
|
|
|
|
|
|
|
# XXX undocumented hack that may become to an option one day |
180
|
|
|
|
|
|
|
# Useful for making the time data more easily parseable |
181
|
|
|
|
|
|
|
my $fmt_time_opt = $ENV{NYTPROF_FMT_TIME}; # e.g., '%f' for 'raw' times |
182
|
|
|
|
|
|
|
|
183
|
|
|
|
|
|
|
sub fmt_time { |
184
|
28
|
|
|
28
|
0
|
134
|
my ($sec, $width) = @_; |
185
|
28
|
50
|
|
|
|
65
|
$width = '' unless defined $width; |
186
|
28
|
50
|
|
|
|
54
|
return undef if not defined $sec; |
187
|
28
|
50
|
|
|
|
65
|
return '-'.fmt_time(-$sec, $width) if $sec < 0; # negative value, can happen |
188
|
28
|
50
|
|
|
|
62
|
return sprintf $fmt_time_opt, $sec if $fmt_time_opt; |
189
|
28
|
100
|
|
|
|
60
|
return sprintf "%$width.0fs", 0 unless $sec; |
190
|
27
|
100
|
|
|
|
76
|
return sprintf "%$width.0fns", $sec * 1e9 if $sec < 1e-6; |
191
|
23
|
100
|
|
|
|
71
|
return sprintf "%$width.0fµs", $sec * 1e6 if $sec < 1e-3; |
192
|
16
|
100
|
|
|
|
84
|
return sprintf "%$width.*fms", 3 - length(int($sec * 1e3)), $sec * 1e3 if $sec < 1; |
193
|
10
|
100
|
|
|
|
73
|
return sprintf "%$width.*fs", 3 - length(int($sec )), $sec if $sec < 100; |
194
|
2
|
|
|
|
|
14
|
return sprintf "%$width.0fs", $sec; |
195
|
|
|
|
|
|
|
} |
196
|
|
|
|
|
|
|
|
197
|
|
|
|
|
|
|
|
198
|
|
|
|
|
|
|
sub fmt_incl_excl_time { |
199
|
5
|
|
|
5
|
0
|
13
|
my ($incl, $excl) = @_; |
200
|
5
|
|
|
|
|
10
|
my $diff = $incl - $excl; |
201
|
5
|
100
|
|
|
|
16
|
return fmt_time($incl) unless $diff; |
202
|
4
|
|
|
|
|
11
|
$_ = fmt_time($_) for $incl, $excl, $diff; |
203
|
4
|
50
|
|
|
|
27
|
if ($incl =~ /(\D+)$/) { |
204
|
|
|
|
|
|
|
# no need to repeat the unit if it's the same for all time stamps |
205
|
4
|
|
|
|
|
12
|
my $unit = $1; |
206
|
4
|
|
|
|
|
6
|
my $offset = -length($unit); |
207
|
4
|
|
|
|
|
8
|
for ($excl, $diff) { |
208
|
8
|
50
|
|
|
|
28
|
if (/(\D+)$/) { |
209
|
8
|
100
|
|
|
|
25
|
substr($_, $offset) = "" if $1 eq $unit |
210
|
|
|
|
|
|
|
} |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
} |
213
|
4
|
|
|
|
|
26
|
return sprintf "%s (%s+%s)", $incl, $excl, $diff; |
214
|
|
|
|
|
|
|
} |
215
|
|
|
|
|
|
|
|
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
## Given a ref to an array of numeric values |
218
|
|
|
|
|
|
|
## returns median distance from the median value, and the median value. |
219
|
|
|
|
|
|
|
## See http://en.wikipedia.org/wiki/Median_absolute_deviation |
220
|
|
|
|
|
|
|
sub calculate_median_absolute_deviation { |
221
|
20
|
|
|
20
|
0
|
4536
|
my $values_ref = shift; |
222
|
20
|
|
|
|
|
35
|
my ($ignore_zeros) = @_; |
223
|
20
|
100
|
|
|
|
248
|
croak "No array ref given" unless ref $values_ref eq 'ARRAY'; |
224
|
|
|
|
|
|
|
|
225
|
19
|
100
|
|
|
|
178
|
my @values = ($ignore_zeros) ? grep {$_} @$values_ref : @$values_ref; |
|
8
|
|
|
|
|
13
|
|
226
|
19
|
|
|
|
|
54
|
my $median_value = [sort { $a <=> $b } @values]->[@values / 2]; |
|
252
|
|
|
|
|
394
|
|
227
|
|
|
|
|
|
|
|
228
|
19
|
100
|
|
|
|
53
|
return [0, 0] if not defined $median_value; # no data |
229
|
|
|
|
|
|
|
|
230
|
18
|
|
|
|
|
40
|
my @devi = map { abs($_ - $median_value) } @values; |
|
135
|
|
|
|
|
208
|
|
231
|
18
|
|
|
|
|
36
|
my $median_devi = [sort { $a <=> $b } @devi]->[@devi / 2]; |
|
264
|
|
|
|
|
468
|
|
232
|
|
|
|
|
|
|
|
233
|
18
|
|
|
|
|
102
|
return [$median_devi, $median_value]; |
234
|
|
|
|
|
|
|
} |
235
|
|
|
|
|
|
|
|
236
|
|
|
|
|
|
|
|
237
|
|
|
|
|
|
|
sub html_safe_filename { |
238
|
336
|
|
|
336
|
0
|
807154
|
my ($fname) = @_; |
239
|
|
|
|
|
|
|
# replace / and \ with html safe '-', we also do a bunch of other |
240
|
|
|
|
|
|
|
# chars, especially ':' for Windows, to make the namer simpler and safer |
241
|
|
|
|
|
|
|
# also remove dots to keep VMS happy |
242
|
336
|
|
|
|
|
3656
|
$fname =~ s{ [-/\\:\*\?"'<>|.]+ }{-}xg; |
243
|
|
|
|
|
|
|
# remove any leading or trailing '-' chars |
244
|
336
|
|
|
|
|
2772
|
$fname =~ s{^-}{}; |
245
|
336
|
|
|
|
|
1881
|
$fname =~ s{-$}{}; |
246
|
336
|
50
|
|
|
|
2458
|
if($^O eq 'VMS'){ |
247
|
|
|
|
|
|
|
# ODS-2 is limited to 39.39 chars (39 filename, 39 extension) |
248
|
|
|
|
|
|
|
# Reader.pm appends -LEVEL onto html safe filename so must |
249
|
|
|
|
|
|
|
# subtract 1 + max length of (sub block line), so 6. |
250
|
0
|
|
|
|
|
0
|
$fname = substr($fname,-33); |
251
|
|
|
|
|
|
|
} |
252
|
336
|
|
|
|
|
1657
|
return $fname; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
sub _dumper { |
256
|
2
|
|
|
2
|
|
1831
|
require Data::Dumper; |
257
|
2
|
|
|
|
|
7776
|
local $Data::Dumper::Sortkeys = 1; |
258
|
2
|
|
|
|
|
6
|
local $Data::Dumper::Indent = 1; |
259
|
2
|
|
|
|
|
13
|
return Data::Dumper::Dumper(@_); |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
|
|
|
|
|
|
1; |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
__END__ |