Skip Menu |

This queue is for tickets about the Perl-RPM CPAN distribution.

Report information
The Basics
Id: 12120
Status: open
Priority: 0/
Queue: Perl-RPM

People
Owner: Nobody in particular
Requestors: at [...] altlinux.org
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: 0.40
Fixed in: (no value)



Subject: memory leaks
Hello, I found that Perl-RPM suffers memory leaks. Even opening RPM database introduces a leak: $ perl -MRPM::Database -e 'RPM::Database->new while 1' Now you can launch top(1) and see ever increasing memory usage. Thanks to Devel::Leak, I think I managed to fix some leaks. The patch is attached. $ lsdiff perl-RPM-0.40-alt-fix-memleaks.patch Perl-RPM-0.40/t/09_leaks.t Perl-RPM-0.40/RPM/Database.xs Perl-RPM-0.40/RPM/Header.xs Perl-RPM-0.40/typemap $ Note that I also disabled database cache (dbstruct->storage). This is because quering a lot of packages (and some algorithms may want to query each package in the database) makes the whole database to be cached into memory. After all, Berkeley DB seems to have its own caching algorithms. Even with this, 09_leaks.t takes more than 100M to run. So there seems to be other leaks (or sort of caching), but hopefully not on the side of Perl-RPM. -- Alexey Tourbin ALT Linux Team
--- Perl-RPM-0.40/t/09_leaks.t- 2005-04-02 19:07:11 +0400 +++ Perl-RPM-0.40/t/09_leaks.t 2005-04-02 19:11:46 +0400 @@ -0,0 +1,48 @@ +#!/usr/bin/perl +use strict; +use Devel::Leak; +use Test::More tests => 8; + +sub test_leak (&$;$) { + my ($code, $descr, $maxleak) = (@_, 0); + my $n1 = Devel::Leak::NoteSV(my $handle); + $code->() for 0..3; + my $n2 = Devel::Leak::CheckSV($handle); + cmp_ok($n1 + $maxleak, '>=', $n2, $descr); +} + +use RPM::Database; + +test1: test_leak { my $db = RPM::Database->new or die } + "rpmdb_TIEHASH", 1; # XXX + +test2: test_leak { my $db = RPM::Database->new(root => "/dev/null") } + "rpmdb_TIEHASH w/ invalid args (errSV set OK)", 1; + +test3: test_leak { my $db = RPM::Database->new or die; + for (0..3) { my $hdr = $$db{rpm} or die; } } + "rpmdb_FETCH"; + +test4: test_leak { my $db = RPM::Database->new or die; + for (0..3) { my $hdr = $$db{rpm} or die; + for (0..3) { + my $name = $$hdr{NAME} or die; + my $summary = $$hdr{SUMMARY} or die; } } } + "rpmhdr_FETCH"; + +test5: test_leak { my $db = RPM::Database->new or die; + for (0..3) { $db->find_by_file("/usr/bin/perl") or die; } } + "find_by_file"; + +test6: test_leak { my $db = RPM::Database->new or die; + for (0..3) { $db->find_what_provides("libc.so.6") or die; } } + "find_what_provides"; + +# expensive tests +test7: test_leak { my $db = RPM::Database->new or die; + for (0..3) { $db->find_what_requires("libc.so.6") or die; } } + "find_what_requires"; + +test8: test_leak { my $db = RPM::Database->new or die; + while (my ($k, $v) = each %$db) { die if $k eq $v; } } + "rpmdb_NEXTKEY"; --- Perl-RPM-0.40/RPM/Database.xs- 2002-05-10 09:53:48 +0400 +++ Perl-RPM-0.40/RPM/Database.xs 2005-04-02 19:16:53 +0400 @@ -56,8 +56,11 @@ /* The retvalp is used for the C-level rpmlib information on databases */ Newz(0, retvalp, 1, RPM_Database); if (rpmdbOpen(root, &retvalp->dbp, mode, perms) != 0) + { + Safefree(retvalp); /* rpm lib will have set the error already */ return (Null(RPM__Database)); + } else { retvalp->current_rec = 0; @@ -100,11 +103,13 @@ { name = SvPV(key, namelen); +#if 0 /* Step 1: Check to see if this has already been requested and is thus cached on the hash itself */ svp = hv_fetch(dbstruct->storage, (char *)name, namelen, FALSE); if (svp && SvROK(*svp)) return newSVsv(*svp); +#endif offset = -1; lasthdr = NULL; @@ -155,14 +160,18 @@ FETCHp = rpmhdr_TIEHASH(aTHX_ "RPM::Header", sv_2mortal(newSViv((unsigned)hdr)), RPM_HEADER_FROM_REF | RPM_HEADER_READONLY); +#if 0 if (name == Null(const char *)) name = SvPV(rpmhdr_FETCH(aTHX_ FETCHp, - sv_2mortal(newSVpv("NAME", 4)), + sv_2mortal(newSVpvn("NAME", 4)), Null(const char *), 0, 0), namelen); +#endif FETCH = sv_bless(newRV_noinc((SV*)FETCHp), gv_stashpv("RPM::Header", TRUE)); +#if 0 hv_store(dbstruct->storage, (char *)name, namelen, newSVsv(FETCH), FALSE); +#endif } rpmdbFreeIterator(mi); @@ -173,7 +182,7 @@ { SV* tmp; - tmp = rpmdb_FETCH(aTHX_ self, key); + tmp = sv_2mortal(rpmdb_FETCH(aTHX_ self, key)); return (tmp != &PL_sv_undef); } @@ -194,14 +203,14 @@ Header h; if (dbstruct->offsets) - free(dbstruct->offsets); + free(dbstruct->offsets); /* realloc */ dbstruct->offsets = NULL; dbstruct->noffs = 0; mi = rpmdbInitIterator(dbstruct->dbp, RPMDBI_PACKAGES, NULL, 0); while ((h = rpmdbNextIterator(mi)) != NULL) { dbstruct->noffs++; - dbstruct->offsets = + dbstruct->offsets = /* XXX realloc? */ realloc(dbstruct->offsets, dbstruct->noffs * sizeof(dbstruct->offsets[0])); dbstruct->offsets[dbstruct->noffs-1] = rpmdbGetIteratorOffset(mi); @@ -215,8 +224,8 @@ dbstruct->offx = 0; dbstruct->current_rec = dbstruct->offsets[dbstruct->offx++]; - *value = rpmdb_FETCH(aTHX_ self, newSViv(dbstruct->current_rec)); - *key = rpmhdr_FETCH(aTHX_ (RPM__Header)SvRV(*value), newSVpv("name", 4), + *value = rpmdb_FETCH(aTHX_ self, sv_2mortal(newSViv(dbstruct->current_rec))); + *key = rpmhdr_FETCH(aTHX_ (RPM__Header)SvRV(*value), sv_2mortal(newSVpvn("name", 4)), Nullch, 0, 0); return 1; @@ -236,9 +245,9 @@ dbstruct->current_rec = dbstruct->offsets[dbstruct->offx++]; - *nextvalue = rpmdb_FETCH(aTHX_ self, newSViv(dbstruct->current_rec)); + *nextvalue = rpmdb_FETCH(aTHX_ self, sv_2mortal(newSViv(dbstruct->current_rec))); *nextkey = rpmhdr_FETCH(aTHX_ (RPM__Header)SvRV(*nextvalue), - newSVpv("name", 4), Nullch, 0, 0); + sv_2mortal(newSVpvn("name", 4)), Nullch, 0, 0); return 1; } @@ -251,11 +260,13 @@ rpmdbClose(dbstruct->dbp); if (dbstruct->offsets) - Safefree(dbstruct->offsets); + free(dbstruct->offsets); /* realloc */ hv_undef(dbstruct->storage); + sv_free((SV*) dbstruct->storage); Safefree(dbstruct); - hv_undef(self); +/* CLEAR: operation not permitted + hv_undef(self); */ } int rpmdb_init(SV* class, const char* root, int perms) @@ -312,7 +323,7 @@ { idx = rpmdbGetIteratorOffset(mi); tmp_hdr = rpmdb_FETCH(aTHX_ self, sv_2mortal(newSViv(idx))); - av_store(return_val, loop++, sv_2mortal(newSVsv(tmp_hdr))); + av_store(return_val, loop++, newSVsv(sv_2mortal(tmp_hdr))); } } rpmdbFreeIterator(mi); @@ -410,7 +421,7 @@ EXTEND(SP, 2); PUSHs(sv_2mortal(value)); - PUSHs(sv_2mortal(newSVsv(key))); + PUSHs(sv_2mortal(key)); } void @@ -431,7 +442,7 @@ EXTEND(SP, 2); PUSHs(sv_2mortal(nextvalue)); - PUSHs(sv_2mortal(newSVsv(nextkey))); + PUSHs(sv_2mortal(nextkey)); } void @@ -503,5 +514,7 @@ else size = 0; + av_undef(matches); + sv_free((SV*) matches); XSRETURN(size); } --- Perl-RPM-0.40/RPM/Header.xs- 2002-05-10 11:37:08 +0400 +++ Perl-RPM-0.40/RPM/Header.xs 2005-04-02 18:39:16 +0400 @@ -203,6 +203,7 @@ { new_item = newSVsv(*av_fetch(new_list, 0, FALSE)); av_undef(new_list); + sv_free((SV *) new_list); } else new_item = newRV_noinc((SV *)new_list); @@ -368,6 +370,8 @@ return FETCH; } + sv_free(FETCH); + /* Check the three keys that are cached directly on the struct itself: */ if (! strcmp(uc_name, "NAME")) FETCH = newSVpv((char *)hdr->name, 0); @@ -409,6 +413,7 @@ "RPM::Header::FETCH: no tag '%s' in header", uc_name); rpm_error(aTHX_ RPMERR_BADARG, errmsg); Safefree(uc_name); + FETCH = newSVsv(&PL_sv_undef); return FETCH; } FETCH = rpmhdr_create(aTHX_ new_item_p, new_item_type, size, @@ -954,10 +959,12 @@ headerFree(hdr->hdr); hv_undef(hdr->storage); + sv_free((SV *) hdr->storage); if (hdr->source_name) Safefree(hdr->source_name); Safefree(hdr); - hv_undef(self); +/* CLEAR: operation not permitted + hv_undef(self); */ } unsigned int rpmhdr_size(pTHX_ RPM__Header self) @@ -1382,10 +1389,9 @@ snprintf(errmsg, 256, "RPM::Header::scalar_tag: unknown tag %s", uc_name); rpm_error(aTHX_ RPMERR_BADARG, errmsg); - Safefree(uc_name); RETVAL = 0; } - + Safefree(uc_name); RETVAL = scalar_tag(aTHX_ self, tag_value); } } --- Perl-RPM-0.40/typemap- 2000-11-10 11:49:57 +0300 +++ Perl-RPM-0.40/typemap 2005-04-02 15:16:17 +0400 @@ -18,7 +18,7 @@ } else { - $arg = newSVsv(&PL_sv_undef); + $arg = &PL_sv_undef; } O_RPM_Blessed @@ -29,7 +29,7 @@ } else { - $arg = newSVsv(&PL_sv_undef); + $arg = &PL_sv_undef; } INPUT @@ -54,4 +54,4 @@ rpm_error(aTHX_ RPMERR_BADARG, \"$var is not of type ${ntype}\"); XSRETURN_UNDEF; - } \ No newline at end of file + }
All known memory leaks fixed in Perl-RPM-1.50.