tags:

views:

132

answers:

4

I'm trying to detect memory leaks by globally overloading new and delete for debug builds and maintaining a list of allocated blocks. This works, but incorrectly reports leaks for some static objects. For example a static std::vector itself is not allocated using new and delete but its internal buffers are. Since my detection code runs before the main function returns the destructors of these objects are not yet called so they haven't deleted their buffers yet and this appears as a "leak".

One workaround I've found is by deleting and placement newing each static object to clear out their buffers before I check for leaks:

std::vector<int> f;

f.~std::vector<int>();
new (&f) std::vector<int>();

This is really ugly though and including cleanup code that isn't required seems bad. Is there some way of getting my code to run after the statics have been destroyed? (even if it's Microsoft-specific)

+2  A: 

You need to look into std::allocator - the second template parameter of almost all STL containers.

Nikolai N Fetissov
How would that help? I'm logging the allocations, theres just no way of doing the final leak check after the static objects have been destroyed
Lucas
The allocator is actually the entity that manages memory for the containers. Implement your "leak-detecting" allocator and give it to container instantiation. Don't worry about the static objects themselves - they are allocated at compile time.
Nikolai N Fetissov
Oh I see, I could give all the static containers another allocator which would exclude them from being tracked. Yeah that would work, but it would require me to change lots of code all over my system due to the fact that the allocator is a template parameter.
Lucas
That's where typedef comes in handy :)
Nikolai N Fetissov
I ended up going with your solution and making a special untracked allocator which I used for my static containers. The containers mostly contain only pointers to other objects so I'm pretty confident I'm not missing any leaks by doing this. Also it was a good excuse to go through and add typedefs. I was too lazy before :)
Lucas
A: 

You could try using an atexit handler.

Ganesh Sittampalam
Thanks, I didn't know about that function. While atexit wont work for me since the code is in a dll, the Microsoft-specific _onexit will. However, I just tried it and unfortunately my function still executes before the static destructors are called
Lucas
+1  A: 

Write a function which empties your list of allocated blocks. Call it after all static initialization, but before your program starts allocating anything you want to track. First thing in main() should do it. Of course you'll still see leaks for static vectors which have resized, so you might want to reserve them bigger to start with in leak-detection mode. If a static vector is inexorably growing over time, then you probably want to know about that and treat it as a leak even though it's static.

Edit in response to comment:

For example, instead of one central bit of code to destroy all globals at shutdown time:

template<typename T> 
class Reserve {
     Reserve(T &t, typename T::size_type size) {
         t.reserve(size);
     }
};

static std::vector<int> foo;
static Reserve<std::vector<int> > foo_r(foo, 50);

static std:string bar;
static Reserve<std::string> bar_r(bar, 256);

There might be something you can do with a function template, so that type inference gets rid of the need to repeat the type. How about:

template<typename T>
int reserve(T &t, typename T::size_type size) {
    t.reserve(size);
    return 0;
}

static std::vector<int> foo;
static int foo_r = reserve(foo, 50);

static std:string bar;
static int bar_r = reserve(bar, 256);
Steve Jessop
Yeah that seems like a reasonable approach, but I don't think having to manually reserve all my static vectors is much cleaner than doing the in-place delete/new thing on exit
Lucas
True, although code can be placed next to the object being reserved, instead of needing a "magic list of all globals". Clearing the allocation list has other advantages, though. If you aren't trashing your globals then you can dump a useful list of allocations at times other than exit. You can also clear/dump either side of one particular routine instead of the whole program.
Steve Jessop
+1  A: 

Put all of your leak detection code in a another, separate DLL. Then make sure that the code you want to check for leaks depends on the DLL. This lets the loader do the work of ensuring that the "leak checking DLL" gets initialized before everything else and also gets unloaded after everything else. Doing this will ensure that your leak checking code runs after all other code has been unloaded (and destructors called).

This is the technique I used when I created Visual Leak Detector, and it seems to work well.

Dan Moulding
That's a great idea that I hadn't thought of but unfortunately my situation is kind of complicated since there are multiple DLLs in my system each with their own instance of the leak tracker. This would mean the DLL would just get reference counted instead of maintaining separate state
Lucas
The simple solution to that is to maintain the leak checking state for each module (DLL or executable) together (e.g. use an std::map that maps your leak checking state to the module handle that it belongs to).
Dan Moulding