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/. */
7 #ifndef CountingAllocatorBase_h
8 #define CountingAllocatorBase_h
11 #include "mozilla/Assertions.h"
12 #include "mozilla/Atomics.h"
13 #include "mozilla/mozalloc.h"
14 #include "nsIMemoryReporter.h"
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:
23 // class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter>
27 // CollectReports(nsIHandleReportCallback* aHandleReport,
28 // nsISupports* aData, bool aAnonymize) override
30 // MOZ_COLLECT_REPORT(
31 // "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES,
33 // "A description of what we are reporting.");
39 // ...somewhere later in the code...
40 // SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc,
41 // MyMemoryReporter::CountingFree);
43 class CountingAllocatorBase
{
45 CountingAllocatorBase() {
47 // There must be only one instance of this class, due to |sAmount| being
49 static bool hasRun
= false;
55 static size_t MemoryAllocated() { return sAmount
; }
57 static void* CountingMalloc(size_t size
) {
58 void* p
= malloc(size
);
59 sAmount
+= MallocSizeOfOnAlloc(p
);
63 static void* CountingCalloc(size_t nmemb
, size_t size
) {
64 void* p
= calloc(nmemb
, size
);
65 sAmount
+= MallocSizeOfOnAlloc(p
);
69 static void* CountingRealloc(void* p
, size_t size
) {
70 size_t oldsize
= MallocSizeOfOnFree(p
);
71 void* pnew
= realloc(p
, size
);
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).
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.
94 // realloc failed. The amount allocated hasn't changed.
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.
102 static void* CountingFreeingRealloc(void* p
, size_t size
) {
107 return CountingRealloc(p
, size
);
110 static void CountingFree(void* p
) {
111 sAmount
-= MallocSizeOfOnFree(p
);
115 // Infallible-allocation wrappers for the counting malloc/calloc/realloc
116 // functions, for clients that don't safely handle allocation failures
118 static void* InfallibleCountingMalloc(size_t size
) {
119 void* p
= moz_xmalloc(size
);
120 sAmount
+= MallocSizeOfOnAlloc(p
);
124 static void* InfallibleCountingCalloc(size_t nmemb
, size_t size
) {
125 void* p
= moz_xcalloc(nmemb
, size
);
126 sAmount
+= MallocSizeOfOnAlloc(p
);
130 static void* InfallibleCountingRealloc(void* p
, size_t size
) {
131 size_t oldsize
= MallocSizeOfOnFree(p
);
132 void* pnew
= moz_xrealloc(p
, size
);
134 size_t newsize
= MallocSizeOfOnAlloc(pnew
);
135 sAmount
+= newsize
- oldsize
;
136 } else if (size
== 0) {
137 // See comment in CountingRealloc above.
140 // realloc failed. The amount allocated hasn't changed.
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
149 typedef Atomic
<size_t, SequentiallyConsistent
> AmountType
;
150 static inline AmountType sAmount
{0};
152 MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc
)
153 MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree
)
156 } // namespace mozilla
158 #endif // CountingAllocatorBase_h