Possible memory leak?

P

Pep

I have recently eradicated a lot of memory leaks in a very old C++
source set. However, whilst they were all fairly easy to resolve, I am
confused by the last one. This seems to be related to throwing a
std::exception with a string object.

This is the test program

=============================================================================
#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <stdexcept>

int main(int argc, char** argv)
{
std::eek:stringstream os;
os << "Test string";
throw std::eek:ut_of_range(os.str());

return(0);
}
=============================================================================

and this is the output from valgrind when I run it

=============================================================================
[[email protected] trunk]$ g++ -o test-ostringstream test-
ostringstream.cc
[[email protected] trunk]$ valgrind --leak-check=yes ./test-
ostringstream
==23358== Memcheck, a memory error detector.
==23358== Copyright (C) 2002-2007, and GNU GPL'd, by Julian Seward et
al.
==23358== Using LibVEX rev 1854, a library for dynamic binary
translation.
==23358== Copyright (C) 2004-2007, and GNU GPL'd, by OpenWorks LLP.
==23358== Using valgrind-3.3.1, a dynamic binary instrumentation
framework.
==23358== Copyright (C) 2000-2007, and GNU GPL'd, by Julian Seward et
al.
==23358== For more details, rerun with: -v
==23358==
==23358==
==23358== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 19 from
1)
==23358== malloc/free: in use at exit: 2,184 bytes in 3 blocks.
==23358== malloc/free: 3 allocs, 0 frees, 2,184 bytes allocated.
==23358== For counts of detected errors, rerun with: -v
==23358== searching for pointers to 3 not-freed blocks.
==23358== checked 104,152 bytes.
==23358==
==23358== 960 bytes in 1 blocks are possibly lost in loss record 2 of
3
==23358== at 0x401AD81: operator new(unsigned) (vg_replace_malloc.c:
224)
==23358== by 0x409D0F0: std::__default_alloc_template<true,
0>::_S_chunk_alloc(unsigned, int&) (stl_alloc.h:109)
==23358== by 0x409CFFC: std::__default_alloc_template<true,
0>::_S_refill(unsigned) (stl_alloc.h:561)
==23358== by 0x409CB6B: std::__default_alloc_template<true,
0>::allocate(unsigned) (stl_alloc.h:365)
==23358== by 0x40A2B67: std::string::_Rep::_S_create(unsigned,
std::allocator<char> const&) (stl_alloc.h:685)
==23358== by 0x40A38F4: char*
std::string::_S_construct<char*>(char*, char*, std::allocator<char>
const&, std::forward_iterator_t
ag) (basic_string.tcc:154)
==23358== by 0x40A2F84: std::string::string<char*>(char*, char*,
std::allocator<char> const&) (basic_string.h:732)
==23358== by 0x4097959: std::basic_stringbuf<char,
std::char_traits<char>, std::allocator<char> >::str() const
(stl_alloc.h:664)
==23358== by 0x409A57B: std::basic_ostringstream<char,
std::char_traits<char>, std::allocator<char> >::str() const (sstream:
301)
==23358== by 0x8048B84: main (in /mgr/shared/home/pwalker/tweb/
tweb_release/trunk/test-ostringstream)
==23358==
==23358==
==23358== 1,136 bytes in 1 blocks are possibly lost in loss record 3
of 3
==23358== at 0x401AD81: operator new(unsigned) (vg_replace_malloc.c:
224)
==23358== by 0x409CB02: std::__default_alloc_template<true,
0>::allocate(unsigned) (stl_alloc.h:109)
==23358== by 0x40A2B67: std::string::_Rep::_S_create(unsigned,
std::allocator<char> const&) (stl_alloc.h:685)
==23358== by 0x40A2C98:
std::string::_Rep::_M_clone(std::allocator<char> const&, unsigned)
(basic_string.tcc:474)
==23358== by 0x40A0A05: std::string::reserve(unsigned)
(basic_string.h:237)
==23358== by 0x4097C28: std::basic_stringbuf<char,
std::char_traits<char>, std::allocator<char> >::eek:verflow(int)
(sstream.tcc:103
)
==23358== by 0x409DED3: std::basic_streambuf<char,
std::char_traits<char> >::xsputn(char const*, int) (char_traits.h:161)
==23358== by 0x40948D0: std::eek:stream::write(char const*, int)
(streambuf:340)
==23358== by 0x4094FE5: std::basic_ostream<char,
std::char_traits said:
(std::basic_o
stream<char, std::char_traits<char> >&, char const*) (ostream.tcc:640)
==23358== by 0x8048B6B: main (in /mgr/shared/home/pwalker/tweb/
tweb_release/trunk/test-ostringstream)
==23358==
==23358== LEAK SUMMARY:
==23358== definitely lost: 0 bytes in 0 blocks.
==23358== possibly lost: 2,096 bytes in 2 blocks.
==23358== still reachable: 88 bytes in 1 blocks.
==23358== suppressed: 0 bytes in 0 blocks.
==23358== Reachable blocks (those to which a pointer was found) are
not shown.
==23358== To see them, rerun with: --leak-check=full --show-
reachable=yes
Aborted
=============================================================================

