The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
#!/usr/local/bin/perl
#
#   $Id: 60leaks.t 291 2003-05-20 02:43:57Z edpratomo $
#
#   This is a memory leak test.
#

BEGIN { 
    $^W = 1;

    $COUNT_CONNECT = 500;   # Number of connect/disconnect iterations
    $COUNT_PREPARE = 10000;  # Number of prepare/execute/finish iterations
    $TOTALMEM   = 0;

    #
    #   Make -w happy
    #
    $test_dsn = '';
    $test_user = '';
    $test_password = '';
}


print "1..0 # Skipped: Long running memory leak test\n" and exit 0 unless ($^O eq 'linux' && $ENV{MEMORY_TEST});

#
#   Include lib.pl
#
use DBI;

#DBI->trace(2, "trace.txt");

$mdriver = "";
foreach $file ("lib.pl", "t/lib.pl", "DBD-~~dbd_driver~~/t/lib.pl") {
    do $file; if ($@) { print STDERR "Error while executing lib.pl: $@\n";
               exit 10;
              }
    if ($mdriver ne '') {
    last;
    }
}

sub ServerError() {
    print STDERR ("Cannot connect: ", $DBI::errstr, "\n",
    "\tEither your server is not up and running or you have no\n",
    "\tpermissions for acessing the DSN $test_dsn.\n",
    "\tThis test requires a running server and write permissions.\n",
    "\tPlease make sure your server is running and you have\n",
    "\tpermissions, then retry.\n");
    exit 10;
}

#
#   Main loop; leave this untouched, put tests after creating
#   the new table.
#

while (Testing()) {
    #
    #   Connect to the database
    Test($state or $dbh = DBI->connect($test_dsn, $test_user, $test_password))
    or ServerError();

    #
    #   Find a possible new table name
    #
    Test($state or $table = FindNewTable($dbh))
       or DbiError($dbh->err, $dbh->errstr);

    #
    #   Create a new table; EDIT THIS!
    #
    Test($state or ($def = TableDefinition($table,
                      ["id",   "INTEGER",  4, 0],
                      ["name", "CHAR",    64, 0]),
            $dbh->do($def)))
       or DbiError($dbh->err, $dbh->errstr);

    my($size, $prevSize, $ok, $notOk, $dbh2, $msg);

    if (!$state) {
        print "Testing memory leaks in connect/disconnect\n";
        $msg = "Possible memory leak in connect/disconnect detected";

        $ok = 0;
        $notOk = 0;

        for (my $i = 0;  $i < $COUNT_CONNECT;  $i++) {
            if (!($dbh2 = DBI->connect($test_dsn, $test_user,
                       $test_password))) 
            {
                $ok = 0;
                $msg = "Cannot connect: $DBI::errstr\n";
                last;
            }
            $dbh2->disconnect();
            undef $dbh2;

            if ($i == 0) {
                $ok = check_mem(1);     # initialize
            }
            elsif ($i % 100  ==  99) {
                $ok = check_mem();
            }
        }
    }
    Test($state or ($ok > $notOk))
    or print "$msg\n";


    if (!$state) {
        print "Testing memory leaks in prepare/execute/finish\n";
        $msg = "Possible memory leak in prepare/execute/finish detected";

        $ok = 0;
        $notOk = 0;
        undef $prevSize;

        # reconnect, if necessary
        unless ($dbh->ping) {
            $dbh = DBI->connect($test_dsn, $test_user, $test_password)
                or ServerError();
        }

        for (my $i = 0;  $i < $COUNT_PREPARE;  $i++) {
            my $sth = $dbh->prepare("SELECT * FROM $table");
            $sth->execute();
            $sth->finish();
            undef $sth;

            if ($i % 100  ==  99) {
                $ok = check_mem();
            }
        }
    }
    Test($state or ($ok > $notOk))
    or print "$msg\n";


    if (!$state) {
        print "Testing memory leaks in fetchrow_arrayref\n";
        $msg = "Possible memory leak in fetchrow_arrayref detected";

        # Insert some records into the test table
        my $row;
        foreach $row (
                    [1, 'Jochen Wiedmann'],
                    [2, 'Andreas König'],
                    [3, 'Tim Bunce'],
                    [4, 'Alligator Descartes'],
                    [5, 'Jonathan Leffler']) 
        {
            $dbh->do(sprintf("INSERT INTO $table VALUES (%d, %s)",
                 $row->[0], $dbh->quote($row->[1])));
        }

        $ok = 0;
        $notOk = 0;
        undef $prevSize;

        for (my $i = 0;  $i < $COUNT_PREPARE;  $i++) 
        {
            {
                my $sth = $dbh->prepare("SELECT * FROM $table");
                $sth->execute();
                my $row;
                while ($row = $sth->fetchrow_arrayref()) { }
                $sth->finish();
            }

            if ($i % 100  ==  99) {
                $ok = check_mem();
            }
        }
    }
    Test($state or ($ok > $notOk))
    or print "$msg\n";


    if (!$state) {
        print "Testing memory leaks in fetchrow_hashref\n";
        $msg = "Possible memory leak in fetchrow_hashref detected";

        $ok = 0;
        $notOk = 0;
        undef $prevSize;

        for (my $i = 0;  $i < $COUNT_PREPARE;  $i++) {
            {
                my $sth = $dbh->prepare("SELECT * FROM $table");
                $sth->execute();
                my $row;
                while ($row = $sth->fetchrow_hashref()) { }
                $sth->finish();
            }

            if ($i % 100  ==  99) {
                $ok = check_mem();
            }
        }
    }
    Test($state or ($ok > $notOk))
    or print "$msg\n";


    Test($state or $dbh->do("DROP TABLE $table"))
    or DbiError($dbh->err, $dbh->errstr);

}


# stolen from Matt Sergeant's XML::LibXML's memory.t 
sub check_mem {
    my $initialise = shift;
    # Log Memory Usage
    local $^W;
    my %mem;
    if (open(FH, "/proc/self/status")) {
        my $units;
        while (<FH>) {
            if (/^VmSize.*?(\d+)\W*(\w+)$/) {
                $mem{Total} = $1;
                $units = $2;
            }
            if (/^VmRSS:.*?(\d+)/) {
                $mem{Resident} = $1;
            }
        }
        close FH;

        if ($TOTALMEM != $mem{Total}) {
            warn("LEAK! : ", $mem{Total} - $TOTALMEM, " $units\n") unless $initialise;
            $TOTALMEM = $mem{Total};
            return 0;
        }

        print("# Mem Total: $mem{Total} $units, Resident: $mem{Resident} $units\n");
        return 1;
    }
}