4#include "Stroika/Foundation/StroikaPreComp.h"
12#include "Stroika/Foundation/Debug/Debugger.h"
13#include "Stroika/Foundation/Execution/Common.h"
14#include "Stroika/Foundation/Execution/Throw.h"
22using namespace Stroika::Foundation::Memory;
34#ifndef qSuperAssertChecks_MemAllocator
35#define qSuperAssertChecks_MemAllocator qStroika_Foundation_Debug_AssertionsChecked
39#if qSuperAssertChecks_MemAllocator
40 inline void SUPER_ASSERT_ (
bool t)
47 Debug::DropIntoDebuggerIfPresent ();
52#define SUPER_ASSERT_(x)
61void* SimpleAllocator_CallLIBCMallocFree::Allocate (
size_t size)
63 void* p = malloc (size);
64 if (p ==
nullptr) [[unlikely]] {
70void SimpleAllocator_CallLIBCMallocFree::Deallocate (
void* p)
81void* SimpleAllocator_CallLIBCNewDelete::Allocate (
size_t size)
83 return ::operator
new (size);
86void SimpleAllocator_CallLIBCNewDelete::Deallocate (
void* p)
89 ::operator
delete (p);
93 const unsigned int kPreGUARD = 0x39;
94 const unsigned int kPost_GUARD = 0x1f;
96 struct alignas (double) MemWithExtraStuff {
97 unsigned int fPreGuard;
107SimpleSizeCountingGeneralPurposeAllocator::SimpleSizeCountingGeneralPurposeAllocator ()
108 : fBaseAllocator_{sDefaultAllocator_}
113 : fBaseAllocator_{baseAllocator}
117SimpleSizeCountingGeneralPurposeAllocator::~SimpleSizeCountingGeneralPurposeAllocator ()
122 Require (fNetAllocationCount_ == 0);
123 Require (fNetAllocatedByteCount_ == 0);
126void* SimpleSizeCountingGeneralPurposeAllocator::Allocate (
size_t size)
128 size_t effectiveSize = size +
sizeof (MemWithExtraStuff) +
sizeof (
unsigned int);
129 MemWithExtraStuff* p =
reinterpret_cast<MemWithExtraStuff*
> (fBaseAllocator_.Allocate (effectiveSize));
130 p->fPreGuard = kPreGUARD;
131 p->fBlockSize = size;
132 (void)::memcpy (
reinterpret_cast<byte*
> (p) + size +
sizeof (MemWithExtraStuff), &kPost_GUARD,
sizeof (kPost_GUARD));
133 fNetAllocationCount_++;
134 fNetAllocatedByteCount_ +=
static_cast<int32_t
> (size);
135 return (
reinterpret_cast<byte*
> (p) +
sizeof (MemWithExtraStuff));
138void SimpleSizeCountingGeneralPurposeAllocator::Deallocate (
void* ptr)
141 MemWithExtraStuff* p =
reinterpret_cast<MemWithExtraStuff*
> (
reinterpret_cast<byte*
> (ptr) -
sizeof (MemWithExtraStuff));
142 SUPER_ASSERT_ (p->fPreGuard == kPreGUARD);
143 SUPER_ASSERT_ (::memcmp (
reinterpret_cast<byte*
> (p) + p->fBlockSize + sizeof (MemWithExtraStuff), &kPost_GUARD,
sizeof (kPost_GUARD)) == 0);
144 --fNetAllocationCount_;
145 fNetAllocatedByteCount_ -= p->fBlockSize;
146 fBaseAllocator_.Deallocate (p);
149size_t SimpleSizeCountingGeneralPurposeAllocator::GetNetAllocationCount ()
const
151 Require (fNetAllocationCount_ >= 0);
152 return static_cast<size_t> (fNetAllocationCount_);
155size_t SimpleSizeCountingGeneralPurposeAllocator::GetNetAllocatedByteCount ()
const
157 Require (fNetAllocatedByteCount_ >= 0);
158 return static_cast<size_t> (fNetAllocatedByteCount_);
167 using PTRMAP = LeakTrackingGeneralPurposeAllocator::PTRMAP;
168 void ExtractInfo_ (
const PTRMAP& m, set<size_t>* sizeSet,
size_t* totalAllocated)
174 for (
auto i = m.begin (); i != m.end (); ++i) {
175 sizeSet->insert (i->second);
176 (*totalAllocated) += i->second;
179 unsigned int ExtractCountUsedForSize_ (
const PTRMAP& m,
size_t eltSize)
181 unsigned int result = 0;
182 for (
auto i = m.begin (); i != m.end (); ++i) {
183 if (i->second == eltSize) {
190LeakTrackingGeneralPurposeAllocator::LeakTrackingGeneralPurposeAllocator ()
191 : fBaseAllocator_{sDefaultAllocator_}
196 : fBaseAllocator_{baseAllocator}
200LeakTrackingGeneralPurposeAllocator::~LeakTrackingGeneralPurposeAllocator ()
206 SUPER_ASSERT_ (fAllocations_.size () == 0);
209void* LeakTrackingGeneralPurposeAllocator::Allocate (
size_t size)
211 void* memptr = fBaseAllocator_.Allocate (size);
213 [[maybe_unused]] lock_guard critSec{fCritSection_};
215 fAllocations_.insert ({memptr, size});
219 fBaseAllocator_.Deallocate (memptr);
224void LeakTrackingGeneralPurposeAllocator::Deallocate (
void* p)
227 [[maybe_unused]] lock_guard critSec{fCritSection_};
228 PTRMAP::iterator i = fAllocations_.find (p);
229 SUPER_ASSERT_ (i != fAllocations_.end ());
230 fAllocations_.erase (i);
231 fBaseAllocator_.Deallocate (p);
234size_t LeakTrackingGeneralPurposeAllocator::GetNetAllocationCount ()
const
236 [[maybe_unused]] lock_guard critSec{fCritSection_};
237 return fAllocations_.size ();
240size_t LeakTrackingGeneralPurposeAllocator::GetNetAllocatedByteCount ()
const
242 [[maybe_unused]] lock_guard critSec{fCritSection_};
244 for (
auto i = fAllocations_.begin (); i != fAllocations_.end (); ++i) {
250LeakTrackingGeneralPurposeAllocator::Snapshot LeakTrackingGeneralPurposeAllocator::GetSnapshot ()
const
252 [[maybe_unused]] lock_guard critSec{fCritSection_};
253 return Snapshot{fAllocations_};
257 void DUMPCurMemStats_ (
const LeakTrackingGeneralPurposeAllocator::Snapshot& curSnapshot,
const LeakTrackingGeneralPurposeAllocator::Snapshot& sinceSnapshot)
260 set<size_t> prevSizes;
261 size_t totalRemainingAlloced = 0;
262 size_t prevTotalRemainingAlloced = 0;
263 ExtractInfo_ (curSnapshot.fAllocations, &sizes, &totalRemainingAlloced);
264 ExtractInfo_ (sinceSnapshot.fAllocations, &prevSizes, &prevTotalRemainingAlloced);
265 DbgTrace (
"Net Allocation Count = {} (prev {})"_f, curSnapshot.fAllocations.size (), sinceSnapshot.fAllocations.size ());
266 DbgTrace (
"Net Allocation byte Count = {} (prev {})"_f, totalRemainingAlloced, prevTotalRemainingAlloced);
267 if (totalRemainingAlloced > prevTotalRemainingAlloced) {
268 DbgTrace (
"Leaked {} bytes"_f, totalRemainingAlloced - prevTotalRemainingAlloced);
270 else if (prevTotalRemainingAlloced > totalRemainingAlloced) {
271 DbgTrace (
"Reverse-Leaked {} bytes"_f, prevTotalRemainingAlloced - totalRemainingAlloced);
276 for (
auto si = sizes.begin (); si != sizes.end (); ++si) {
277 unsigned int cnt = 0;
278 for (
auto i = fAllocMap.begin (); i != fAllocMap.end (); ++i) {
279 if (i->second == *si) {
283 DbgTrace (
"items of size {}, count {}"_f, *si, cnt);
289 set<size_t>::const_iterator psi = prevSizes.begin ();
290 for (
auto si = sizes.begin (); si != sizes.end ();) {
294 if (psi == prevSizes.end ()) {
296 DbgTrace (
"Leak: new size bucket {}"_f, *si);
301 DbgTrace (
"Leak: new size bucket {}"_f, *si);
304 else if (*si == *psi) {
306 unsigned int oldCountThisSize = ExtractCountUsedForSize_ (sinceSnapshot.fAllocations, *si);
307 unsigned int newCountThisSize = ExtractCountUsedForSize_ (curSnapshot.fAllocations, *si);
308 if (oldCountThisSize < newCountThisSize) {
309 DbgTrace (
"Leak: for bucket size {}, oldCount={}, newCount={}"_f, *si, oldCountThisSize, newCountThisSize);
311 else if (oldCountThisSize > newCountThisSize) {
312 DbgTrace (
"Reverse-Leak: for bucket size {}, oldCount={}, newCount={}"_f, *si, oldCountThisSize, newCountThisSize);
318 DbgTrace (
"Reverse-Leak: old size bucket {}"_f, *psi);
323 while (psi != prevSizes.end ()) {
324 DbgTrace (
"Reverse-Leak: old size bucket {}"_f, *psi);
330void LeakTrackingGeneralPurposeAllocator::DUMPCurMemStats (
const Snapshot& sinceSnapshot)
332 Snapshot curSnapshot = GetSnapshot ();
334 DUMPCurMemStats_ (curSnapshot, sinceSnapshot);
337LeakTrackingGeneralPurposeAllocator::Snapshot::Snapshot (
const PTRMAP& m)
#define qStroika_Foundation_Debug_AssertionsChecked
The qStroika_Foundation_Debug_AssertionsChecked flag determines if assertions are checked and validat...
#define RequireNotNull(p)
void Throw(T &&e2Throw)
identical to builtin C++ 'throw' except that it does helpful, type dependent DbgTrace() messages firs...