If I remove the throw, then valgrind says everything is fine. So my
question is whether this is expected behavior from the throw and if
not, how do I solve it?

TIA
 
K

Kai-Uwe Bux

Pep said:
I have recently eradicated a lot of memory leaks in a very old C++
source set. However, whilst they were all fairly easy to resolve, I am
confused by the last one. This seems to be related to throwing a
std::exception with a string object.

This is the test program

=============================================================================
#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <stdexcept>

int main(int argc, char** argv)
{
std::eek:stringstream os;
os << "Test string";
throw std::eek:ut_of_range(os.str());

return(0);
}
=============================================================================

and this is the output from valgrind when I run it [snip]

If I remove the throw, then valgrind says everything is fine. So my
question is whether this is expected behavior from the throw and if
not, how do I solve it?

The following is valgrind-clean on my platform:

#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <stdexcept>

int main(int argc, char** argv)
{
try {
std::eek:stringstream os;
os << "Test string";
throw std::eek:ut_of_range(os.str());
}
catch ( std::exception const & e ) {
std::cerr << "exception propagated into main: "
<< e.what() << "\n";
}
catch ( ... ) {
std::cerr << "unidentified thing caught in main.\n";
}
return(0);
}


Best

Kai-Uwe Bux
 
K

khalid302

The following is valgrind-clean on my platform:
#include <sstream>
#include <iostream>
#include <stdlib.h>
#include <stdexcept>

int main(int argc, char** argv)
{
  try {
        std::eek:stringstream os;
        os << "Test string";
        throw std::eek:ut_of_range(os.str());
  }
  catch ( std::exception const & e ) {
    std::cerr << "exception propagated into main: "
              << e.what() << "\n";
  }
  catch ( ... ) {
    std::cerr << "unidentified thing caught in main.\n";
  }
  return(0);

}

Does this mean that having an exception thrown without being caught
causes a memory leak ?
 
J

Juha Nieminen

Does this mean that having an exception thrown without being caught
causes a memory leak ?

Exceptions should always be caught. If an exception is thrown out of
main(), bad things can happen (at least with some compilers and systems).

