diff --git a/distrho/extra/d_leakdetector.hpp b/distrho/extra/d_leakdetector.hpp new file mode 100644 index 00000000..7d275559 --- /dev/null +++ b/distrho/extra/d_leakdetector.hpp @@ -0,0 +1,139 @@ +/* + * DISTRHO Plugin Framework (DPF) + * Copyright (C) 2012-2014 Filipe Coelho + * + * 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. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD + * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL + * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER + * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#ifndef DISTRHO_LEAK_DETECTOR_HPP_INCLUDED +#define DISTRHO_LEAK_DETECTOR_HPP_INCLUDED + +#include "../DistrhoUtils.hpp" + +// ----------------------------------------------------------------------- +// The following code was based from juce-core LeakDetector class +// Copyright (C) 2013 Raw Material Software Ltd. + +/** A good old-fashioned C macro concatenation helper. + This combines two items (which may themselves be macros) into a single string, + avoiding the pitfalls of the ## macro operator. +*/ +#define DISTRHO_JOIN_MACRO_HELPER(a, b) a ## b +#define DISTRHO_JOIN_MACRO(item1, item2) DISTRHO_JOIN_MACRO_HELPER(item1, item2) + +/** This macro lets you embed a leak-detecting object inside a class.\n + To use it, simply declare a DISTRHO_LEAK_DETECTOR(YourClassName) inside a private section + of the class declaration. E.g. + \code + class MyClass + { + public: + MyClass(); + void blahBlah(); + + private: + DISTRHO_LEAK_DETECTOR(MyClass) + }; + \endcode +*/ +#define DISTRHO_LEAK_DETECTOR(ClassName) \ + friend class ::LeakedObjectDetector; \ + static const char* getLeakedObjectClassName() noexcept { return #ClassName; } \ + ::LeakedObjectDetector DISTRHO_JOIN_MACRO(leakDetector, __LINE__); + +#define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \ + DISTRHO_DECLARE_NON_COPY_CLASS(ClassName) \ + DISTRHO_LEAK_DETECTOR(ClassName) + +//============================================================================== +/** + Embedding an instance of this class inside another class can be used as a low-overhead + way of detecting leaked instances. + + This class keeps an internal static count of the number of instances that are + active, so that when the app is shutdown and the static destructors are called, + it can check whether there are any left-over instances that may have been leaked. + + To use it, use the DISTRHO_LEAK_DETECTOR macro as a simple way to put one in your + class declaration. +*/ +template +class LeakedObjectDetector +{ +public: + //============================================================================== + LeakedObjectDetector() noexcept { ++(getCounter().numObjects); } + LeakedObjectDetector(const LeakedObjectDetector&) noexcept { ++(getCounter().numObjects); } + + ~LeakedObjectDetector() noexcept + { + if (--(getCounter().numObjects) < 0) + { + /** If you hit this, then you've managed to delete more instances of this class than you've + created.. That indicates that you're deleting some dangling pointers. + + Note that although this assertion will have been triggered during a destructor, it might + not be this particular deletion that's at fault - the incorrect one may have happened + at an earlier point in the program, and simply not been detected until now. + + Most errors like this are caused by using old-fashioned, non-RAII techniques for + your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays, + ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! + */ + d_stderr2("*** Dangling pointer deletion! Class: '%s', Count: %i", getLeakedObjectClassName(), getCounter().numObjects); + } + } + +private: + //============================================================================== + class LeakCounter + { + public: + LeakCounter() noexcept + { + numObjects = 0; + } + + ~LeakCounter() noexcept + { + if (numObjects > 0) + { + /** If you hit this, then you've leaked one or more objects of the type specified by + the 'OwnerClass' template parameter - the name should have been printed by the line 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 ScopedPointers, OwnedArrays, + ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs! + */ + d_stderr2("*** Leaked objects detected: %i instance(s) of class '%s'", numObjects, getLeakedObjectClassName()); + } + } + + // this should be an atomic... + volatile int numObjects; + }; + + static const char* getLeakedObjectClassName() noexcept + { + return OwnerClass::getLeakedObjectClassName(); + } + + static LeakCounter& getCounter() noexcept + { + static LeakCounter counter; + return counter; + } +}; + +// ----------------------------------------------------------------------- + +#endif // DISTRHO_LEAK_DETECTOR_HPP_INCLUDED