Slash Boxes
NOTE: use Perl; is on undef hiatus. You can read content, but you can't post it. More info will be forthcoming forthcomingly.

All the Perl that's Practical to Extract and Report

use Perl Log In

Log In

[ Create a new account ]

petdance (2468)

AOL IM: petdance (Add Buddy, Send Message)
Yahoo! ID: petdance (Add User, Send Message)

I'm Andy Lester, and I like to test stuff. I also write for the Perl Journal, and do tech edits on books. Sometimes I write code, too.

Journal of petdance (2468)

Monday December 15, 2003
01:18 AM

New memory testing tools

[ #16339 ]
Here are a couple of slick new tools that make it easy to find circular references in your Perl structures that foul up Perl's reference counting.

Lincoln Stein just released Devel::Cycle the other day, and I just noticed it today. It checks a given reference for circular references (what Lincoln calls "memory cycles"), and thus memory leaks. "What a great tool," I thought, "I'll make a Test:: module out of it!" So I just now released the 0.01 version of Test::Memory::Cycle. I named it in the Test::Memory:: namespace in planning for future memory-testing modules.

The two work together really well. For instance, WWW::Mechanize has always been plagued with circular references. It keeps a queue of pages that have been visited, implemented with references back to prior objects. I'd sure like some way to verify that I haven't fouled anything. So I started adding memory_cycle_ok() calls in the t/*.t files, like so:

use Test::More tests => 18;
use_ok( 'WWW::Mechanize' );

my $agent = WWW::Mechanize->new();
isa_ok( $agent, "WWW::Mechanize" );

# ... do a whole bunch of Mech testing
# and then at the end ...

    eval "use Test::Memory::Cycle";
    skip "Test::Memory::Cycle not installed", 1 if $@;

    memory_cycle_ok( $agent, "No memory cycles found" );

The output format is pretty cool, too, if I do say so myself. Here's some sample memory cycle-creating code:

my $mom = {
    name => "Marilyn Lester",

my $me = {
    name => "Andy Lester",
    mother => $mom,
$mom->{son} = $me;

my $quinn = {
    name => "Quinn Lester",
    father => $me,
    grandmother => $mom,
$mom->{grandchild} = [ $quinn ];

Here, my mom and I each point at each other, and Mom and Quinn point at each other. Mom's pointing at Quinn is an anonymous list, to handle more grandchildren (my sister is 7 months pregnant), but Quinn only has the one grandma (in this example).

In Lincoln's module, you run find_cycle($me) and it prints to standard output:

Cycle (1):
              HASH(0x8004a0)->{mother} => HASH(0x800368)
          HASH(0x800368)->{grandchild} => ARRAY(0x804c6c)
                  ARRAY(0x804c6c)->[0] => HASH(0x8049f0)
         HASH(0x8049f0)->{grandmother} => HASH(0x800368)

Cycle (2):
              HASH(0x8004a0)->{mother} => HASH(0x800368)
          HASH(0x800368)->{grandchild} => ARRAY(0x804c6c)
                  ARRAY(0x804c6c)->[0] => HASH(0x8049f0)
              HASH(0x8049f0)->{father} => HASH(0x8004a0)

Cycle (3):
              HASH(0x8004a0)->{mother} => HASH(0x800368)
                 HASH(0x800368)->{son} => HASH(0x8004a0)

In mine, you call memory_cycle_ok($me) and it prints in the test diagnostic:

# Cycle (1)
#                         %A->{mother} => %B
#                     %B->{grandchild} => @C
#                              @C->[0] => %D
#                    %D->{grandmother} => %B
# Cycle (2)
#                         %A->{mother} => %B
#                     %B->{grandchild} => @C
#                              @C->[0] => %D
#                         %D->{father} => %A
# Cycle (3)
#                         %A->{mother} => %B
#                            %B->{son} => %A

I'm hoping that'll make debugging easier.

As always, your comments and suggestions are welcome!

The Fine Print: The following comments are owned by whoever posted them. We are not responsible for them in any way.
More | Login | Reply
Loading... please wait.