line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
1
|
|
|
|
|
|
|
package Devel::TraceUse; |
2
|
|
|
|
|
|
|
$Devel::TraceUse::VERSION = '2.095'; |
3
|
|
|
|
|
|
|
# detect being loaded via -d:TraceUse and disable the debugger features we |
4
|
|
|
|
|
|
|
# don't need. better names for evals (0x100) and anon subs (0x200). |
5
|
|
|
|
|
|
|
BEGIN { |
6
|
3
|
50
|
33
|
3
|
|
149753
|
if (!defined &DB::DB && $^P & 0x02) { |
7
|
0
|
|
|
|
|
0
|
$^P = 0x100 | 0x200; |
8
|
|
|
|
|
|
|
} |
9
|
|
|
|
|
|
|
} |
10
|
|
|
|
|
|
|
|
11
|
|
|
|
|
|
|
BEGIN |
12
|
|
|
|
|
|
|
{ |
13
|
3
|
50
|
|
3
|
|
8
|
unshift @INC, \&trace_use unless grep { "$_" eq \&trace_use . '' } @INC; |
|
32
|
|
|
|
|
5362
|
|
14
|
|
|
|
|
|
|
} |
15
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
# initialize the tree of require calls |
17
|
|
|
|
|
|
|
my $root = (caller)[1]; |
18
|
|
|
|
|
|
|
my %used; # track loaded modules by "filename" (parameter to require) |
19
|
|
|
|
|
|
|
my %loaded; # track "filename"s loaded by "filepath" (value from %INC) |
20
|
|
|
|
|
|
|
my %reported; # track reported "filename" |
21
|
|
|
|
|
|
|
my %loader; # track potential proxy modules |
22
|
|
|
|
|
|
|
my $rank = 0; # record the loading order of modules |
23
|
|
|
|
|
|
|
my $quiet = 1; # no output until decided otherwise |
24
|
|
|
|
|
|
|
my $output_fh; # optional write filehandle where results will be output |
25
|
|
|
|
|
|
|
|
26
|
|
|
|
|
|
|
# Hide core modules (for the specified version)? |
27
|
|
|
|
|
|
|
my $hide_core = 0; |
28
|
|
|
|
|
|
|
|
29
|
|
|
|
|
|
|
sub import { |
30
|
2
|
|
|
2
|
|
1298
|
my $class = shift; |
31
|
|
|
|
|
|
|
|
32
|
|
|
|
|
|
|
# ensure "use Devel::TraceUse ();" will produce no output |
33
|
2
|
|
|
|
|
2
|
$quiet = 0; |
34
|
|
|
|
|
|
|
|
35
|
|
|
|
|
|
|
# process options |
36
|
2
|
|
|
|
|
18
|
for(@_) { |
37
|
0
|
0
|
|
|
|
0
|
if(/^hidecore(?::(.*))?/) { |
|
|
0
|
|
|
|
|
|
38
|
0
|
0
|
|
|
|
0
|
$hide_core = numify( $1 ? $1 : $] ); |
39
|
|
|
|
|
|
|
} elsif (/^output:(.*)$/) { |
40
|
0
|
0
|
|
|
|
0
|
open $output_fh, '>', $1 or die "can't open $1: $!"; |
41
|
|
|
|
|
|
|
} else { |
42
|
0
|
|
|
|
|
0
|
die "Unknown argument to $class: $_\n"; |
43
|
|
|
|
|
|
|
} |
44
|
|
|
|
|
|
|
} |
45
|
|
|
|
|
|
|
} |
46
|
|
|
|
|
|
|
|
47
|
|
|
|
|
|
|
my @caller_info = qw( package filepath line ); |
48
|
|
|
|
|
|
|
|
49
|
|
|
|
|
|
|
# Keys used in the data structure: |
50
|
|
|
|
|
|
|
# - filename: parameter given to use/require |
51
|
|
|
|
|
|
|
# - module: module, computed from filename |
52
|
|
|
|
|
|
|
# - rank: rank of loading |
53
|
|
|
|
|
|
|
# - eval: was this use/require done in an eval? |
54
|
|
|
|
|
|
|
# - loaded: list of files loaded from this one |
55
|
|
|
|
|
|
|
# - filepath: file that was actually loaded from disk (obtained from %INC) |
56
|
|
|
|
|
|
|
# - caller: information on the caller (same keys + everything from caller()) |
57
|
|
|
|
|
|
|
|
58
|
|
|
|
|
|
|
sub trace_use |
59
|
|
|
|
|
|
|
{ |
60
|
17
|
|
|
17
|
0
|
142591
|
my ( $code, $filename ) = @_; |
61
|
|
|
|
|
|
|
|
62
|
|
|
|
|
|
|
# ensure our hook remains first in @INC |
63
|
17
|
50
|
|
|
|
65
|
@INC = ( $code, grep { $_ ne $code } @INC ) |
|
0
|
|
|
|
|
0
|
|
64
|
|
|
|
|
|
|
if $INC[0] ne $code; |
65
|
|
|
|
|
|
|
|
66
|
|
|
|
|
|
|
# $filename may be an actual filename, e.g. with do() |
67
|
|
|
|
|
|
|
# try to compute a module name from it |
68
|
17
|
|
|
|
|
21
|
my $module = $filename; |
69
|
17
|
50
|
|
|
|
151
|
$module =~ s{/}{::}g |
70
|
|
|
|
|
|
|
if $module =~ s/\.pm$//; |
71
|
|
|
|
|
|
|
|
72
|
|
|
|
|
|
|
# info about the module being loaded |
73
|
17
|
|
|
|
|
20
|
push @{ $used{$filename} }, my $info = { |
|
17
|
|
|
|
|
108
|
|
74
|
|
|
|
|
|
|
filename => $filename, |
75
|
|
|
|
|
|
|
module => $module, |
76
|
|
|
|
|
|
|
rank => ++$rank, |
77
|
|
|
|
|
|
|
eval => '', |
78
|
|
|
|
|
|
|
}; |
79
|
|
|
|
|
|
|
|
80
|
|
|
|
|
|
|
# info about the loading module |
81
|
17
|
|
|
|
|
35
|
my $caller = $info->{caller} = {}; |
82
|
17
|
|
|
|
|
40
|
@{$caller}{@caller_info} = caller; |
|
17
|
|
|
|
|
54
|
|
83
|
|
|
|
|
|
|
|
84
|
|
|
|
|
|
|
# try to compute a "filename" (as received by require) |
85
|
17
|
|
|
|
|
28
|
$caller->{filename} = $caller->{filepath}; |
86
|
|
|
|
|
|
|
|
87
|
|
|
|
|
|
|
# some values seen in the wild: |
88
|
|
|
|
|
|
|
# - "(eval $num)[$path:$line]" (debugger) |
89
|
|
|
|
|
|
|
# - "$filename (autosplit into $path)" (AutoLoader) |
90
|
17
|
100
|
|
|
|
57
|
if ( $caller->{filename} =~ /^(\(eval \d+\))(?:\[(.*):(\d+)\])?$/ ) { |
91
|
4
|
|
|
|
|
10
|
$info->{eval} = $1; |
92
|
4
|
|
|
|
|
11
|
$caller->{filename} = $caller->{filepath} = $2; |
93
|
4
|
|
|
|
|
8
|
$caller->{line} = $3; |
94
|
|
|
|
|
|
|
} |
95
|
|
|
|
|
|
|
|
96
|
|
|
|
|
|
|
# clean up path |
97
|
|
|
|
|
|
|
$caller->{filename} |
98
|
17
|
|
|
|
|
17
|
=~ s!^(?:@{[ join '|', map quotemeta, reverse sort @INC ]})/?!!; |
|
17
|
|
|
|
|
785
|
|
99
|
|
|
|
|
|
|
|
100
|
|
|
|
|
|
|
# try to compute the package associated with the file |
101
|
17
|
|
|
|
|
59
|
$caller->{filepackage} = $caller->{filename}; |
102
|
17
|
|
|
|
|
31
|
$caller->{filepackage} =~ s/\.(pm|al)\s.*$/.$1/; |
103
|
|
|
|
|
|
|
$caller->{filepackage} =~ s{/}{::}g |
104
|
17
|
100
|
|
|
|
113
|
if $caller->{filepackage} =~ s/\.pm$//; |
105
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
# record who tried to load us |
107
|
17
|
|
|
|
|
20
|
push @{ $loaded{ $caller->{filepath} } }, $info->{filename}; |
|
17
|
|
|
|
|
50
|
|
108
|
|
|
|
|
|
|
|
109
|
|
|
|
|
|
|
# record potential proxies |
110
|
17
|
50
|
|
|
|
42
|
if ( $caller->{filename} ) { |
111
|
17
|
|
|
|
|
15
|
my($subroutine, $level); |
112
|
17
|
|
100
|
|
|
114
|
while ( $subroutine = ( caller ++$level )[3] || '' ) { |
113
|
20
|
100
|
|
|
|
94
|
last if $subroutine =~ /::/; |
114
|
|
|
|
|
|
|
} |
115
|
17
|
|
|
|
|
19
|
$loader{ join "\0", @{$caller}{qw( filename line )}, $subroutine }++; |
|
17
|
|
|
|
|
62
|
|
116
|
|
|
|
|
|
|
} |
117
|
|
|
|
|
|
|
|
118
|
|
|
|
|
|
|
# let Perl ultimately find the required file |
119
|
17
|
|
|
|
|
11763
|
return; |
120
|
|
|
|
|
|
|
} |
121
|
|
|
|
|
|
|
|
122
|
|
|
|
|
|
|
sub show_trace_visitor |
123
|
|
|
|
|
|
|
{ |
124
|
0
|
|
|
0
|
0
|
0
|
my ( $mod, $pos, $output_cb, @args ) = @_; |
125
|
|
|
|
|
|
|
|
126
|
0
|
|
|
|
|
0
|
my $caller = $mod->{caller}; |
127
|
0
|
|
|
|
|
0
|
my $message = sprintf( '%4s.', $mod->{rank} ) . ' ' x $pos; |
128
|
0
|
|
|
|
|
0
|
$message .= "$mod->{module}"; |
129
|
0
|
|
|
|
|
0
|
my $version = ${"$mod->{module}\::VERSION"}; |
|
0
|
|
|
|
|
0
|
|
130
|
0
|
0
|
|
|
|
0
|
$message .= defined $version ? " $version," : ','; |
131
|
|
|
|
|
|
|
$message .= " $caller->{filename}" |
132
|
0
|
0
|
|
|
|
0
|
if defined $caller->{filename}; |
133
|
|
|
|
|
|
|
$message .= " line $caller->{line}" |
134
|
0
|
0
|
|
|
|
0
|
if defined $caller->{line}; |
135
|
|
|
|
|
|
|
$message .= " $mod->{eval}" |
136
|
0
|
0
|
|
|
|
0
|
if $mod->{eval}; |
137
|
|
|
|
|
|
|
$message .= " [$caller->{package}]" |
138
|
0
|
0
|
|
|
|
0
|
if $caller->{package} ne $caller->{filepackage}; |
139
|
|
|
|
|
|
|
$message .= " (FAILED)" |
140
|
0
|
0
|
|
|
|
0
|
if !exists $INC{$mod->{filename}}; |
141
|
|
|
|
|
|
|
|
142
|
0
|
|
|
|
|
0
|
$output_cb->($message, @args); |
143
|
|
|
|
|
|
|
} |
144
|
|
|
|
|
|
|
|
145
|
|
|
|
|
|
|
sub visit_trace |
146
|
|
|
|
|
|
|
{ |
147
|
1
|
|
|
1
|
0
|
2
|
my ( $visitor, $mod, $pos, @args ) = @_; |
148
|
|
|
|
|
|
|
|
149
|
1
|
|
|
|
|
1
|
my $hide = 0; |
150
|
|
|
|
|
|
|
|
151
|
1
|
50
|
|
|
|
11
|
if ( ref $mod ) { |
152
|
0
|
|
|
|
|
0
|
$mod = shift @$mod; |
153
|
|
|
|
|
|
|
|
154
|
0
|
0
|
|
|
|
0
|
if($hide_core) { |
155
|
0
|
|
|
|
|
0
|
$hide = exists $Module::CoreList::version{$hide_core}{$mod->{module}}; |
156
|
|
|
|
|
|
|
} |
157
|
|
|
|
|
|
|
|
158
|
0
|
0
|
|
|
|
0
|
$visitor->( $mod, $pos, @args ) unless $hide; |
159
|
|
|
|
|
|
|
|
160
|
0
|
|
|
|
|
0
|
$reported{$mod->{filename}}++; |
161
|
|
|
|
|
|
|
} |
162
|
|
|
|
|
|
|
else { |
163
|
1
|
|
|
|
|
3
|
$mod = { loaded => delete $loaded{$mod} }; |
164
|
|
|
|
|
|
|
} |
165
|
|
|
|
|
|
|
|
166
|
|
|
|
|
|
|
visit_trace( $visitor, $used{$_}, $hide ? $pos : $pos + 1, @args ) |
167
|
1
|
0
|
|
|
|
1
|
for map { $INC{$_} || $_ } @{ $mod->{loaded} }; |
|
0
|
0
|
|
|
|
0
|
|
|
1
|
|
|
|
|
5
|
|
168
|
|
|
|
|
|
|
} |
169
|
|
|
|
|
|
|
|
170
|
|
|
|
|
|
|
# we don't want to use version.pm on old Perls |
171
|
|
|
|
|
|
|
sub numify { |
172
|
24
|
|
|
24
|
0
|
46615
|
my ($version) = @_; |
173
|
24
|
|
|
|
|
42
|
$version =~ y/_//d; |
174
|
24
|
|
|
|
|
79
|
my @parts = split /\./, $version; |
175
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
# %Module::CoreList::version's keys are x.yyyzzz *numbers* |
177
|
24
|
|
|
|
|
200
|
return 0+ join '', shift @parts, '.', map sprintf( '%03s', $_ ), @parts; |
178
|
|
|
|
|
|
|
} |
179
|
|
|
|
|
|
|
|
180
|
|
|
|
|
|
|
sub dump_proxies |
181
|
|
|
|
|
|
|
{ |
182
|
1
|
|
|
1
|
0
|
2
|
my $output = shift; |
183
|
|
|
|
|
|
|
|
184
|
|
|
|
|
|
|
my @hot_loaders = |
185
|
0
|
|
|
|
|
0
|
sort { $loader{$b} <=> $loader{$a} } |
186
|
1
|
|
|
|
|
3
|
grep { $loader{$_} > 1 } |
|
0
|
|
|
|
|
0
|
|
187
|
|
|
|
|
|
|
keys %loader; |
188
|
|
|
|
|
|
|
|
189
|
1
|
50
|
|
|
|
4
|
return unless @hot_loaders; |
190
|
|
|
|
|
|
|
|
191
|
0
|
|
|
|
|
0
|
$output->("Possible proxies:"); |
192
|
|
|
|
|
|
|
|
193
|
0
|
|
|
|
|
0
|
for my $loader (@hot_loaders) { |
194
|
0
|
|
|
|
|
0
|
my ( $filename, $line, $subroutine ) = split /\0/, $loader; |
195
|
|
|
|
|
|
|
$output->(sprintf("%4d %s line %d%s", |
196
|
0
|
0
|
|
|
|
0
|
$loader{$loader}, |
197
|
|
|
|
|
|
|
$filename, $line, |
198
|
|
|
|
|
|
|
(defined($subroutine) ? ", sub $subroutine" : ''))); |
199
|
|
|
|
|
|
|
} |
200
|
|
|
|
|
|
|
} |
201
|
|
|
|
|
|
|
|
202
|
|
|
|
|
|
|
sub dump_result |
203
|
|
|
|
|
|
|
{ |
204
|
3
|
100
|
|
3
|
0
|
20
|
return if $quiet; |
205
|
|
|
|
|
|
|
|
206
|
|
|
|
|
|
|
# map "filename" to "filepath" for everything that was loaded |
207
|
1
|
|
|
|
|
7
|
while ( my ( $filename, $filepath ) = each %INC ) { |
208
|
74
|
50
|
|
|
|
275
|
if ( exists $used{$filename} ) { |
209
|
0
|
|
0
|
|
|
0
|
$used{$filename}[0]{loaded} = delete $loaded{$filepath} || []; |
210
|
0
|
|
|
|
|
0
|
$used{$filepath} = delete $used{$filename}; |
211
|
|
|
|
|
|
|
} |
212
|
|
|
|
|
|
|
} |
213
|
|
|
|
|
|
|
|
214
|
|
|
|
|
|
|
# let people know more accurate information is available |
215
|
1
|
50
|
|
|
|
6
|
warn "Use -d:TraceUse for more accurate information.\n" if !$^P; |
216
|
|
|
|
|
|
|
|
217
|
|
|
|
|
|
|
# load Module::CoreList if needed |
218
|
1
|
50
|
|
|
|
3
|
if ($hide_core) { |
219
|
0
|
|
|
|
|
0
|
local @INC = grep { $_ ne \&trace_use } @INC; |
|
0
|
|
|
|
|
0
|
|
220
|
0
|
|
|
|
|
0
|
local %INC = %INC; # don't report it loaded |
221
|
0
|
|
|
|
|
0
|
require Module::CoreList; |
222
|
|
|
|
|
|
|
warn sprintf "Module::CoreList %s doesn't know about Perl %s\n", |
223
|
|
|
|
|
|
|
$Module::CoreList::VERSION, $hide_core |
224
|
0
|
0
|
|
|
|
0
|
if !exists $Module::CoreList::version{$hide_core}; |
225
|
|
|
|
|
|
|
} |
226
|
|
|
|
|
|
|
|
227
|
|
|
|
|
|
|
my $output = defined $output_fh |
228
|
0
|
|
|
0
|
|
0
|
? sub { print $output_fh "$_[0]\n" } |
229
|
1
|
50
|
|
75
|
|
7
|
: sub { warn "$_[0]\n" }; |
|
75
|
|
|
|
|
495
|
|
230
|
|
|
|
|
|
|
|
231
|
|
|
|
|
|
|
# output the diagnostic |
232
|
1
|
|
|
|
|
4
|
$output->("Modules used from $root:"); |
233
|
1
|
|
|
|
|
6
|
visit_trace( \&show_trace_visitor, $root, 0, $output ); |
234
|
|
|
|
|
|
|
|
235
|
|
|
|
|
|
|
# anything left? |
236
|
1
|
50
|
|
|
|
3
|
if (%loaded) { |
237
|
0
|
|
|
|
|
0
|
visit_trace( \&show_trace_visitor, $_, 0, $output ) for sort keys %loaded; |
238
|
|
|
|
|
|
|
} |
239
|
|
|
|
|
|
|
|
240
|
|
|
|
|
|
|
# did we miss some modules? |
241
|
1
|
50
|
|
|
|
10
|
if (my @missed |
242
|
74
|
50
|
|
|
|
261
|
= sort grep { !exists $reported{$_} && $_ ne 'Devel/TraceUse.pm' } |
243
|
|
|
|
|
|
|
keys %INC |
244
|
|
|
|
|
|
|
) |
245
|
|
|
|
|
|
|
{ |
246
|
1
|
50
|
|
|
|
5
|
$output->("Modules used, but not reported:") if @missed; |
247
|
1
|
|
|
|
|
7
|
$output->(" $_") for @missed; |
248
|
|
|
|
|
|
|
} |
249
|
|
|
|
|
|
|
|
250
|
1
|
|
|
|
|
8
|
dump_proxies($output); |
251
|
|
|
|
|
|
|
|
252
|
1
|
50
|
|
|
|
17
|
close $output_fh if defined $output_fh; |
253
|
|
|
|
|
|
|
} |
254
|
|
|
|
|
|
|
|
255
|
|
|
|
|
|
|
# Install the final hook |
256
|
|
|
|
|
|
|
# If perl runs with -c we want to dump |
257
|
|
|
|
|
|
|
CHECK { |
258
|
|
|
|
|
|
|
# "perl -c" ? |
259
|
2
|
50
|
|
2
|
|
5056
|
dump_result() if $^C; |
260
|
|
|
|
|
|
|
} |
261
|
|
|
|
|
|
|
|
262
|
2
|
|
|
2
|
|
2162924
|
END { dump_result() } |
263
|
|
|
|
|
|
|
|
264
|
|
|
|
|
|
|
1; |
265
|
|
|
|
|
|
|
__END__ |