Line data Source code
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 "mozilla/Assertions.h"
11 : #include "mozilla/Atomics.h"
12 : #include "nsIMemoryReporter.h"
13 :
14 : namespace mozilla {
15 :
16 : // This CRTP class handles several details of wrapping allocators and should
17 : // be preferred to manually counting with MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC
18 : // and MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE. The typical use is in a memory
19 : // reporter for a particular third party library:
20 : //
21 : // class MyMemoryReporter : public CountingAllocatorBase<MyMemoryReporter>
22 : // {
23 : // ...
24 : // NS_IMETHOD
25 : // CollectReports(nsIHandleReportCallback* aHandleReport,
26 : // nsISupports* aData, bool aAnonymize) override
27 : // {
28 : // MOZ_COLLECT_REPORT(
29 : // "explicit/path/to/somewhere", KIND_HEAP, UNITS_BYTES,
30 : // MemoryAllocated(),
31 : // "A description of what we are reporting.");
32 : //
33 : // return NS_OK;
34 : // }
35 : // };
36 : //
37 : // ...somewhere later in the code...
38 : // SetThirdPartyMemoryFunctions(MyMemoryReporter::CountingAlloc,
39 : // MyMemoryReporter::CountingFree);
40 : template<typename T>
41 : class CountingAllocatorBase
42 : {
43 : public:
44 6 : CountingAllocatorBase()
45 : {
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 6 : MOZ_ASSERT(!hasRun);
51 6 : hasRun = true;
52 : #endif
53 6 : }
54 :
55 : static size_t
56 0 : MemoryAllocated()
57 : {
58 0 : return sAmount;
59 : }
60 :
61 : static void*
62 82 : CountingMalloc(size_t size)
63 : {
64 82 : void* p = malloc(size);
65 82 : sAmount += MallocSizeOfOnAlloc(p);
66 82 : return p;
67 : }
68 :
69 : static void*
70 0 : CountingCalloc(size_t nmemb, size_t size)
71 : {
72 0 : void* p = calloc(nmemb, size);
73 0 : sAmount += MallocSizeOfOnAlloc(p);
74 0 : return p;
75 : }
76 :
77 : static void*
78 0 : CountingRealloc(void* p, size_t size)
79 : {
80 0 : size_t oldsize = MallocSizeOfOnFree(p);
81 0 : void *pnew = realloc(p, size);
82 0 : if (pnew) {
83 0 : size_t newsize = MallocSizeOfOnAlloc(pnew);
84 0 : sAmount += newsize - oldsize;
85 0 : } else if (size == 0) {
86 : // We asked for a 0-sized (re)allocation of some existing pointer
87 : // and received NULL in return. 0-sized allocations are permitted
88 : // to either return NULL or to allocate a unique object per call (!).
89 : // For a malloc implementation that chooses the second strategy,
90 : // that allocation may fail (unlikely, but possible).
91 : //
92 : // Given a NULL return value and an allocation size of 0, then, we
93 : // don't know if that means the original pointer was freed or if
94 : // the allocation of the unique object failed. If the original
95 : // pointer was freed, then we have nothing to do here. If the
96 : // allocation of the unique object failed, the original pointer is
97 : // still valid and we ought to undo the decrement from above.
98 : // However, we have no way of knowing how the underlying realloc
99 : // implementation is behaving. Assuming that the original pointer
100 : // was freed is the safest course of action. We do, however, need
101 : // to note that we freed memory.
102 0 : sAmount -= oldsize;
103 : } else {
104 : // realloc failed. The amount allocated hasn't changed.
105 : }
106 0 : return pnew;
107 : }
108 :
109 : // Some library code expects that realloc(x, 0) will free x, which is not
110 : // the behavior of the version of jemalloc we're using, so this wrapped
111 : // version of realloc is needed.
112 : static void*
113 : CountingFreeingRealloc(void* p, size_t size)
114 : {
115 : if (size == 0) {
116 : CountingFree(p);
117 : return nullptr;
118 : }
119 : return CountingRealloc(p, size);
120 : }
121 :
122 : static void
123 19 : CountingFree(void* p)
124 : {
125 19 : sAmount -= MallocSizeOfOnFree(p);
126 19 : free(p);
127 19 : }
128 :
129 : private:
130 : // |sAmount| can be (implicitly) accessed by multiple threads, so it
131 : // must be thread-safe.
132 : static Atomic<size_t> sAmount;
133 :
134 82 : MOZ_DEFINE_MALLOC_SIZE_OF_ON_ALLOC(MallocSizeOfOnAlloc)
135 19 : MOZ_DEFINE_MALLOC_SIZE_OF_ON_FREE(MallocSizeOfOnFree)
136 : };
137 :
138 : } // namespace mozilla
139 :
140 : #endif // CountingAllocatorBase_h
|