NAME
Test::Weaken - Test that freed references are, indeed, freed
SYNOPSIS
use Test::Weaken qw(leaks);
use Data::Dumper;
use Math::BigInt;
use Math::BigFloat;
my $good_test = sub {
my $obj1 = new Math::BigInt('42');
my $obj2 = new Math::BigFloat('7.11');
[ $obj1, $obj2 ];
};
my $bad_test = sub {
my $array = [ 42, 711 ];
push @{$array}, $array;
$array;
};
my $bad_destructor = sub { "I don't work" };
if ( !leaks( $good_test ) ) {
print "No leaks in test 1\n";
} else {
print "There were memory leaks from test 1!\n";
}
my $test = Test::Weaken::leaks({
constructor => $bad_test,
destructor => $bad_destructor,
});
if ( $test ) {
my $unfreed_proberefs = $test->unfreed_proberefs();
my $unfreed_count = @{$unfreed_proberefs};
printf "Test 2: %d of %d original references were not freed\n",
$test->unfreed_count(),
$test->probe_count();
print "These are the probe references to the unfreed objects:\n";
for my $proberef ( @{$unfreed_proberefs} ) {
print Data::Dumper->Dump( [$proberef], ['unfreed'] );
}
}
DESCRIPTION
Memory leaks happen when the memory allocated by objects which are no longer needed is not completely deallocated. (Deallocating memory is also called freeing it.) As an example in Perl, a memory leak will occur if an object contains circular references and does not have an effective scheme for weakening references or cleaning up memory. Leaked memory is a useless overhead. Leaky memory objects can significantly impact system performance. They can also cause the application with the leaks to abend due to lack of memory.
Test::Weaken
allows you to check that an object does not leak memory. If it does leak memory, Test::Weaken
allows you to examine the "leaked" memory objects, even objects that would usually be inaccessible. It performs this magic by creating a set of weakened probe references, as explained below.
The test object is passed as the return value of a closure. The closure should return the primary test reference, a reference to the test object. Test::Weaken
checks the memory that can be found by following the primary test reference. Starting with the primary test reference, arrays, hashes, weak references and strong references are followed recursively and to unlimited depth.
Test::Weaken
handles circular references gracefully. A major purpose of Test::Weaken
is to test schemes for circular references. To avoid infinite loops, Test::Weaken
records all the memory objects it visits, and will not visit the same memory object twice.
Why the Test Object is Passed via a Closure
Test::Weaken
does not accept its test object or its primary test reference directly as an argument. Instead, Test::Weaken
receives its test objects from test object constructors.
Why so roundabout? It turns out the indirect way is the easiest. The test object must not have any strong references to it from outside. It takes some craft to create the test object in Test::Weaken
's calling environment without leaving any reference to the test object in the calling environment, and it is easy to make a mistake.
When the calling environment retains a reference to memory contained in the test object, the result is a memory leak. Mistakes in setting up the test object, therefore, appear as false reports of memory leaks. These are hard to distinguish from the real thing.
Memory objects local to a closure will be destroyed when the closure returns, and any references they held will be released. When the test object is set up entirely in a closure, using only memory objects local to that closure, it becomes relatively easy to be sure that nothing is left behind that will hold an unintended reference to memory inside the test object.
To encourage this discipline, Test::Weaken
requires that its primary test reference be the return value of a closure. This makes what is the safe and almost always right thing to do, also the easiest thing to do.
Of course, if the user wants to, within the closure he can refer to data in global and other scopes from outside the closure. The user can also return memory objects created partially or completely from data in any or all of those scopes. Subverting Test::Weaken
's "closure data only" discipline can be done with only a small amount of trouble, certainly by comparison to the grief that the user is exposing himself to.
Returns and Exceptions
The methods of Test::Weaken
do not return errors. Errors are always thrown as exceptions.
METHODS
leaks
Arguments to the leaks
static method may be passed as a reference to a hash of named arguments, or directly as code references. leaks
returns a Test::Weaken
object if it found leaks, and a Perl false value otherwise. Users simply wanting to know if there were leaks can check whether the return value of leaks
is a Perl true or false. Users who want to look more closely at leaks can use other methods to interrogate the return value.
A test object constructor is a required argument. It must be a code reference. If passed directly, it must be the first argument to leaks
. Otherwise, it must be the value of the constructor
named argument.
The test object constructor should build the test object and create a primary test reference to it. The return value of the test object constructor must be the primary test reference. It is best to construct the test object inside the test object constructor as much as possible. That is the easiest way to construct a test object with no references into it from the calling environment.
The test object destructor is an optional argument. If specified, it must be a code reference. If passed directly, it must be the second argument to leaks
. Otherwise, it must be the value of the destructor
named argument.
If specified, the test object destructor is called just before the primary test reference is undefined. It will be passed one argument, the primary test reference. One purpose for the test object destructor is to allow Test::Weaken
to work with objects that require a destructor to be called on them when they are freed. For example, some of the objects created by Gtk2-Perl are of this type.
unfreed_proberefs
Returns a reference to an array of probe references to the unfreed memory objects. The user may examine these to find the source of a leak, or to produce her own statistics.
The array is returned as a reference because in some applications it can be quite long. The array contains probe references, not the memory objects themselves. This is because some memory objects, such as other arrays and hashes, cannot be elements of arrays. Weak references are another reason for not returning an array containing the memory objects themselves. Directly copying the weak references would strengthen them.
unfreed_count
Returns the count of unfreed memory objects. This count will be exactly the length of the array referred to by the return value of the unfreed_proberefs
method.
probe_count
Returns the total number of probe references in the test, including references to freed memory objects. This is the count of probe references after Test::Weaken
was finished following the test object reference recursively, but before it called the test object destructor and undefined the test object reference.
ADVANCED TECHNIQUES
The simplest way to use Test::Weaken
is to call the leaks
method, and treat its return value as a Perl true or false. But you can also use Test::Weaken
for tracing leaks. Here are some potentially helpful techniques.
Tracing Memory Leaks
The unfreed_proberefs
method returns an array containing the unfreed memory objects and can be used to find the source of leaks. If circumstances allow you to add elements to the arrays and hashes, you might find it useful to "tag" them for tracking purposes.
You can uniquely identify memory objects using the referent addresses of the probe references. A referent address can be determined by using the refaddr
method of Scalar::Util. You can also obtain the referent address of a reference by adding zero to the reference.
Note that in other Perl documentation, the term "reference address" is often used when a referent address is meant. Any given reference has both a reference address and a referent address. The reference address is the reference's own location in memory. The referent address is the address of the memory object to which it refers. It is the referent address that interests us here and, happily, it is the referent address that addition of zero and refaddr
return.
Testing Objects Which Refer to Persistent or External Memory
Your test object may refer to memory that is considered to be "outside" the object: external memory. In other cases, the specification of the object may allow certain memory referred to by the object to persist after the object is destroyed: persistent memory. External memory is often expected to be persistent memory.
To check for leaks in objects which refer to persistent memory, you can examine the unfreed objects returned by unfreed_proberefs
and eliminate the memory objects which are persistent. The remaining objects will be the memory leaks.
If You Really Must Test Deallocation of a Global
As explained above, Test::Weaken
receives its test object as the return value of a closure. It does this because it's tricky to create objects in a global environment without keeping references to them. References accidently held by the calling environment will cause false leak reports.
But you may have no other choice. The test object constructor can refer to data in a scope which is not local to the constructor. It can also return a primary test reference built using this data. Nothing prevents a test object constructor from, for example, simply returning a reference it finds in global scope as the primary test reference.
EXPORT
By default, Test::Weaken
exports nothing. Optionally, leaks
may be exported.
LIMITATIONS
Test::Weaken
does not check for leaked code references or look inside them.
IMPLEMENTATION
Test::Weaken
first recurses through the test object. It follows all weak and strong references, arrays and hashes. The test object is explored to unlimited depth, looking for memory objects, that is, objects which have memory allocated. Visited memory objects are tracked, and no memory object is visited twice. For each memory object, a probe reference is created.
Once recursion through the test object is complete, the probe references are weakened, so that they will not interfere with normal deallocation of memory. Next, the test object destructor is called, if there is one.
Finally, the primary test reference is undefined. This should trigger the complete deallocation of all memory held by the test object. To check that this happened, Test::Weaken
dereferences the probe references. If the referent of a probe reference was deallocated, the value of that probe reference will be undef
. If a probe reference is still defined at this point, it refers to an unfreed memory object.
AUTHOR
Jeffrey Kegler
BUGS
Please report any bugs or feature requests to bug-test-weaken at rt.cpan.org
, or through the web interface at http://rt.cpan.org/NoAuth/ReportBug.html?Queue=Test-Weaken. I will be notified, and then you'll automatically be notified of progress on your bug as I make changes.
SUPPORT
You can find documentation for this module with the perldoc command.
perldoc Test::Weaken
You can also look for information at:
AnnoCPAN: Annotated CPAN documentation
CPAN Ratings
RT: CPAN's request tracker
Search CPAN
SEE ALSO
Potential users will want to compare Test::Memory::Cycle and Devel::Cycle, which examine existing structures non-destructively. Devel::Leak also covers similar ground, although it requires Perl to be compiled with -DDEBUGGING
in order to work. Devel::Cycle looks inside closures if PadWalker is present, a feature Test::Weaken
does not have at present.
ACKNOWLEDGEMENTS
Thanks to jettero, Juerd and perrin of Perlmonks for their advice. Thanks to Lincoln Stein (developer of Devel::Cycle) for test cases and other ideas.
After the first release of Test::Weaken
, Kevin Ryde made several important suggestions and provided test cases. These provided the impetus for version 2.000000.
LICENSE AND COPYRIGHT
Copyright 2007-2009 Jeffrey Kegler, all rights reserved.
This program is free software; you can redistribute it and/or modify it under the same terms as Perl 5.10.