1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this 5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #ifndef CountingAllocatorBase_h 8 #define CountingAllocatorBase_h 9 10 #include <cstdlib> 11 #include "mozilla/Assertions.h" 12 #include "mozilla/Atomics.h" 13 #include "mozilla/mozalloc.h" 14 #include "nsIMemoryReporter.h" 15 16 namespace mozilla { 17 18 // This CRTP class handles several details of wrapping allocators and should 19 // be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC 20 // and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory 21 // reporter for a particular third party library: 22 // 23 // class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter> 24 // { 25 // ... 26 // NS_IMETHOD 27 // CollectReports(nsIHandleReportCallback* aHandleReport, 28 // nsISupports* aData, bool aAnonymize) override 29 // { 30 // MOZ_COLLECT_REPORT( 31 // "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES, 32 // MemoryAllocated(), 33 // "A description of what we are reporting."); 34 // 35 // return NS_OK; 36 // } 37 // }; 38 // 39 // ...somewhere later in the code... 40 // SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc, 41 // MyMemoryReporter::CountingFree); 42 template <typename T> 43 class CountingAllocatorBase { 44 public: CountingAllocatorBase()45 CountingAllocatorBase() { 46 #ifdef DEBUG 47 // There must be only one instance of this class, due to |sAmount| being 48 // static. 49 static bool hasRun = false; 50 MOZ_ASSERT(!hasRun); 51 hasRun = true; 52 #endif 53 } 54 MemoryAllocated()55 static size_t MemoryAllocated() { return sAmount; } 56 CountingMalloc(size_t size)57 static void* CountingMalloc(size_t size) { 58 void* p = malloc(size); 59 sAmount += MallocSizeOfOnAlloc(p); 60 return p; 61 } 62 CountingCalloc(size_t nmemb,size_t size)63 static void* CountingCalloc(size_t nmemb, size_t size) { 64 void* p = calloc(nmemb, size); 65 sAmount += MallocSizeOfOnAlloc(p); 66 return p; 67 } 68 CountingRealloc(void * p,size_t size)69 static void* CountingRealloc(void* p, size_t size) { 70 size_t oldsize = MallocSizeOfOnFree(p); 71 void* pnew = realloc(p, size); 72 if (pnew) { 73 size_t newsize = MallocSizeOfOnAlloc(pnew); 74 sAmount += newsize - oldsize; 75 } else if (size == 0) { 76 // We asked for a 0-sized (re)allocation of some existing pointer 77 // and received NULL in return. 0-sized allocations are permitted 78 // to either return NULL or to allocate a unique object per call (!). 79 // For a malloc implementation that chooses the second strategy, 80 // that allocation may fail (unlikely, but possible). 81 // 82 // Given a NULL return value and an allocation size of 0, then, we 83 // don't know if that means the original pointer was freed or if 84 // the allocation of the unique object failed. If the original 85 // pointer was freed, then we have nothing to do here. If the 86 // allocation of the unique object failed, the original pointer is 87 // still valid and we ought to undo the decrement from above. 88 // However, we have no way of knowing how the underlying realloc 89 // implementation is behaving. Assuming that the original pointer 90 // was freed is the safest course of action. We do, however, need 91 // to note that we freed memory. 92 sAmount -= oldsize; 93 } else { 94 // realloc failed. The amount allocated hasn't changed. 95 } 96 return pnew; 97 } 98 99 // Some library code expects that realloc(x, 0) will free x, which is not 100 // the behavior of the version of jemalloc we're using, so this wrapped 101 // version of realloc is needed. CountingFreeingRealloc(void * p,size_t size)102 static void* CountingFreeingRealloc(void* p, size_t size) { 103 if (size == 0) { 104 CountingFree(p); 105 return nullptr; 106 } 107 return CountingRealloc(p, size); 108 } 109 CountingFree(void * p)110 static void CountingFree(void* p) { 111 sAmount -= MallocSizeOfOnFree(p); 112 free(p); 113 } 114 115 // Infallible-allocation wrappers for the counting malloc/calloc/realloc 116 // functions, for clients that don't safely handle allocation failures 117 // themselves. InfallibleCountingMalloc(size_t size)118 static void* InfallibleCountingMalloc(size_t size) { 119 void* p = moz_xmalloc(size); 120 sAmount += MallocSizeOfOnAlloc(p); 121 return p; 122 } 123 InfallibleCountingCalloc(size_t nmemb,size_t size)124 static void* InfallibleCountingCalloc(size_t nmemb, size_t size) { 125 void* p = moz_xcalloc(nmemb, size); 126 sAmount += MallocSizeOfOnAlloc(p); 127 return p; 128 } 129 InfallibleCountingRealloc(void * p,size_t size)130 static void* InfallibleCountingRealloc(void* p, size_t size) { 131 size_t oldsize = MallocSizeOfOnFree(p); 132 void* pnew = moz_xrealloc(p, size); 133 if (pnew) { 134 size_t newsize = MallocSizeOfOnAlloc(pnew); 135 sAmount += newsize - oldsize; 136 } else if (size == 0) { 137 // See comment in CountingRealloc above. 138 sAmount -= oldsize; 139 } else { 140 // realloc failed. The amount allocated hasn't changed. 141 } 142 return pnew; 143 } 144 145 private: 146 // |sAmount| can be (implicitly) accessed by multiple threads, so it 147 // must be thread-safe. It may be written during GC, so accesses are not 148 // recorded. 149 typedef Atomic<size_t, SequentiallyConsistent> AmountType; 150 static AmountType sAmount; 151 152 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc) 153 MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree) 154 }; 155 156 } // namespace mozilla 157 158 #endif // CountingAllocatorBase_h 159