Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Howto debug double deletes in C++?

I'm maintaining a legacy application written in C++. It crashes every now and then and Valgrind tells me its a double delete of some object.

What are the best ways to find the bug that is causing a double delete in an application you don't fully understand and which is too large to be rewritten ?

Please share your best tips and tricks!

like image 909
Gene Vincent Avatar asked Apr 09 '12 17:04

Gene Vincent


People also ask

What if we delete a pointer twice?

If delete is applied to one of the pointers, then the object's memory is returned to the free store. If we subsequently delete the second pointer, then the free store may be corrupted.

What is double deleting?

Double-deletion is when you delete an email message from your inbox and then immediately delete it from the trash folder as well.

What happens if we delete a pointer twice in C++?

The answer is nothing happens.


3 Answers

Here's some general suggestion's that have helped me in that situation:

  1. Turn your logging level up to full debug, if you are using a logger. Look for suspicious stuff in the output. If your app doesn't log pointer allocations and deletes of the object/class under suspicion, it's time to insert some cout << "class Foo constructed, ptr= " << this << endl; statements in your code (and corresponding delete/destructor prints).
  2. Run valgrind with --db-attach=yes. I've found this very handy, if a bit tedious. Valgrind will show you a stack trace every time it detects a significant memory error or event and then ask you if you want to debug it. You may find yourself repeatedly pressing 'n' many many times if your app is large, but keep looking for the line of code where the object in question is first (and secondly) deleted.
  3. Just scour the code. Look for construction/deletion of the object in question. Sadly, sometimes it winds up being in a 3rd party library :-(.
  4. Update: Just found this out recently: Apparently gcc 4.8 and later (if you can use GCC on your system) has some new built-in features for detecting memory errors, the "address sanitizer". Also available in the LLVM compiler system.
like image 93
Ogre Psalm33 Avatar answered Oct 31 '22 16:10

Ogre Psalm33


Yep. What @OliCharlesworth said. There's no surefire way of testing a pointer to see if it points to allocated memory, since it really is just the memory location itself.

The biggest problem your question implies is the lack of reproducability. Continuing with that in mind, you're stuck with changing simple 'delete' constructs to delete foo;foo = NULL;.

Even then the best case scenario is "it seems to occur less" until you've really stamped it down.

I'd also ask by what evidence Valgrind suggests it's a double-delete problem. Might be a better clue lingering around in there.

It's one of the simpler truly nasty problems.

like image 38
Michael Wilson Avatar answered Oct 31 '22 17:10

Michael Wilson


This may or may not work for you.

Long time ago I was working on 1M+ lines program that was 15 years old at the time. Faced with the exact same problem - double delete with huge data set. With such data any out of the box "memory profiler" would be a no go.

Things that were on my side:

  1. It was very reproducible - we had macro language and running same script exactly the same way reproduced it every time
  2. Sometime during the history of the project someone decided that "#define malloc my_malloc" and "#define free my_free" had some use. These didn't do much more than call built-in malloc() and free() but project already compiled and worked this way.

Now the trick/idea:

my_malloc(int size)
{
   static int allocation_num = 0;  // it was single threaded

   void* p = builtin_malloc(size+16);

   *(int*)p = ++allocation_num;
   *((char*)p+sizeof(int)) = 0; // not freed

   return (char*)p+16;  // check for NULL in order here
}

my_free(void* p)
{
    if (*((char*)p+sizeof(int)))
    {
        // this is double free, check allocation_number
        // then rerun app with this in my_alloc
        //    if (alloc_num == XXX) debug_break();
    }

    *((char*)p+sizeof(int)) = 1; // freed

    //built_in_free((char*)p-16);  // do not do this until problem is figured out
}

With new/delete it might be trickier, but still with LD_PRELOAD you might be able to replace malloc/free without even recompiling your app.

like image 2
dpiskyulev Avatar answered Oct 31 '22 17:10

dpiskyulev