ViewVC Help
View File | Revision Log | Show Annotations | Download File
/cvs/Devel-FindRef/FindRef.pm
Revision: 1.28
Committed: Sat Apr 13 06:29:39 2013 UTC (11 years, 1 month ago) by root
Branch: MAIN
Changes since 1.27: +9 -6 lines
Log Message:
*** empty log message ***

File Contents

# Content
1 package Devel::FindRef;
2
3 use common::sense;
4
5 use XSLoader;
6 use Scalar::Util;
7
8 BEGIN {
9 our $VERSION = '1.422';
10 XSLoader::load __PACKAGE__, $VERSION;
11 }
12
13 =head1 NAME
14
15 Devel::FindRef - where is that reference to my variable hiding?
16
17 =head1 SYNOPSIS
18
19 use Devel::FindRef;
20
21 print Devel::FindRef::track \$some_variable;
22
23 =head1 DESCRIPTION
24
25 Tracking down reference problems (e.g. you expect some object to be
26 destroyed, but there are still references to it that keep it alive) can be
27 very hard. Fortunately, perl keeps track of all its values, so tracking
28 references "backwards" is usually possible.
29
30 The C<track> function can help track down some of those references back to
31 the variables containing them.
32
33 For example, for this fragment:
34
35 package Test;
36
37 use Devel::FindRef;
38 use Scalar::Util;
39
40 our $var = "hi\n";
41 my $global_my = \$var;
42 our %global_hash = (ukukey => \$var);
43 our $global_hashref = { ukukey2 => \$var };
44
45 sub testsub {
46 my $testsub_local = $global_hashref;
47 print Devel::FindRef::track \$var;
48 }
49
50
51 my $closure = sub {
52 my $closure_var = \$_[0];
53 Scalar::Util::weaken (my $weak_ref = \$var);
54 testsub;
55 };
56
57 $closure->($var);
58
59 The output is as follows (or similar to this, in case I forget to update
60 the manpage after some changes):
61
62 SCALAR(0x7cc888) [refcount 6] is
63 +- referenced by REF(0x8abcc8) [refcount 1], which is
64 | the lexical '$closure_var' in CODE(0x8abc50) [refcount 4], which is
65 | +- the closure created at tst:18.
66 | +- referenced by REF(0x7d3c58) [refcount 1], which is
67 | | the lexical '$closure' in CODE(0x7ae530) [refcount 2], which is
68 | | +- the containing scope for CODE(0x8ab430) [refcount 3], which is
69 | | | the global &Test::testsub.
70 | | +- the main body of the program.
71 | +- the lexical '&' in CODE(0x7ae530) [refcount 2], which was seen before.
72 +- referenced by REF(0x7cc7c8) [refcount 1], which is
73 | the lexical '$global_my' in CODE(0x7ae530) [refcount 2], which was seen before.
74 +- the global $Test::var.
75 +- referenced by REF(0x7cc558) [refcount 1], which is
76 | the member 'ukukey2' of HASH(0x7ae140) [refcount 2], which is
77 | +- referenced by REF(0x8abad0) [refcount 1], which is
78 | | the lexical '$testsub_local' in CODE(0x8ab430) [refcount 3], which was seen before.
79 | +- referenced by REF(0x8ab4f0) [refcount 1], which is
80 | the global $Test::global_hashref.
81 +- referenced by REF(0x7ae518) [refcount 1], which is
82 | the member 'ukukey' of HASH(0x7d3bb0) [refcount 1], which is
83 | the global %Test::global_hash.
84 +- referenced by REF(0x7ae2f0) [refcount 1], which is
85 a temporary on the stack.
86
87 It is a bit convoluted to read, but basically it says that the value
88 stored in C<$var> is referenced by:
89
90 =over 4
91
92 =item - the lexical C<$closure_var> (0x8abcc8), which is inside an instantiated
93 closure, which in turn is used quite a bit.
94
95 =item - the package-level lexical C<$global_my>.
96
97 =item - the global package variable named C<$Test::var>.
98
99 =item - the hash element C<ukukey2>, in the hash in the my variable
100 C<$testsub_local> in the sub C<Test::testsub> and also in the hash
101 C<$referenced by Test::hash2>.
102
103 =item - the hash element with key C<ukukey> in the hash stored in
104 C<%Test::hash>.
105
106 =item - some anonymous mortalised reference on the stack (which is caused
107 by calling C<track> with the expression C<\$var>, which creates the
108 reference).
109
110 =back
111
112 And all these account for six reference counts.
113
114
115 =head1 EXPORTS
116
117 None.
118
119 =head1 FUNCTIONS
120
121 =over 4
122
123 =item $string = Devel::FindRef::track $ref[, $depth]
124
125 Track the perl value pointed to by C<$ref> up to a depth of C<$depth> and
126 return a descriptive string. C<$ref> can point at any perl value, be it
127 anonymous sub, hash, array, scalar etc.
128
129 This is the function you most often use.
130
131 =cut
132
133 sub find($);
134
135 sub _f($) {
136 "$_[0] [refcount " . (_refcnt $_[0]) . "]"
137 }
138
139 sub track {
140 my ($ref, $depth) = @_;
141 @_ = ();
142
143 my $buf = "";
144 my %seen;
145
146 Scalar::Util::weaken $ref;
147
148 my $track; $track = sub {
149 my ($refref, $depth, $indent) = @_;
150
151 if ($depth) {
152 my (@about) = find $$refref;
153 if (@about) {
154 for my $about (@about) {
155 $about->[0] =~ s/([^\x20-\x7e])/sprintf "\\{%02x}", ord $1/ge;
156 $buf .= "$indent" . (@about > 1 ? "+- " : "") . $about->[0];
157 if (@$about > 1) {
158 if ($seen{ref2ptr $about->[1]}++) {
159 $buf .= " " . (_f $about->[1]) . ", which was seen before.\n";
160 } else {
161 $buf .= " " . (_f $about->[1]) . ", which is\n";
162 $track->(\$about->[1], $depth - 1, $about == $about[-1] ? "$indent " : "$indent| ");
163 }
164 } else {
165 $buf .= ".\n";
166 }
167 }
168 } else {
169 $buf .= "$indent not found anywhere I looked :(\n";
170 }
171 } else {
172 $buf .= "$indent not referenced within the search depth.\n";
173 }
174 };
175
176 $buf .= (_f $ref) . " is\n";
177
178 $track->(\$ref, $depth || $ENV{PERL_DEVEL_FINDREF_DEPTH} || 10, "");
179 $buf
180 }
181
182 =item @references = Devel::FindRef::find $ref
183
184 Return arrayrefs that contain [$message, $ref] pairs. The message
185 describes what kind of reference was found and the C<$ref> is the
186 reference itself, which can be omitted if C<find> decided to end the
187 search. The returned references are all weak references.
188
189 The C<track> function uses this to find references to the value you are
190 interested in and recurses on the returned references.
191
192 =cut
193
194 sub find($) {
195 my ($about, $excl) = &find_;
196 my %excl = map +($_ => undef), @$excl;
197 grep !($#$_ && exists $excl{ref2ptr $_->[1]}), @$about
198 }
199
200 =item $ref = Devel::FindRef::ptr2ref $integer
201
202 Sometimes you know (from debugging output) the address of a perl value you
203 are interested in (e.g. C<HASH(0x176ff70)>). This function can be used to
204 turn the address into a reference to that value. It is quite safe to call
205 on valid addresses, but extremely dangerous to call on invalid ones. I<No
206 checks whatsoever will be done>, so don't use this unless you really know
207 the value is the address of a valid perl value.
208
209 # we know that HASH(0x176ff70) exists, so turn it into a hashref:
210 my $ref_to_hash = Devel::FindRef::ptr2ref 0x176ff70;
211
212 =item $ptr = Devel::FindRef::ref2ptr $reference
213
214 The opposite of C<ptr2ref>, above: returns the internal address of the
215 value pointed to by the passed reference. This function is safe to call on
216 anything, and returns the same value taht a normal reference would if used
217 in a numeric context.
218
219 =back
220
221 =head1 ENVIRONMENT VARIABLES
222
223 You can set the environment variable C<PERL_DEVEL_FINDREF_DEPTH> to an
224 integer to override the default depth in C<track>. If a call explicitly
225 specified a depth it is not overridden.
226
227 =head1 AUTHOR
228
229 Marc Lehmann <pcg@goof.com>.
230
231 =head1 COPYRIGHT AND LICENSE
232
233 Copyright (C) 2007, 2008 by Marc Lehmann.
234
235 This library is free software; you can redistribute it and/or modify
236 it under the same terms as Perl itself, either Perl version 5.8.8 or,
237 at your option, any later version of Perl 5 you may have available.
238
239 =cut
240
241 1
242