DPF

DISTRHO Plugin Framework
Log | Files | Refs | Submodules | README | LICENSE

LeakDetector.hpp (6938B)


      1 /*
      2  * DISTRHO Plugin Framework (DPF)
      3  * Copyright (C) 2012-2023 Filipe Coelho <falktx@falktx.com>
      4  *
      5  * Permission to use, copy, modify, and/or distribute this software for any purpose with
      6  * or without fee is hereby granted, provided that the above copyright notice and this
      7  * permission notice appear in all copies.
      8  *
      9  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD
     10  * TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN
     11  * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL
     12  * DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
     13  * IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
     14  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     15  */
     16 
     17 #ifndef DISTRHO_LEAK_DETECTOR_HPP_INCLUDED
     18 #define DISTRHO_LEAK_DETECTOR_HPP_INCLUDED
     19 
     20 #include "../DistrhoUtils.hpp"
     21 
     22 START_NAMESPACE_DISTRHO
     23 
     24 // -----------------------------------------------------------------------
     25 // The following code was based from juce-core LeakDetector class
     26 
     27 /**
     28    Copyright (C) 2013 Raw Material Software Ltd.
     29 
     30    Permission is granted to use this software under the terms of the ISC license
     31    http://www.isc.org/downloads/software-support-policy/isc-license/
     32 
     33    Permission to use, copy, modify, and/or distribute this software for any
     34    purpose with or without fee is hereby granted, provided that the above
     35    copyright notice and this permission notice appear in all copies.
     36 
     37    THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH REGARD
     38    TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
     39    FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT,
     40    OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF
     41    USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER
     42    TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE
     43    OF THIS SOFTWARE.
     44 */
     45 
     46 /** A good old-fashioned C macro concatenation helper.
     47    This combines two items (which may themselves be macros) into a single string,
     48    avoiding the pitfalls of the ## macro operator.
     49 */
     50 #define DISTRHO_JOIN_MACRO_HELPER(a, b) a ## b
     51 #define DISTRHO_JOIN_MACRO(item1, item2) DISTRHO_JOIN_MACRO_HELPER(item1, item2)
     52 
     53 #if defined(DPF_DEBUG) && !defined(NDEBUG)
     54 /** This macro lets you embed a leak-detecting object inside a class.\n
     55     To use it, simply declare a DISTRHO_LEAK_DETECTOR(YourClassName) inside a private section
     56     of the class declaration. E.g.
     57     \code
     58     class MyClass
     59     {
     60     public:
     61         MyClass();
     62         void blahBlah();
     63 
     64     private:
     65         DISTRHO_LEAK_DETECTOR(MyClass)
     66     };
     67     \endcode
     68 */
     69 # define DISTRHO_LEAK_DETECTOR(ClassName)                                         \
     70     friend class DISTRHO_NAMESPACE::LeakedObjectDetector<ClassName>;              \
     71     static const char* getLeakedObjectClassName() noexcept { return #ClassName; } \
     72     DISTRHO_NAMESPACE::LeakedObjectDetector<ClassName> DISTRHO_JOIN_MACRO(leakDetector_, ClassName);
     73 
     74 # define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \
     75     DISTRHO_DECLARE_NON_COPYABLE(ClassName)                       \
     76     DISTRHO_LEAK_DETECTOR(ClassName)
     77 #else
     78 /** Don't use leak detection on release builds. */
     79 # define DISTRHO_LEAK_DETECTOR(ClassName)
     80 # define DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(ClassName) \
     81     DISTRHO_DECLARE_NON_COPYABLE(ClassName)
     82 #endif
     83 
     84 //==============================================================================
     85 /**
     86     Embedding an instance of this class inside another class can be used as a low-overhead
     87     way of detecting leaked instances.
     88 
     89     This class keeps an internal static count of the number of instances that are
     90     active, so that when the app is shutdown and the static destructors are called,
     91     it can check whether there are any left-over instances that may have been leaked.
     92 
     93     To use it, use the DISTRHO_LEAK_DETECTOR macro as a simple way to put one in your
     94     class declaration.
     95 */
     96 template<class OwnerClass>
     97 class LeakedObjectDetector
     98 {
     99 public:
    100     //==============================================================================
    101     LeakedObjectDetector() noexcept                            { ++(getCounter().numObjects); }
    102     LeakedObjectDetector(const LeakedObjectDetector&) noexcept { ++(getCounter().numObjects); }
    103 
    104     ~LeakedObjectDetector() noexcept
    105     {
    106         if (--(getCounter().numObjects) < 0)
    107         {
    108             /** If you hit this, then you've managed to delete more instances of this class than you've
    109                 created.. That indicates that you're deleting some dangling pointers.
    110 
    111                 Note that although this assertion will have been triggered during a destructor, it might
    112                 not be this particular deletion that's at fault - the incorrect one may have happened
    113                 at an earlier point in the program, and simply not been detected until now.
    114 
    115                 Most errors like this are caused by using old-fashioned, non-RAII techniques for
    116                 your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays,
    117                 ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs!
    118             */
    119             d_stderr2("*** Dangling pointer deletion! Class: '%s', Count: %i", getLeakedObjectClassName(), getCounter().numObjects);
    120         }
    121     }
    122 
    123 private:
    124     //==============================================================================
    125     class LeakCounter
    126     {
    127     public:
    128         LeakCounter() noexcept
    129             : numObjects(0) {}
    130 
    131         ~LeakCounter() noexcept
    132         {
    133             if (numObjects > 0)
    134             {
    135                 /** If you hit this, then you've leaked one or more objects of the type specified by
    136                     the 'OwnerClass' template parameter - the name should have been printed by the line above.
    137 
    138                     If you're leaking, it's probably because you're using old-fashioned, non-RAII techniques for
    139                     your object management. Tut, tut. Always, always use ScopedPointers, OwnedArrays,
    140                     ReferenceCountedObjects, etc, and avoid the 'delete' operator at all costs!
    141                 */
    142                 d_stderr2("*** Leaked objects detected: %i instance(s) of class '%s'", numObjects, getLeakedObjectClassName());
    143             }
    144         }
    145 
    146         // this should be an atomic...
    147         volatile int numObjects;
    148     };
    149 
    150     static const char* getLeakedObjectClassName() noexcept
    151     {
    152         return OwnerClass::getLeakedObjectClassName();
    153     }
    154 
    155     static LeakCounter& getCounter() noexcept
    156     {
    157         static LeakCounter counter;
    158         return counter;
    159     }
    160 };
    161 
    162 // -----------------------------------------------------------------------
    163 
    164 END_NAMESPACE_DISTRHO
    165 
    166 #endif // DISTRHO_LEAK_DETECTOR_HPP_INCLUDED