views:

618

answers:

6

Hello, when I run my program everything goes fine. At the end it prints out this:

*** glibc detected *** ./streamShare: double free or corruption (fasttop): 0x08292130 ***
======= Backtrace: =========
/lib/tls/i686/cmov/libc.so.6[0xcc2ff1]
/lib/tls/i686/cmov/libc.so.6[0xcc46f2]
/lib/tls/i686/cmov/libc.so.6(cfree+0x6d)[0xcc779d]
/usr/lib/libstdc++.so.6(_ZdlPv+0x21)[0x1c86f1]
./streamShare[0x804be7f]
./streamShare[0x804be3e]
./streamShare[0x804abc0]
./streamShare[0x804a5f2]
./streamShare[0x804a1c4]
./streamShare[0x804a1d7]
./streamShare[0x804a46a]
./streamShare[0x804ba45]
./streamShare[0x804b49c]
./streamShare[0x804ac68]
./streamShare[0x804ac48]
./streamShare[0x804a676]
./streamShare[0x804a237]
./streamShare[0x8049a3f]
./streamShare[0x804d2e5]
./streamShare[0x804d34d]
/lib/tls/i686/cmov/libc.so.6(__libc_start_main+0xe6)[0xc6eb56]
./streamShare[0x8049361]

I checked out, it happens when a function returns, where all the objects of the program are instatied automatically. Anyway I didn't define any destructor for these objects, I tried to use STL Containers and TR1 shared_ptr. I guess everything happens in the default destructors. Is there a way to know where does it breaks up? I mean, I'd like to know which object distruction does the whole mess. I'm using these containers and shared pointers:

typedef std::tr1::shared_ptr<messageListener> mlsptr;

typedef std::map<const char*, mlsptr, ltstr> CONSTCHP2MSLST;

messageListener doesn't have a distructor. And two of these vectors:

std::vector<MSG> queueto1;

where MSG destructor is:

MSG::~MSG() {
    destroy();
}

void MSG::destroy() {
    if (payload != NULL)
     delete[] payload;
    payload = NULL;
    payloadLen = 0;
}

that never gave problems before and I this it shouldn't ever...

Any recomendations how to track this problem? I'm clueless...

EDIT:

HERE IS VALGRIND OUTPUT:

valgrind ./streamShare -v
==25795== Memcheck, a memory error detector
==25795== Copyright (C) 2002-2009, and GNU GPL'd, by Julian Seward et al.
==25795== Using Valgrind-3.5.0-Debian and LibVEX; rerun with -h for copyright info
==25795== Command: ./streamShare -v
==25795== 
==25795== Invalid free() / delete / delete[]
==25795==    at 0x402454D: operator delete(void*) (vg_replace_malloc.c:346)
==25795==    by 0x804BCC0: std::tr1::_Sp_deleter<streamShare::messageListener>::operator()(streamShare::messageListener*) const (shared_ptr.h:97)
==25795==    by 0x804BC7F: std::tr1::_Sp_counted_base_impl<streamShare::messageListener*, std::tr1::_Sp_deleter<streamShare::messageListener>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr.h:75)
==25795==    by 0x804AAF7: std::tr1::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (boost_sp_counted_base.h:140)
==25795==    by 0x804A58D: std::tr1::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr.h:153)
==25795==    by 0x804A173: std::tr1::__shared_ptr<streamShare::messageListener, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr.h:358)
==25795==    by 0x804A186: std::tr1::shared_ptr<streamShare::messageListener>::~shared_ptr() (shared_ptr.h:834)
==25795==    by 0x804A405: std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >::~pair() (stl_pair.h:68)
==25795==    by 0x804D3D0: __gnu_cxx::new_allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >::destroy(std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >*) (new_allocator.h:115)
==25795==    by 0x804D337: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:383)
==25795==    by 0x804D29B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:972)
==25795==    by 0x804D27B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:970)
==25795==  Address 0x42c3358 is 0 bytes inside a block of size 8 free'd
==25795==    at 0x402454D: operator delete(void*) (vg_replace_malloc.c:346)
==25795==    by 0x804BCC0: std::tr1::_Sp_deleter<streamShare::messageListener>::operator()(streamShare::messageListener*) const (shared_ptr.h:97)
==25795==    by 0x804BC7F: std::tr1::_Sp_counted_base_impl<streamShare::messageListener*, std::tr1::_Sp_deleter<streamShare::messageListener>, (__gnu_cxx::_Lock_policy)2>::_M_dispose() (shared_ptr.h:75)
==25795==    by 0x804AAF7: std::tr1::_Sp_counted_base<(__gnu_cxx::_Lock_policy)2>::_M_release() (boost_sp_counted_base.h:140)
==25795==    by 0x804A58D: std::tr1::__shared_count<(__gnu_cxx::_Lock_policy)2>::~__shared_count() (shared_ptr.h:153)
==25795==    by 0x804A173: std::tr1::__shared_ptr<streamShare::messageListener, (__gnu_cxx::_Lock_policy)2>::~__shared_ptr() (shared_ptr.h:358)
==25795==    by 0x804A186: std::tr1::shared_ptr<streamShare::messageListener>::~shared_ptr() (shared_ptr.h:834)
==25795==    by 0x804A405: std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >::~pair() (stl_pair.h:68)
==25795==    by 0x804D3D0: __gnu_cxx::new_allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >::destroy(std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >*) (new_allocator.h:115)
==25795==    by 0x804D337: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_destroy_node(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:383)
==25795==    by 0x804D29B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:972)
==25795==    by 0x804D27B: std::_Rb_tree<char const*, std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> >, std::_Select1st<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >, streamShare::ltstr, std::allocator<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > > >::_M_erase(std::_Rb_tree_node<std::pair<char const* const, std::tr1::shared_ptr<streamShare::messageListener> > >*) (stl_tree.h:970)
==25795== 
==25795== 
==25795== HEAP SUMMARY:
==25795==     in use at exit: 0 bytes in 0 blocks
==25795==   total heap usage: 22 allocs, 30 frees, 496 bytes allocated
==25795== 
==25795== All heap blocks were freed -- no leaks are possible
==25795== 
==25795== For counts of detected and suppressed errors, rerun with: -v
==25795== ERROR SUMMARY: 8 errors from 1 contexts (suppressed: 19 from 8)
+2  A: 

