From d56e4c3cc3fbb3499ea1f5874c523ffc5c3ea720 Mon Sep 17 00:00:00 2001 From: ed Date: Thu, 13 Sep 2018 16:11:52 +0100 Subject: [PATCH] Added a new HeavyweightLeakedObjectDetector to juce_core which will print out a stack trace showing where the leaked object was created and added a corresponding JUCE_HEAVYWEIGHT_LEAK_DETECTOR macro --- modules/juce_core/juce_core.h | 1 + .../juce_HeavyweightLeakedObjectDetector.h | 144 ++++++++++++++++++ 2 files changed, 145 insertions(+) create mode 100644 modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h diff --git a/modules/juce_core/juce_core.h b/modules/juce_core/juce_core.h index f44a9823c2..e02c74ddfb 100644 --- a/modules/juce_core/juce_core.h +++ b/modules/juce_core/juce_core.h @@ -338,6 +338,7 @@ namespace juce #include "zip/juce_ZipFile.h" #include "containers/juce_PropertySet.h" #include "memory/juce_SharedResourcePointer.h" +#include "memory/juce_HeavyweightLeakedObjectDetector.h" #if JUCE_CORE_INCLUDE_OBJC_HELPERS && (JUCE_MAC || JUCE_IOS) #include "native/juce_osx_ObjCHelpers.h" diff --git a/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h b/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h new file mode 100644 index 0000000000..aacf2c5156 --- /dev/null +++ b/modules/juce_core/memory/juce_HeavyweightLeakedObjectDetector.h @@ -0,0 +1,144 @@ +/* + ============================================================================== + + This file is part of the JUCE library. + Copyright (c) 2017 - ROLI Ltd. + + JUCE is an open source library subject to commercial or open-source + licensing. + + The code included in this file is provided under the terms of the ISC license + http://www.isc.org/downloads/software-support-policy/isc-license. Permission + To use, copy, modify, and/or distribute this software for any purpose with or + without fee is hereby granted provided that the above copyright notice and + this permission notice appear in all copies. + + JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER + EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE + DISCLAIMED. + + ============================================================================== +*/ + +namespace juce +{ + +//============================================================================== +/** + This class is a useful way of tracking down hard to find memory leaks when the + regular LeakedObjectDetector isn't enough. + + As well as firing when any instances of the OwnerClass type are leaked, it will + print out a stack trace showing where the leaked object was created. This is obviously + quite a heavyweight task so, unlike the LeakedObjectDetector which should be always + be added to your classes, you should only use this object temporarily when you are + debugging and remove it when finished. + + To use it, use the JUCE_HEAVYWEIGHT_LEAK_DETECTOR macro as a simple way to put + one in your class declaration. + + @tags{Core} +*/ +template +class HeavyweightLeakedObjectDetector +{ +public: + //============================================================================== + HeavyweightLeakedObjectDetector() noexcept { getBacktraceMap().set (this, SystemStats::getStackBacktrace()); } + HeavyweightLeakedObjectDetector (const HeavyweightLeakedObjectDetector&) noexcept { getBacktraceMap().set (this, SystemStats::getStackBacktrace()); } + + ~HeavyweightLeakedObjectDetector() { getBacktraceMap().remove (this); } + +private: + //============================================================================== + typedef HashMap*, String> BacktraceMap; + + //============================================================================== + struct BacktraceMapHolder + { + BacktraceMapHolder() noexcept {} + + ~BacktraceMapHolder() + { + if (map.size() > 0) + { + DBG ("*** Leaked objects detected: " << map.size() << " instance(s) of class " << getLeakedObjectClassName()); + DBG (getFormattedBacktracesString()); + + /** If you hit this, then you've leaked one or more objects of the type specified by + the 'OwnerClass' template parameter - the name and stack trace of its creation should + have been printed by the lines above. + + If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for + your object management. Tut, tut. Always, always use std::unique_ptrs, OwnedArrays, + ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! + */ + jassertfalse; + } + } + + String getFormattedBacktracesString() const + { + String str; + + int counter = 1; + for (typename BacktraceMap::Iterator i (map); i.next();) + { + str << "\nBacktrace " << String (counter++) << "\n" + << "-----------------------------------------------------------------" << "\n" + << i.getValue(); + } + + return str; + } + + BacktraceMap map; + }; + + static BacktraceMap& getBacktraceMap() + { + static BacktraceMapHolder holder; + return holder.map; + } + + static const char* getLeakedObjectClassName() + { + return OwnerClass::getLeakedObjectClassName(); + } +}; + +//============================================================================== +#if DOXYGEN || ! defined (JUCE_HEAVYWEIGHT_LEAK_DETECTOR) + #if (DOXYGEN || JUCE_CHECK_MEMORY_LEAKS) + /** This macro lets you embed a heavyweight leak-detecting object inside a class. + + To use it, simply declare a JUCE_HEAVYWEIGHT_LEAK_DETECTOR (YourClassName) inside a private section + of the class declaration. E.g. + + @code + class MyClass + { + public: + MyClass(); + void blahBlah(); + + private: + JUCE_HEAVYWEIGHT_LEAK_DETECTOR (MyClass) + }; + @endcode + + NB: you should only use this when you really need to track down a tricky memory leak, and + should never leave one of these inside a class! + + @see HeavyweightLeakedObjectDetector, JUCE_LEAK_DETECTOR, LeakedObjectDetector + */ + #define JUCE_HEAVYWEIGHT_LEAK_DETECTOR(OwnerClass) \ + friend class juce::HeavyweightLeakedObjectDetector; \ + static const char* getLeakedObjectClassName() noexcept { return #OwnerClass; } \ + juce::HeavyweightLeakedObjectDetector JUCE_JOIN_MACRO (leakDetector, __LINE__); + #else + #define JUCE_HEAVYWEIGHT_LEAK_DETECTOR(OwnerClass) + #endif +#endif + +} // namespace juce