diff --git a/src/common/engine/stats.h b/src/common/engine/stats.h index 0794455d2..2d855bc82 100644 --- a/src/common/engine/stats.h +++ b/src/common/engine/stats.h @@ -221,6 +221,11 @@ public: Counter = 0; } + void ResetAndClock() + { + Counter = -static_cast(rdtsc()); + } + void Clock() { int64_t time = rdtsc(); diff --git a/src/common/objects/dobjgc.cpp b/src/common/objects/dobjgc.cpp index 8664ac541..8a0f753f1 100644 --- a/src/common/objects/dobjgc.cpp +++ b/src/common/objects/dobjgc.cpp @@ -82,23 +82,58 @@ ** infinity, where each step performs a full collection.) You can also ** change this value dynamically. */ -#define DEFAULT_GCMUL 200 // GC runs 'double the speed' of memory allocation +#ifndef _DEBUG +#define DEFAULT_GCMUL 600 // GC runs gcmul% the speed of memory allocation +#else +// Higher in debug builds to account for the extra time spent freeing objects +#define DEFAULT_GCMUL 800 +#endif // Minimum step size -#define GCSTEPSIZE (sizeof(DObject) * 16) +#define GCMINSTEPSIZE (sizeof(DObject) * 16) -// Maximum number of elements to sweep in a single step -#define GCSWEEPMAX 40 +// Sweeps traverse objects in chunks of this size +#define GCSWEEPGRANULARITY 40 -// Cost of sweeping one element (the size of a small object divided by -// some adjust for the sweep speed) -#define GCSWEEPCOST (sizeof(DObject) / 4) +// Cost of deleting an object +#ifndef _DEBUG +#define GCDELETECOST 75 +#else +// Freeing memory is much more costly in debug builds +#define GCDELETECOST 230 +#endif -// Cost of calling of one destructor -#define GCFINALIZECOST 100 +// Cost of destroying an object +#define GCDESTROYCOST 15 // TYPES ------------------------------------------------------------------- +class FAveragizer +{ + // Number of allocations to track + static inline constexpr unsigned HistorySize = 512; + + size_t History[HistorySize]; + size_t TotalAmount; + int TotalCount; + unsigned NewestPos; + +public: + FAveragizer(); + void AddAlloc(size_t alloc); + size_t GetAverage(); +}; + +struct FStepStats +{ + cycle_t Clock[GC::GCS_COUNT]; + size_t BytesCovered[GC::GCS_COUNT]; + int Count[GC::GCS_COUNT]; + + void Format(FString &out); + void Reset(); +}; + // EXTERNAL FUNCTION PROTOTYPES -------------------------------------------- // PUBLIC FUNCTION PROTOTYPES ---------------------------------------------- @@ -114,28 +149,50 @@ static size_t CalcStepSize(); namespace GC { size_t AllocBytes; +size_t RunningAllocBytes; +size_t RunningDeallocBytes; size_t Threshold; size_t Estimate; DObject *Gray; DObject *Root; DObject *SoftRoots; DObject **SweepPos; +DObject *ToDestroy; uint32_t CurrentWhite = OF_White0 | OF_Fixed; EGCState State = GCS_Pause; int Pause = DEFAULT_GCPAUSE; int StepMul = DEFAULT_GCMUL; -int StepCount; -uint64_t CheckTime; +FStepStats StepStats; +FStepStats PrevStepStats; bool FinalGC; +bool HadToDestroy; // PRIVATE DATA DEFINITIONS ------------------------------------------------ -static int LastCollectTime; // Time last time collector finished -static size_t LastCollectAlloc; // Memory allocation when collector finished -static size_t MinStepSize; // Cover at least this much memory per step +static FAveragizer AllocHistory;// Tracks allocation rate over time +static cycle_t GCTime; // Track time spent in GC // CODE -------------------------------------------------------------------- +//========================================================================== +// +// CheckGC +// +// Check if it's time to collect, and do a collection step if it is. +// Also does some bookkeeping. Should be called fairly consistantly. +// +//========================================================================== + +void CheckGC() +{ + AllocHistory.AddAlloc(RunningAllocBytes); + RunningAllocBytes = 0; + if (State > GCS_Pause || AllocBytes >= Threshold) + { + Step(); + } +} + //========================================================================== // // SetThreshold @@ -146,7 +203,7 @@ static size_t MinStepSize; // Cover at least this much memory per step void SetThreshold() { - Threshold = (Estimate / 100) * Pause; + Threshold = (std::min(Estimate, AllocBytes) / 100) * Pause; } //========================================================================== @@ -170,55 +227,72 @@ size_t PropagateMark() //========================================================================== // -// SweepList +// SweepObjects // -// Runs a limited sweep on a list, returning the position in the list just -// after the last object swept. +// Runs a limited sweep on the object list, returning the number of bytes +// swept. // //========================================================================== -static DObject **SweepList(DObject **p, size_t count, size_t *finalize_count) +static size_t SweepObjects(size_t count) { DObject *curr; int deadmask = OtherWhite(); - size_t finalized = 0; + size_t swept = 0; - while ((curr = *p) != NULL && count-- > 0) + while ((curr = *SweepPos) != nullptr && count-- > 0) { + swept += curr->GetClass()->Size; if ((curr->ObjectFlags ^ OF_WhiteBits) & deadmask) // not dead? { assert(!curr->IsDead() || (curr->ObjectFlags & OF_Fixed)); curr->MakeWhite(); // make it white (for next cycle) - p = &curr->ObjNext; + SweepPos = &curr->ObjNext; } - else // must erase 'curr' + else { assert(curr->IsDead()); - *p = curr->ObjNext; if (!(curr->ObjectFlags & OF_EuthanizeMe)) - { // The object must be destroyed before it can be finalized. - // Note that thinkers must already have been destroyed. If they get here without - // having been destroyed first, it means they somehow became unattached from the - // thinker lists. If I don't maintain the invariant that all live thinkers must - // be in a thinker list, then I need to add write barriers for every time a - // thinker pointer is changed. This seems easier and perfectly reasonable, since - // a live thinker that isn't on a thinker list isn't much of a thinker. - - // However, this can happen during deletion of the thinker list while cleaning up - // from a savegame error so we can't assume that any thinker that gets here is an error. - - curr->Destroy(); + { // The object must be destroyed before it can be deleted. + curr->GCNext = ToDestroy; + ToDestroy = curr; + SweepPos = &curr->ObjNext; + } + else + { // must erase 'curr' + *SweepPos = curr->ObjNext; + curr->ObjectFlags |= OF_Cleanup; + delete curr; + swept += GCDELETECOST; } - curr->ObjectFlags |= OF_Cleanup; - delete curr; - finalized++; } } - if (finalize_count != NULL) + return swept; +} + +//========================================================================== +// +// DestroyObjects +// +// Destroys up to count objects on a list linked on GCNext, returning the +// size of objects destroyed, for updating the estimate. +// +//========================================================================== + +static size_t DestroyObjects(size_t count) +{ + DObject *curr; + size_t bytes_destroyed = 0; + + while ((curr = ToDestroy) != nullptr && count-- > 0) { - *finalize_count = finalized; + assert(!(curr->ObjectFlags & OF_EuthanizeMe)); + bytes_destroyed += curr->GetClass()->Size + GCDESTROYCOST; + ToDestroy = curr->GCNext; + curr->GCNext = nullptr; + curr->Destroy(); } - return p; + return bytes_destroyed; } //========================================================================== @@ -269,20 +343,14 @@ void MarkArray(DObject **obj, size_t count) // // CalcStepSize // -// Decide how big a step should be based, depending on how long it took to -// allocate up to the threshold from the amount left after the previous -// collection. +// Decide how big a step should be, based on the current allocation rate. // //========================================================================== static size_t CalcStepSize() { - int time_passed = int(CheckTime - LastCollectTime); - auto alloc = min(LastCollectAlloc, Estimate); - size_t bytes_gained = AllocBytes > alloc ? AllocBytes - alloc : 0; - return (StepMul > 0 && time_passed > 0) - ? std::max(GCSTEPSIZE, bytes_gained / time_passed * StepMul / 100) - : std::numeric_limits::max() / 2; // no limit + size_t avg = AllocHistory.GetAverage(); + return std::max(GCMINSTEPSIZE, avg * StepMul / 100); } //========================================================================== @@ -302,15 +370,18 @@ void AddMarkerFunc(GCMarkerFunc func) static void MarkRoot() { - Gray = NULL; + PrevStepStats = StepStats; + StepStats.Reset(); + + Gray = nullptr; for (auto func : markers) func(); // Mark soft roots. - if (SoftRoots != NULL) + if (SoftRoots != nullptr) { DObject **probe = &SoftRoots->ObjNext; - while (*probe != NULL) + while (*probe != nullptr) { DObject *soft = *probe; probe = &soft->ObjNext; @@ -322,7 +393,6 @@ static void MarkRoot() } // Time to propagate the marks. State = GCS_Propagate; - StepCount = 0; } //========================================================================== @@ -341,10 +411,21 @@ static void Atomic() SweepPos = &Root; State = GCS_Sweep; Estimate = AllocBytes; +} - // Now that we are about to start a sweep, establish a baseline minimum - // step size for how much memory we want to sweep each CheckGC(). - MinStepSize = CalcStepSize(); +//========================================================================== +// +// SweepDone +// +// Sets up the Destroy phase, if there are any dead objects that haven't +// been destroyed yet, or skips to the Done state. +// +//========================================================================== + +static void SweepDone() +{ + HadToDestroy = ToDestroy != nullptr; + State = HadToDestroy ? GCS_Destroy : GCS_Done; } //========================================================================== @@ -364,7 +445,7 @@ static size_t SingleStep() return 0; case GCS_Propagate: - if (Gray != NULL) + if (Gray != nullptr) { return PropagateMark(); } @@ -375,22 +456,30 @@ static size_t SingleStep() } case GCS_Sweep: { - size_t old = AllocBytes; - size_t finalize_count; - SweepPos = SweepList(SweepPos, GCSWEEPMAX, &finalize_count); - if (*SweepPos == NULL) + RunningDeallocBytes = 0; + size_t swept = SweepObjects(GCSWEEPGRANULARITY); + Estimate -= RunningDeallocBytes; + if (*SweepPos == nullptr) { // Nothing more to sweep? - State = GCS_Finalize; + SweepDone(); } - //assert(old >= AllocBytes); - Estimate -= max(0, old - AllocBytes); - return (GCSWEEPMAX - finalize_count) * GCSWEEPCOST + finalize_count * GCFINALIZECOST; + return swept; } - case GCS_Finalize: + case GCS_Destroy: { + size_t destroy_size; + destroy_size = DestroyObjects(GCSWEEPGRANULARITY); + Estimate -= destroy_size; + if (ToDestroy == nullptr) + { // Nothing more to destroy? + State = GCS_Done; + } + return destroy_size; + } + + case GCS_Done: State = GCS_Pause; // end collection - LastCollectAlloc = AllocBytes; - LastCollectTime = (int)CheckTime; + SetThreshold(); return 0; default: @@ -403,21 +492,27 @@ static size_t SingleStep() // // Step // -// Performs enough single steps to cover GCSTEPSIZE * StepMul% bytes of -// memory. +// Performs enough single steps to cover bytes of memory. +// Some of those bytes might be "fake" to account for the cost of freeing +// or destroying object. // //========================================================================== void Step() { - // We recalculate a step size in case the rate of allocation went up - // since we started sweeping because we don't want to fall behind. - // However, we also don't want to go slower than what was decided upon - // when the sweep began if the rate of allocation has slowed. - size_t lim = max(CalcStepSize(), MinStepSize); + GCTime.ResetAndClock(); + + auto enter_state = State; + StepStats.Count[enter_state]++; + StepStats.Clock[enter_state].Clock(); + + size_t did = 0; + size_t lim = CalcStepSize(); + do { size_t done = SingleStep(); + did += done; if (done < lim) { lim -= done; @@ -426,17 +521,23 @@ void Step() { lim = 0; } + if (State != enter_state) + { + // Finish stats on old state + StepStats.Clock[enter_state].Unclock(); + StepStats.BytesCovered[enter_state] += did; + + // Start stats on new state + did = 0; + enter_state = State; + StepStats.Clock[enter_state].Clock(); + StepStats.Count[enter_state]++; + } } while (lim && State != GCS_Pause); - if (State != GCS_Pause) - { - Threshold = AllocBytes; - } - else - { - assert(AllocBytes >= Estimate); - SetThreshold(); - } - StepCount++; + + StepStats.Clock[enter_state].Unclock(); + StepStats.BytesCovered[enter_state] += did; + GCTime.Unclock(); } //========================================================================== @@ -454,20 +555,23 @@ void FullGC() // Reset sweep mark to sweep all elements (returning them to white) SweepPos = &Root; // Reset other collector lists - Gray = NULL; + Gray = nullptr; State = GCS_Sweep; } - // Finish any pending sweep phase - while (State != GCS_Finalize) - { - SingleStep(); - } - MarkRoot(); + // Finish any pending GC stages while (State != GCS_Pause) { SingleStep(); } - SetThreshold(); + // Loop until everything that can be destroyed and freed is + do + { + MarkRoot(); + while (State != GCS_Pause) + { + SingleStep(); + } + } while (HadToDestroy); } //========================================================================== @@ -481,9 +585,9 @@ void FullGC() void Barrier(DObject *pointing, DObject *pointed) { - assert(pointing == NULL || (pointing->IsBlack() && !pointing->IsDead())); + assert(pointing == nullptr || (pointing->IsBlack() && !pointing->IsDead())); assert(pointed->IsWhite() && !pointed->IsDead()); - assert(State != GCS_Finalize && State != GCS_Pause); + assert(State != GCS_Destroy && State != GCS_Pause); assert(!(pointed->ObjectFlags & OF_Released)); // if a released object gets here, something must be wrong. if (pointed->ObjectFlags & OF_Released) return; // don't do anything with non-GC'd objects. // The invariant only needs to be maintained in the propagate state. @@ -495,7 +599,7 @@ void Barrier(DObject *pointing, DObject *pointed) } // In other states, we can mark the pointing object white so this // barrier won't be triggered again, saving a few cycles in the future. - else if (pointing != NULL) + else if (pointing != nullptr) { pointing->MakeWhite(); } @@ -503,13 +607,13 @@ void Barrier(DObject *pointing, DObject *pointed) void DelSoftRootHead() { - if (SoftRoots != NULL) + if (SoftRoots != nullptr) { // Don't let the destructor print a warning message SoftRoots->ObjectFlags |= OF_YesReallyDelete; delete SoftRoots; } - SoftRoots = NULL; + SoftRoots = nullptr; } //========================================================================== @@ -526,7 +630,7 @@ void AddSoftRoot(DObject *obj) DObject **probe; // Are there any soft roots yet? - if (SoftRoots == NULL) + if (SoftRoots == nullptr) { // Create a new object to root the soft roots off of, and stick // it at the end of the object list, so we know that anything @@ -534,17 +638,17 @@ void AddSoftRoot(DObject *obj) SoftRoots = Create(); SoftRoots->ObjectFlags |= OF_Fixed; probe = &Root; - while (*probe != NULL) + while (*probe != nullptr) { probe = &(*probe)->ObjNext; } Root = SoftRoots->ObjNext; - SoftRoots->ObjNext = NULL; + SoftRoots->ObjNext = nullptr; *probe = SoftRoots; } // Mark this object as rooted and move it after the SoftRoots marker. probe = &Root; - while (*probe != NULL && *probe != obj) + while (*probe != nullptr && *probe != obj) { probe = &(*probe)->ObjNext; } @@ -567,14 +671,14 @@ void DelSoftRoot(DObject *obj) { DObject **probe; - if (!(obj->ObjectFlags & OF_Rooted)) + if (obj == nullptr || !(obj->ObjectFlags & OF_Rooted)) { // Not rooted, so nothing to do. return; } obj->ObjectFlags &= ~OF_Rooted; // Move object out of the soft roots part of the list. probe = &SoftRoots; - while (*probe != NULL && *probe != obj) + while (*probe != nullptr && *probe != obj) { probe = &(*probe)->ObjNext; } @@ -588,6 +692,52 @@ void DelSoftRoot(DObject *obj) } +//========================================================================== +// +// FAveragizer - Constructor +// +//========================================================================== + +FAveragizer::FAveragizer() +{ + NewestPos = 0; + TotalAmount = 0; + TotalCount = 0; + memset(History, 0, sizeof(History)); +} + +//========================================================================== +// +// FAveragizer :: AddAlloc +// +//========================================================================== + +void FAveragizer::AddAlloc(size_t alloc) +{ + NewestPos = (NewestPos + 1) & (HistorySize - 1); + if (TotalCount < HistorySize) + { + TotalCount++; + } + else + { + TotalAmount -= History[NewestPos]; + } + History[NewestPos] = alloc; + TotalAmount += alloc; +} + +//========================================================================== +// +// FAveragizer :: GetAverage +// +//========================================================================== + +size_t FAveragizer::GetAverage() +{ + return TotalCount != 0 ? TotalAmount / TotalCount : 0; +} + //========================================================================== // // STAT gc @@ -602,18 +752,66 @@ ADD_STAT(gc) " Pause ", "Propagate", " Sweep ", - "Finalize " }; + " Destroy ", + " Done " + }; FString out; - out.Format("[%s] Alloc:%6zuK Thresh:%6zuK Est:%6zuK Steps: %d %zuK", + double time = GC::State != GC::GCS_Pause ? GC::GCTime.TimeMS() : 0; + + GC::PrevStepStats.Format(out); + out << "\n"; + GC::StepStats.Format(out); + out.AppendFormat("\n%.2fms [%s] Rate:%3zuK (%3zuK) Alloc:%6zuK Est:%6zuK Thresh:%6zuK", + time, StateStrings[GC::State], + (GC::AllocHistory.GetAverage() + 1023) >> 10, + (GC::CalcStepSize() + 1023) >> 10, (GC::AllocBytes + 1023) >> 10, - (GC::Threshold + 1023) >> 10, (GC::Estimate + 1023) >> 10, - GC::StepCount, - (GC::MinStepSize + 1023) >> 10); + (GC::Threshold + 1023) >> 10); return out; } +//========================================================================== +// +// FStepStats :: Reset +// +//========================================================================== + +void FStepStats::Reset() +{ + for (int i = 0; i < countof(Count); ++i) + { + Count[i] = 0; + BytesCovered[i] = 0; + Clock[i].Reset(); + } +} + +//========================================================================== +// +// FStepStats :: Format +// +// Appends its stats to the given FString. +// +//========================================================================== + +void FStepStats::Format(FString &out) +{ + // Because everything in the default green is hard to distinguish, + // each stage has its own color. + for (int i = GC::GCS_Propagate; i < GC::GCS_Done; ++i) + { + int count = Count[i]; + double time = Clock[i].TimeMS(); + out.AppendFormat(TEXTCOLOR_ESCAPESTR "%c[%c%6zuK %4d*%.2fms]", + "-NKB"[i], /* Color codes */ + "-PSD"[i], /* Stage prefixes: (P)ropagate, (S)weep, (D)estroy */ + (BytesCovered[i] + 1023) >> 10, count, count != 0 ? time / count : time); + } + out << TEXTCOLOR_GREEN; +} + //========================================================================== // // CCMD gc diff --git a/src/common/objects/dobjgc.h b/src/common/objects/dobjgc.h index 7997145b7..a844a16db 100644 --- a/src/common/objects/dobjgc.h +++ b/src/common/objects/dobjgc.h @@ -37,12 +37,21 @@ namespace GC GCS_Pause, GCS_Propagate, GCS_Sweep, - GCS_Finalize + GCS_Destroy, + GCS_Done, + + GCS_COUNT }; // Number of bytes currently allocated through M_Malloc/M_Realloc. extern size_t AllocBytes; + // Number of bytes allocated since last collection step. + extern size_t RunningAllocBytes; + + // Number of bytes freed since last collection step. + extern size_t RunningDeallocBytes; + // Amount of memory to allocate before triggering a collection. extern size_t Threshold; @@ -70,18 +79,12 @@ namespace GC // Is this the final collection just before exit? extern bool FinalGC; - // Counts the number of times CheckGC has been called. - extern uint64_t CheckTime; - // Current white value for known-dead objects. static inline uint32_t OtherWhite() { return CurrentWhite ^ OF_WhiteBits; } - // Frees all objects, whether they're dead or not. - void FreeAll(); - // Does one collection step. void Step(); @@ -118,12 +121,7 @@ namespace GC } // Check if it's time to collect, and do a collection step if it is. - static inline void CheckGC() - { - CheckTime++; - if (AllocBytes >= Threshold) - Step(); - } + void CheckGC(); // Forces a collection to start now. static inline void StartCollection() @@ -176,6 +174,32 @@ namespace GC using GCMarkerFunc = void(*)(); void AddMarkerFunc(GCMarkerFunc func); + // Report an allocation to the GC + static inline void ReportAlloc(size_t alloc) + { + AllocBytes += alloc; + RunningAllocBytes += alloc; + } + + // Report a deallocation to the GC + static inline void ReportDealloc(size_t dealloc) + { + AllocBytes -= dealloc; + RunningDeallocBytes += dealloc; + } + + // Report a reallocation to the GC + static inline void ReportRealloc(size_t oldsize, size_t newsize) + { + if (oldsize < newsize) + { + ReportAlloc(newsize - oldsize); + } + else + { + ReportDealloc(oldsize - newsize); + } + } } // A template class to help with handling read barriers. It does not diff --git a/src/common/utility/m_alloc.cpp b/src/common/utility/m_alloc.cpp index 5c2ea25b9..3b7e12f43 100644 --- a/src/common/utility/m_alloc.cpp +++ b/src/common/utility/m_alloc.cpp @@ -3,7 +3,7 @@ ** Wrappers for the malloc family of functions that count used bytes. ** **--------------------------------------------------------------------------- -** Copyright 1998-2008 Randy Heit +** Copyright 1998-2008 Marisa Heit ** All rights reserved. ** ** Redistribution and use in source and binary forms, with or without @@ -45,7 +45,7 @@ #endif #include "engineerrors.h" -#include "dobject.h" +#include "dobjgc.h" #ifndef _MSC_VER #define _NORMAL_BLOCK 0 @@ -59,25 +59,22 @@ void *M_Malloc(size_t size) { void *block = malloc(size); - if (block == NULL) + if (block == nullptr) I_FatalError("Could not malloc %zu bytes", size); - GC::AllocBytes += _msize(block); + GC::ReportAlloc(_msize(block)); return block; } void *M_Realloc(void *memblock, size_t size) { - if (memblock != NULL) - { - GC::AllocBytes -= _msize(memblock); - } + size_t oldsize = memblock ? _msize(memblock) : 0; void *block = realloc(memblock, size); - if (block == NULL) + if (block == nullptr) { I_FatalError("Could not realloc %zu bytes", size); } - GC::AllocBytes += _msize(block); + GC::ReportRealloc(oldsize, _msize(block)); return block; } #else @@ -85,28 +82,25 @@ void *M_Malloc(size_t size) { void *block = malloc(size+sizeof(size_t)); - if (block == NULL) + if (block == nullptr) I_FatalError("Could not malloc %zu bytes", size); size_t *sizeStore = (size_t *) block; *sizeStore = size; block = sizeStore+1; - GC::AllocBytes += _msize(block); + GC::ReportAlloc(_msize(block)); return block; } void *M_Realloc(void *memblock, size_t size) { - if(memblock == NULL) + if (memblock == nullptr) return M_Malloc(size); - if (memblock != NULL) - { - GC::AllocBytes -= _msize(memblock); - } + size_t oldsize = _msize(memblock); void *block = realloc(((size_t*) memblock)-1, size+sizeof(size_t)); - if (block == NULL) + if (block == nullptr) { I_FatalError("Could not realloc %zu bytes", size); } @@ -115,7 +109,7 @@ void *M_Realloc(void *memblock, size_t size) *sizeStore = size; block = sizeStore+1; - GC::AllocBytes += _msize(block); + GC::ReportRealloc(oldsize, _msize(block)); return block; } #endif @@ -129,25 +123,22 @@ void *M_Malloc_Dbg(size_t size, const char *file, int lineno) { void *block = _malloc_dbg(size, _NORMAL_BLOCK, file, lineno); - if (block == NULL) + if (block == nullptr) I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno); - GC::AllocBytes += _msize(block); + GC::ReportAlloc(_msize(block)); return block; } void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno) { - if (memblock != NULL) - { - GC::AllocBytes -= _msize(memblock); - } + size_t oldsize = memblock ? _msize(memblock) : 0; void *block = _realloc_dbg(memblock, size, _NORMAL_BLOCK, file, lineno); - if (block == NULL) + if (block == nullptr) { I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno); } - GC::AllocBytes += _msize(block); + GC::ReportRealloc(oldsize, _msize(block)); return block; } #else @@ -155,29 +146,26 @@ void *M_Malloc_Dbg(size_t size, const char *file, int lineno) { void *block = _malloc_dbg(size+sizeof(size_t), _NORMAL_BLOCK, file, lineno); - if (block == NULL) + if (block == nullptr) I_FatalError("Could not malloc %zu bytes in %s, line %d", size, file, lineno); size_t *sizeStore = (size_t *) block; *sizeStore = size; block = sizeStore+1; - GC::AllocBytes += _msize(block); + GC::ReportAlloc(_msize(block)); return block; } void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno) { - if(memblock == NULL) + if (memblock == nullptr) return M_Malloc_Dbg(size, file, lineno); - if (memblock != NULL) - { - GC::AllocBytes -= _msize(memblock); - } + size_t oldsize = _msize(memblock); void *block = _realloc_dbg(((size_t*) memblock)-1, size+sizeof(size_t), _NORMAL_BLOCK, file, lineno); - if (block == NULL) + if (block == nullptr) { I_FatalError("Could not realloc %zu bytes in %s, line %d", size, file, lineno); } @@ -186,29 +174,22 @@ void *M_Realloc_Dbg(void *memblock, size_t size, const char *file, int lineno) *sizeStore = size; block = sizeStore+1; - GC::AllocBytes += _msize(block); + GC::ReportRealloc(oldsize, _msize(block)); return block; } #endif #endif +void M_Free (void *block) +{ + if (block != nullptr) + { + GC::ReportDealloc(_msize(block)); #if !defined(__solaris__) && !defined(__OpenBSD__) && !defined(__DragonFly__) -void M_Free (void *block) -{ - if (block != NULL) - { - GC::AllocBytes -= _msize(block); free(block); - } -} #else -void M_Free (void *block) -{ - if(block != NULL) - { - GC::AllocBytes -= _msize(block); free(((size_t*) block)-1); +#endif } } -#endif