First step is to compile your program with -g 3 switch so that you get much more debugging info.

There's not a lot to go on, but there is a possibility that your ~MSG() is getting called when the vector reallocates to grow.

Here's the most simple version of your code:

#include <vector>

struct MSG {
  MSG(int count);
  ~MSG();
  void destroy();
  char* payload;
  int payloadLen;
};

MSG::MSG(int count): payload(new char[count]), payloadLen(count) {}

MSG::~MSG() {
    destroy();
}

void MSG::destroy() {
    if (payload != NULL)
        delete[] payload;
    payload = NULL;
    payloadLen = 0;
}

std::vector<MSG> queueto1;

int main() {
    queueto1.push_back(MSG(10));
    return 0;
}

And G++ says:

block freed twice

Exited: ExitFailure 127

Now when the push_back(MSG(10)) is being called, it is creating a temporary MSG instance and then using the default copy constructor to set the item in the vector, before calling ~MSG() on the temporary (which deletes the payload that the item in the vector now points at).

You can implement the copy constructor MSG(const MSG& copy) to transfer ownership, but that requires a const_cast on the copy and is very very messy.

Two easy options are available: MSG::payload is a shared pointer, or queueto1 is a std::vector<MSG*>

Will
unfortunately, it's done upon destruction of the vector.
gotch4
exactly, the backtrace you posted is just missing debug information! also remove -Ox (-O1 -O2 or whatever was used) option. you used gdb, right?
frunsi
no optimization and debugging level is maximum...
gotch4
payload is initialized at NULL by the DEFAULT CONSTRUCTOR. If I compile your example G++ doesn't give any error. Anyway I think that upon copy, the default constructor is called, then payload whould be NULL and no problem should arise I think.
gotch4
Default constructor isn't called on copy; a copy constructor is called on copy. If you did not write a copy constructor for your class, then compiler will automatically provide one (which will do a memberwise copy) for you. Ditto for `operator=`.
Pavel Minaev
really, follow the link on the phrase "And G++ says" to see what G++ says!
Will
+1  A: 

to trace memory allocation and corruption problems you can use valgrind or Rational Purify

catwalk
I tried valgrind... says a bunch of stuff about STL templates and doesn't get me point out the problem...
gotch4
A: 

Printf the "payLoad" pointer and a counter as the deletes happen. You can then see a double free happening and which deletes it is ...

Goz
+12  A: 