(OTOH I find it strange that it would leak anything. The stack should
be unwound normally even if it's the main() function.)
 
J

James Kanze

Exceptions should always be caught. If an exception is thrown
out of main(), bad things can happen (at least with some
compilers and systems).

Such as? The behavior of an uncaught exception is defined by
the standard, and I'm not aware of any compilers which have
problems in this regard.
(OTOH I find it strange that it would leak anything. The stack
should be unwound normally even if it's the main() function.)

If the exception is not caught, it is unspecified whether the
stack is unwound or not---I think that typically, it isn't, so
you still have the information as to where the exception was
thrown in your core dump.

Of course, under a well behaved operating system, nothing leaks,
even if a process terminates abnormally.
 
A

acehreli

  (OTOH I find it strange that it would leak anything. The stack should
be unwound normally even if it's the main() function.)

The thrown object lives somewhere else outside of any stack frame. (I
am not sure whether this is by the standard, or just an implementation
necessity.) So apparently the OP's platform does not destroy that
object if not caught.

Ali
 
P

Pep

Exceptions should always be caught. If an exception is thrown out of
main(), bad things can happen (at least with some compilers and systems).

(OTOH I find it strange that it would leak anything. The stack should
be unwound normally even if it's the main() function.)

Hey everyone, thanks for the replies.

Firstly, I did not even think about the fact that the test program and
therefore the live one that it is based on, is not catching the thrown
exception. Seems silly now you pointed it out, as the main program
itself should really handle the exception and then send the error
status back to the cli via a return statement.

As to whether valgrind is simply wrong, that is another good question
that I had been toying with myself, yet loathe to mention. It always
seems easier to blame the tools ;) However, I am loading up some more
memory testers on this dev server, so that I should be able to answer
that question soon enough.

Apologies for not mentioning that the dev server in question is RHEL
3.0

Thanks again for the replies, I'll catch the exception and handle it
correctly now.
 
J

Juha Nieminen

James said:
Of course, under a well behaved operating system, nothing leaks,
even if a process terminates abnormally.

No OS can know if the program created some temporary files which it
intends to delete when it terminates.
 
J

James Kanze

No OS can know if the program created some temporary files
which it intends to delete when it terminates.

Sure it can. Perhaps the most common OS's don't do this, but
they certainly could (and I've used at least one OS which did).
For that matter, it's possible to do so under Unix (but only to
a limited extent).

More generally, of course, you're right in that there will
always be some resources for which it isn't clear whether they
should remain or not. g++ (and probably many other compilers)
has an option to explicitly not delete intermediate files; if
something crashes, they can be very useful for debugging. Is
this a resource leak? I'd say yes, but an intentional one.
For that matter, if I don't catch an exception under Unix, I get
a core dump, which can eat up considerable resources.

Within the context of the original posting, however: if a
program terminates because of an uncaught exception, the system
should recover all of the critical resources, without a stack
walkback, and a stack walkback is likely to destroy resoures,
and above all context, that you want to keep, for debugging
purposes. The standard makes it implementation defined (or
unspecified, I'm not sure) whether there is a stack walkback in
such cases, but from a quality of implementation point of view,
I think not walking back the stack is to be preferred.
 
J

James Kanze

Indeed. But in the case of memory testers, it's not uncommon
to get false positives for resources that are managed in
peculiar ways by the runtime library. Exception objects are
not like normal objects, and I wouldn't be surprised if memory
testers sometimes get them wrong.

More generally, there are really cases where "it depends".
Purify (but probably all of the others as well---I've just not
had the occasion to check) has options to allow masking certain
"errors", because they're intentional (e.g. you don't destruct a
singleton), but the tool has no way of knowing this.
 
P

Pascal J. Bourguignon

James Kanze said:
Sure it can. Perhaps the most common OS's don't do this, but
they certainly could (and I've used at least one OS which did).
For that matter, it's possible to do so under Unix (but only to
a limited extent).

What limit do you see in:

char path[]="/tmp/myappl-XXXXXX";
int fd=mkstemp(path);
if(fd!=-1){
unlink(path);
}

knowing that unix systems implement a garbage collector over /tmp on
each reboot (and sometimes even more often), for the unlikely case
where the program crashes between the mkstemp call and the unlink
call.
 
M

Matthias Buelow

Pascal said:
What limit do you see in:

Other programs can't access it unless you smuggle the open file
descriptor over a socket or something similarly arcane (if supported by
the OS in question).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,020
Latest member
GenesisGai

Latest Threads

Top