Wednesday, December 28, 2011

Useful Trick for Debugging Memory Leaks in C++ Code

The summer of 2010 was fucking awesome. I was living on campus at my university, taking two classes (neither of which started before 6:00pm), and lived with my friends in the house of a fraternity that we didn't belong to. I was waking up at 7:00am every day to run and had obscene amounts of free time.

I was also building a 2D video game engine from scratch in my spare time. I didn't have any serious intentions for the project, but it was something to do and was a great learning experience. From that project, I learned how critical impeccable code organization is, I learned tricks such as using pointers to class member functions, and I had a blast doing it.

Somewhere along the line, I was reading about C++'s ability to overload the new and delete operators, and in the process came up with a great, simple trick for detecting memory leaks. By overloading the memory allocation operators to keep track of the total amount of memory allocated by the program at any given time, one need only check, at the conclusion of the program, how much memory remains allocated. If the value is anything greater than 0, you've got memory leaking.

"But wait!" you say, "How do I know how much memory has been freed by each delete call? The operator delete signature doesn't give me that information!"

A valid objection. Here's the trick: Every time your custom new function gets called, use malloc() to dynamically overallocate the memory requested by exactly the size of a size_t variable. Store the amount of memory which is being allocated at the address returned by malloc(), and return the address which is sizeof(size_t) bytes after the address returned by malloc()! This way, when delete is called, you need only look sizeof(size_t) bytes backwards to determine how much memory was originally allocated.

I'll provide source code below. Note the use of a macro _TRACKMEMORY which can be switched on and off; although this trick is great for debugging, it does of course allocate more memory than is necessary, and thus it would not be a good idea to compile the code into a release build of a binary.

   unsigned long totalAllocation = 0;
   void* operator new(size_t sz)
      void* storage = malloc (sz + sizeof(size_t));   
      totalAllocation += sz;
      *((size_t*)storage) = sz;
      return ((char*)storage) + sizeof(size_t);
   void operator delete (void* ptr)
      totalAllocation -= *(size_t*)((char*)ptr - sizeof(size_t));
      free(((char*)ptr) - sizeof(size_t));

unsigned long GetAllocatedMemory( void )
   #ifndef _TRACKMEMORY
      return 0;
      return totalAllocation;

Ideally of course, you simply compile this into a separate source file (I like to call it TrackMemory.cpp). From there, you just call GetAllocatedMemory() every time you want to know the amount of memory currently allocated by the program; of course, it is necessary to expose the declaration of GetAllocatedMemory() through a header file before calling it. This may go without saying, but of course this trick will not necessarily work as intended if your program is allocating memory through some method besides using the global new operator (malloc, class-specific new operator overloads, etc).

Let me know if you think this is as useful as I do.