Memory Leak Tool


#1

I had a memory leak driving me insane last night.  The JUCE_LEAK_DETECTOR was firing but I couldn't work out where it was coming from.  

Anyway.  Solved now, but thought this might be very useful for someone else.  So here's a little class you can pop in a member variable to any object that's showing up as leaking, and then it gives you the stack trace showing where the leaked objects were created. 

It's based on the JUCE_LEAK_DETECTOR but prioritising information over performance...

/** Stick an instance of this in a class that JUCE is reporting memory leaks for
and you'll get a nice report with a stack trace which should lead you to a
happy place quite quickly.
It might slow down your program a bit, so don't forget to take it out once your
problems are all fixed. In fact. I've wrapped it in a #ifdef JUCE_DEBUG so you
can't forget. Forgetting would be bad.
If the JUCE_LEAK_DETECTOR assert fires first, just push the continue button
on your debugger and this should report straight afterwards.
*/
#ifdef JUCE_DEBUG
class AdvancedLeakDetector {
public:
AdvancedLeakDetector() {
getBackTraceHash().set((void *)this, SystemStats::getStackBacktrace());
}
~AdvancedLeakDetector() {
getBackTraceHash().remove((void *) this);
}
private:
typedef HashMap<void*, String> BackTraceHash;
struct HashHolder {
~HashHolder() {
if (traces.size()>0)
{
/* Memory leak info. */
DBG("Found " + String(traces.size()) + " possible leaks");
for (BackTraceHash::Iterator i (traces); i.next();)
{
DBG("-----");
DBG (i.getValue());
}
jassertfalse;
}
}
BackTraceHash traces;
};
BackTraceHash & getBackTraceHash() {
static HashHolder holder;
return holder.traces;
}
};
#endif

#2

Ah - that's a nice idea. And a good use for SystemStats::getStackBacktrace!


#3

You wouldn't believe how quickly I found my problem.  

I thought about having it as a #define on the existing JUCE_LEAK_DETECTOR but I think peformance will drop to a number close to zero...


#4

@bazrush Thanks a lot!

Here is a formatted and non-html-escaped version of the snippet (probably this broke during forum migration):

/** Stick an instance of this in a class that JUCE is reporting memory leaks for
and you'll get a nice report with a stack trace which should lead you to a
happy place quite quickly.
It might slow down your program a bit, so don't forget to take it out once your
problems are all fixed. In fact. I've wrapped it in a #ifdef JUCE_DEBUG so you
can't forget. Forgetting would be bad.
If the JUCE_LEAK_DETECTOR assert fires first, just push the continue button
on your debugger and this should report straight afterwards.
*/
#ifdef JUCE_DEBUG
class AdvancedLeakDetector {
public:

    AdvancedLeakDetector () {
        getBackTraceHash ().set ((void *)this, SystemStats::getStackBacktrace ());
    }

    ~AdvancedLeakDetector() {
        getBackTraceHash ().remove ((void *) this);
    }

private:

    typedef HashMap<void*, String> BackTraceHash;

    struct HashHolder {
        ~HashHolder () {
            if (traces.size () > 0)
            {
                /* Memory leak info. */
                DBG ("Found " + String (traces.size ()) + " possible leaks");

                for (BackTraceHash::Iterator i (traces); i.next ();)
                {
                    DBG ("-----");
                    DBG (i.getValue ());
                }

                jassertfalse;
            }
        }
        BackTraceHash traces;
    };

    BackTraceHash& getBackTraceHash () {
        static HashHolder holder;
        return holder.traces;
    }

};
#endif

#5

Very neat! :slight_smile:


#6

integrating this in the juce LeakDetector and enable a global macro to switch it on would be extremely profitable. you get the report of the standard juce LeakDetector, if you cannot understand what’s going on you enable this extra reporting macro in Projucer, rebuild, run again, and everything should be more clear :slight_smile:


#7

I think enabling it globally would be too expensive in performance, but I like the idea of being able to add it to a class that you’re struggling to debug.


#8

yes, something like

#define JUCE_ADVANCED_LEAK_DETECTOR_FOR_CLASS MyLeakingClass

#9

Yeah - if memory serves me correctly you definitely don’t want to enable this for all the classes at once. It was slow enough on the one class (although definitely valuable).