Judging by your Valgrind output, the problem is that an object pointed to by a shared_ptr is being deleted twice. One possibility of getting that is if you initialize two shared_ptr with the same raw pointer, e.g.:

int* p = new int(123);
shared_ptr<int> sp1(p);
shared_ptr<int> sp2(p);

shared_ptr is not magical, and it cannot know if the object you ask it to take care of is already owned by some other unrelated shared_ptr, if all you give it is a raw pointer. In the example above, each shared_ptr will create its own reference counter, initialized to 1; when they die, each will decrement its own counter, see that it is 0, and delete the object, producing double deletion. I suspect your case is similar. If you show the code used to initialize shared_ptr objects that are added to the vector, this can be verified.

[EDIT] Since this is validated to be the cause of the crash now, let me elaborate a bit on how to properly use shared_ptr.

First of all, the nature of the problem. The way shared_ptr is written, it works with any C++ type, and provides reference counting semantics. The obvious problem is that most types do not provide any space to store the reference counter (e.g. consider shared_ptr<int> - there's no extra space "inside" int). To work around this, for every shared object, a separate block of memory is allocated that contains the reference counter. This is done whenever you create a shared_ptr from a raw pointer. The shared_ptr object itself then stores the original raw pointer, and the pointer to reference counter (which is why it's more "fat" than a raw pointer, which can be trivially checked with sizeof). When you create one shared_ptr from another (using copy constructor or assignment operator), it copies the pointer to reference counter, so all shared_ptr instances created from one another maintain a single counter, and guarantee correct deletion. But if you have two unrelated "families" of shared_ptr objects to the same objects (where two or more pointers were created from the same raw pointer), those "families" do not know about each other, and will refcount separately, and each will delete when it hits 0.

What this means in practice is that, when using shared_ptr, one must adhere to certain rules. Those depend on which implementation you use.

With std::tr1::shared_ptr, or older Boost versions, the only fully safe pattern for object allocation is this:

shared_ptr<T> x(new T(...));

In other words, the result of new should be immediately put into a shared_ptr - you can then copy the latter as much as you want.

A reasonably safe pattern is also this:

auto_ptr<T> x(new T);
...
shared_ptr<T> y(x);

shared_ptr implements the usual transfer-of-ownership when initializing from auto_ptr, and the semantics of the latter (so long as they're correctly followed) ensure that only one auto_ptr to an object should exist; thus, it is safe to construct a shared_ptr from that.

Sometimes you also have to deal with C++ libraries which do not use auto_ptr to indicate transfer of pointer ownership, but simply document the intent for specific functions. In those cases it should also be safe to use shared_ptr, but of course you should be sure that you've understood the documentation correctly...

In C++0x std::shared_ptr, and in newer versions of boost::shared_ptr, there's a helper provided to ensure correct instantiation of shared objects:

shared_ptr<int> p = make_shared<int>(123);

The return type of make_shared<T>() is already shared_ptr<T>, so at no point you're dealing with raw pointers in your code, reducing chance to get something wrong.

Pavel Minaev
S**T, you hit the problem man... It's the first time I used shared_ptr sorry :D I was passing the same raw pointer to many shared_ptr... this was because I replaced regular pointers whit shared ones and I forgot to change a couple of things!!!! Thanks!
gotch4
A: 

According to Valgrind the crash is taking place in the destruction of your CONSTCHP2MSLST instance. The problem likely resides in your messageListener implementation. Could you post the relevant contstructors and copy constructors for that class?

fbrereto
A: 

A side issue: you said you didn't define destructors, but rather used STL containers and shared pointers. What the containers and shared pointers do for you is call your destructors for you. They don't replace the destructor; they replace the need to figure out when to use the destructor.

This means that your classes got the default destructor, which consists of calling whatever destructors there are on base classes and members. If your classes don't actually own any resources except through smart pointers and the like, AND you're not using polymorphism, this can work. It does need more care than "I'm using STL constructs and smart pointers, so I don't need destructors".

If you have class A and class B: public A, and you ever have a pointer to A pointing to a B (raw pointer, smart pointer, it doesn't matter), you need a virtual destructor. Otherwise, when the pointer to A is deleted, it will destroy all subclasses and members of A, and ignore anything added in B. Therefore, you should have a virtual destructor in any base class that will be used in this way, and for safety's sake you should have a virtual destructor in any base class with any virtual function. Even if you don't have anything to get rid of in the destructor, you should have virtual A::~A() {} in your definition of A.

David Thornley