mirror of
https://bitbucket.org/theswgsource/src-1.2.git
synced 2026-10-19 09:02:53 -04:00
2177 lines
61 KiB
C++
Executable File
2177 lines
61 KiB
C++
Executable File
// ======================================================================
|
|
//
|
|
// MemoryManager.cpp
|
|
// Portions copyright 1998 Bootprint Entertainment
|
|
// Portions copyright 2002 Sony Online Entertainment
|
|
// All Rights Reserved
|
|
//
|
|
// ======================================================================
|
|
|
|
#include "sharedMemoryManager/FirstSharedMemoryManager.h"
|
|
#include "sharedMemoryManager/MemoryManager.h"
|
|
#include "sharedMemoryManager/OsMemory.h"
|
|
|
|
#include "sharedFoundation/ConfigSharedFoundation.h"
|
|
#include "sharedFoundation/Production.h"
|
|
#include "sharedDebug/DebugFlags.h"
|
|
#include "sharedDebug/DebugMonitor.h"
|
|
#include "sharedDebug/PixCounter.h"
|
|
#include "sharedDebug/Profiler.h"
|
|
#include "sharedSynchronization/RecursiveMutex.h"
|
|
#include "sharedDebug/RemoteDebug.h"
|
|
|
|
#include <cstdio>
|
|
|
|
#ifdef _WIN32
|
|
#include <io.h>
|
|
#include <crtdbg.h>
|
|
#else
|
|
#include <sys/mman.h>
|
|
#endif
|
|
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
#include <fcntl.h>
|
|
#include <new>
|
|
|
|
// the below seems to bare minimum cause the DB process to segfault
|
|
// too bad, if we weren't using an omnibus mem manager we could potentially maximize usage
|
|
#define DISABLE_MEMORY_MANAGER 1
|
|
|
|
//lint -e826 // Suspicious pointer-to-pointer conversion (area too small)
|
|
|
|
#if !DISABLE_MEMORY_MANAGER
|
|
|
|
// ======================================================================
|
|
|
|
// this flag is different than above, as it doesn't fully disable the manager, only a few pieces
|
|
// including the destructor for some reason
|
|
#define DISABLED 0
|
|
|
|
// removed all the debug cases for these as these seem to cause problems
|
|
// recent modifications force the mem manager to always behave in production mode
|
|
// other areas will remain unaffected
|
|
#define DO_TRACK 0
|
|
#define DO_SCALAR 0
|
|
#define DO_GUARDS 0
|
|
#define DO_INITIALIZE_FILLS 0
|
|
#define DO_FREE_FILLS 0
|
|
|
|
// ======================================================================
|
|
|
|
namespace MemoryManagerNamespace
|
|
{
|
|
class Block;
|
|
|
|
class SystemAllocation
|
|
{
|
|
public:
|
|
|
|
SystemAllocation(int size);
|
|
~SystemAllocation();
|
|
|
|
int getSize();
|
|
|
|
SystemAllocation * getNext();
|
|
void setNext(SystemAllocation *);
|
|
|
|
Block * getFirstMemoryBlock();
|
|
Block * getLastMemoryBlock();
|
|
|
|
private:
|
|
|
|
int m_size;
|
|
SystemAllocation * m_next;
|
|
int m_pad1;
|
|
int m_pad2;
|
|
};
|
|
|
|
|
|
class Block
|
|
{
|
|
public:
|
|
|
|
enum { cms_requestedSizeBits = 30 };
|
|
|
|
public:
|
|
|
|
Block * getPrevious();
|
|
Block const * getPrevious() const;
|
|
void setPrevious(Block *previous);
|
|
|
|
Block * getNext();
|
|
Block const * getNext() const;
|
|
void setNext(Block *previous);
|
|
|
|
bool isFree() const;
|
|
void setFree(bool free);
|
|
|
|
int getSize() const;
|
|
|
|
private:
|
|
|
|
Block * m_previous;
|
|
Block * m_next;
|
|
bool m_free:1;
|
|
|
|
protected:
|
|
|
|
// these are only in allocated blocks, but we can't expect a compiler to merge bitfields across multiple classes
|
|
#if DO_SCALAR
|
|
bool m_array:1;
|
|
#endif
|
|
#if DO_TRACK
|
|
bool m_leakTest:0;
|
|
#endif
|
|
#if DO_TRACK || DO_GUARDS
|
|
unsigned int m_requestedSize:cms_requestedSizeBits;
|
|
#endif
|
|
};
|
|
|
|
class FreeBlock;
|
|
|
|
void addToFreeList(Block * block);
|
|
void removeFromFreeList(FreeBlock * block);
|
|
FreeBlock *searchFreeList(int blockSize);
|
|
|
|
class FreeBlock : public Block
|
|
{
|
|
private:
|
|
|
|
friend void addToFreeList(Block *block);
|
|
friend void removeFromFreeList(FreeBlock *block);
|
|
friend FreeBlock *searchFreeList(int blockSize);
|
|
|
|
private:
|
|
FreeBlock * m_smallerFreeBlock;
|
|
FreeBlock * m_sameFreeBlock;
|
|
FreeBlock * m_largerFreeBlock;
|
|
FreeBlock * m_parentFreeBlock;
|
|
};
|
|
|
|
class AllocatedBlock : public Block
|
|
{
|
|
public:
|
|
|
|
#if DO_SCALAR
|
|
bool isAllocatedAsArray() const;
|
|
void setAllocatedAsArray(bool array);
|
|
#endif
|
|
|
|
#if DO_TRACK
|
|
bool checkForLeaks() const;
|
|
void setCheckForLeaks(bool checkForLeaks);
|
|
|
|
uint32 getOwner(int index) const;
|
|
void setOwner(int index, uint32 owner);
|
|
void fillOwnerWithFreePattern();
|
|
|
|
#endif
|
|
|
|
#if DO_TRACK || DO_GUARDS
|
|
int getRequestedSize() const;
|
|
void setRequestedSize(int requestedSize);
|
|
#endif
|
|
|
|
private:
|
|
|
|
#if DO_TRACK
|
|
uint32 m_owner[DO_TRACK];
|
|
#endif
|
|
};
|
|
|
|
#ifdef _DEBUG
|
|
void logAllocationsNextFrame();
|
|
#endif
|
|
|
|
int convertBytesToMegabytesForSystemAllocation(int bytes);
|
|
void allocateSystemMemory(int megabytes);
|
|
|
|
int min(int a, int b);
|
|
int max(int a, int b);
|
|
byte const * min(byte const * a, byte const * b);
|
|
byte const * max(byte const * a, byte const * b);
|
|
|
|
int report(bool leak);
|
|
void report(AllocatedBlock const * block, bool leak);
|
|
|
|
int quickGuessIsValidPointerFromHeap(void const *);
|
|
int authoritativeIsValidPointerFromHeap(void const *);
|
|
|
|
void emitCharacters(char * & buffer, int blockSize, int & carryOverFree, int & carryOverUsed, int newFree, int newUsed, char const * const bufferOverrunAddress);
|
|
|
|
bool reportToFile(char const *fileName, bool leak);
|
|
void outputDebugStringWrapper(char const * message);
|
|
void logMessageToFd(char const * message);
|
|
|
|
int const cms_blockSize = (sizeof(Block) + 15) & (~15);
|
|
int const cms_freeBlockSize = (sizeof(FreeBlock) + 15) & (~15);
|
|
extern int const cms_allocatedBlockSize = (sizeof(AllocatedBlock) + 15) & (~15);
|
|
byte const cms_guardFillPattern = 0xAB;
|
|
byte const cms_initializeFillPattern = 0xCD;
|
|
byte const cms_freeFillPattern = 0xEF;
|
|
int const cms_guardBandSize = DO_GUARDS ? 16 : 0;
|
|
|
|
int const cms_systemAllocationRoundSize = 4 * 1024 * 1024;
|
|
int const cms_systemAllocationMinimumSize = 4 * 1024 * 1024;
|
|
|
|
bool ms_installed;
|
|
bool ms_limitSet;
|
|
bool ms_hardLimit;
|
|
int ms_limitMegabytes = 3071;
|
|
SystemAllocation * ms_firstSystemAllocation;
|
|
int ms_numberOfSystemAllocations;
|
|
int ms_systemMemoryAllocatedMegabytes;
|
|
bool ms_reportAllocations;
|
|
|
|
#ifdef _DEBUG
|
|
PixCounter::ResetInteger ms_allocationsPerFrame;
|
|
PixCounter::ResetInteger ms_bytesAllocatedPerFrame;
|
|
PixCounter::ResetInteger ms_freesPerFrame;
|
|
PixCounter::ResetInteger ms_bytesFreedPerFrame;
|
|
|
|
bool ms_debugReportFlag;
|
|
bool ms_debugReportMapFlag;
|
|
bool ms_debugReportAllocations;
|
|
bool ms_debugReportLogMemoryAllocFreePointers;
|
|
bool ms_debugLogAllocations;
|
|
bool ms_debugLogAllocationsNextFrame;
|
|
bool ms_debugVerifyGuardPatterns;
|
|
bool ms_debugVerifyFreePatterns;
|
|
bool ms_debugProfileAllocate;
|
|
bool ms_debugLogAllocationsNextFrameStarted;
|
|
#endif
|
|
|
|
RecursiveMutex * ms_criticalSection;
|
|
|
|
char ms_memoryManagerBuffer[sizeof(MemoryManager)];
|
|
|
|
FreeBlock * ms_firstFreeBlock;
|
|
|
|
int ms_allocateCalls;
|
|
unsigned long ms_allocateBytesTotal;
|
|
#ifndef _WIN32
|
|
int ms_processVmSizeKBytes;
|
|
#endif
|
|
int ms_freeCalls;
|
|
|
|
int ms_allocations;
|
|
int ms_maxAllocations;
|
|
|
|
int ms_freeBlocks;
|
|
int ms_maxFreeBlocks;
|
|
#ifdef _DEBUG
|
|
int ms_maxTreeHeight;
|
|
#endif
|
|
|
|
#if DO_TRACK || DO_GUARDS
|
|
unsigned long ms_currentBytesRequested;
|
|
#endif
|
|
unsigned long ms_currentBytesAllocated;
|
|
#if DO_TRACK
|
|
unsigned long ms_currentBytesAllocatedNoLeakTest;
|
|
#endif
|
|
unsigned long ms_maxBytesAllocated;
|
|
|
|
bool ms_allowNameLookup = true;
|
|
int ms_logMessageFd = -1;
|
|
void (*LogMessage)(char const * message) = &outputDebugStringWrapper;
|
|
|
|
PROFILER_BLOCK_DEFINE(ms_allocateProfilerBlock, "MemoryManager::allocate");
|
|
}
|
|
using namespace MemoryManagerNamespace;
|
|
|
|
// ======================================================================
|
|
|
|
SystemAllocation::SystemAllocation(int size)
|
|
:
|
|
m_size(size),
|
|
m_next(nullptr),
|
|
m_pad1(0),
|
|
m_pad2(0)
|
|
{
|
|
DEBUG_FATAL(sizeof(*this) != cms_blockSize, ("SystemAllocation size is not %d bytes", cms_blockSize));
|
|
|
|
#if DO_FREE_FILLS
|
|
// fill the newly allocated user memory with the free pattern
|
|
imemset(reinterpret_cast<byte*>(this) + cms_blockSize, cms_freeFillPattern, m_size - cms_blockSize);
|
|
#endif
|
|
|
|
Block * firstMemoryBlock = getFirstMemoryBlock();
|
|
Block * firstFreeBlock = reinterpret_cast<Block *>(reinterpret_cast<byte *>(firstMemoryBlock) + cms_blockSize);
|
|
Block * lastMemoryBlock = getLastMemoryBlock();
|
|
|
|
// set up the prefix sentinel block
|
|
firstMemoryBlock->setPrevious(nullptr);
|
|
firstMemoryBlock->setNext(firstFreeBlock);
|
|
firstMemoryBlock->setFree(false);
|
|
|
|
firstFreeBlock->setPrevious(firstMemoryBlock);
|
|
firstFreeBlock->setNext(lastMemoryBlock);
|
|
firstFreeBlock->setFree(true);
|
|
|
|
// set up the suffix sentinel block
|
|
lastMemoryBlock->setPrevious(firstFreeBlock);
|
|
lastMemoryBlock->setNext(nullptr);
|
|
lastMemoryBlock->setFree(false);
|
|
|
|
// put the first block on the free list
|
|
addToFreeList(firstFreeBlock);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
SystemAllocation::~SystemAllocation()
|
|
{
|
|
OsMemory::free(this, m_size);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int SystemAllocation::getSize()
|
|
{
|
|
return m_size;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
SystemAllocation * SystemAllocation::getNext()
|
|
{
|
|
return m_next;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void SystemAllocation::setNext(SystemAllocation * next)
|
|
{
|
|
m_next = next;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Block * SystemAllocation ::getFirstMemoryBlock()
|
|
{
|
|
return reinterpret_cast<Block *>(reinterpret_cast<byte *>(this) + cms_blockSize);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
Block * SystemAllocation ::getLastMemoryBlock()
|
|
{
|
|
return reinterpret_cast<Block *>(reinterpret_cast<byte *>(this) + m_size - cms_blockSize);
|
|
}
|
|
|
|
// ======================================================================
|
|
|
|
inline Block * Block::getPrevious()
|
|
{
|
|
return m_previous;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline Block const * Block::getPrevious() const
|
|
{
|
|
return m_previous;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline void Block::setPrevious(Block *previous)
|
|
{
|
|
m_previous = previous;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline Block * Block::getNext()
|
|
{
|
|
return m_next;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline Block const * Block::getNext() const
|
|
{
|
|
return m_next;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline void Block::setNext(Block *next)
|
|
{
|
|
DEBUG_FATAL(next && reinterpret_cast<int>(next) - reinterpret_cast<int>(this) < cms_blockSize, ("too small"));
|
|
m_next = next;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline bool Block::isFree() const
|
|
{
|
|
return m_free;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline void Block::setFree(bool free)
|
|
{
|
|
m_free = free;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline int Block::getSize() const
|
|
{
|
|
return reinterpret_cast<byte const *>(m_next) - reinterpret_cast<byte const *>(this);
|
|
}
|
|
|
|
// ======================================================================
|
|
|
|
#if DO_SCALAR
|
|
|
|
inline bool AllocatedBlock::isAllocatedAsArray() const
|
|
{
|
|
return m_array;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline void AllocatedBlock::setAllocatedAsArray(bool array)
|
|
{
|
|
m_array = array;
|
|
}
|
|
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
#if DO_TRACK
|
|
|
|
inline bool AllocatedBlock::checkForLeaks() const
|
|
{
|
|
return m_leakTest;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline void AllocatedBlock::setCheckForLeaks(bool checkForLeaks)
|
|
{
|
|
m_leakTest = checkForLeaks;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline uint32 AllocatedBlock::getOwner(int index) const
|
|
{
|
|
return m_owner[index];
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline void AllocatedBlock::setOwner(int index, uint32 owner)
|
|
{
|
|
m_owner[index] = owner;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline void AllocatedBlock::fillOwnerWithFreePattern()
|
|
{
|
|
memset(m_owner, cms_freeFillPattern, sizeof(m_owner));
|
|
}
|
|
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
#if DO_TRACK || DO_GUARDS
|
|
|
|
inline int AllocatedBlock::getRequestedSize() const
|
|
{
|
|
return m_requestedSize;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline void AllocatedBlock::setRequestedSize(int requestedSize)
|
|
{
|
|
m_requestedSize = requestedSize;
|
|
}
|
|
|
|
#endif
|
|
|
|
// ======================================================================
|
|
|
|
inline int MemoryManagerNamespace::min(int a, int b)
|
|
{
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline int MemoryManagerNamespace::max(int a, int b)
|
|
{
|
|
return (a > b) ? a : b;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline byte const * MemoryManagerNamespace::min(byte const * a, byte const * b)
|
|
{
|
|
return (a < b) ? a : b;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
inline byte const * MemoryManagerNamespace::max(byte const * a, byte const * b)
|
|
{
|
|
return (a > b) ? a : b;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
// this is here to get around linkage problems on win32
|
|
|
|
void MemoryManagerNamespace::outputDebugStringWrapper(char const * message)
|
|
{
|
|
OutputDebugString(message);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::setLimit(int megabytes, bool hardLimit, bool preallocate)
|
|
{
|
|
DEBUG_FATAL(ms_limitSet, ("MemoryManager::setLimit may only be called once"));
|
|
ms_limitSet = true;
|
|
ms_limitMegabytes = megabytes;
|
|
ms_hardLimit = hardLimit;
|
|
DEBUG_FATAL(ms_hardLimit && ms_systemMemoryAllocatedMegabytes > ms_limitMegabytes, ("hard limit broken before being set %d/%d", ms_systemMemoryAllocatedMegabytes, ms_limitMegabytes));
|
|
if (preallocate && ms_systemMemoryAllocatedMegabytes < ms_limitMegabytes)
|
|
allocateSystemMemory(ms_limitMegabytes - ms_systemMemoryAllocatedMegabytes);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManager::getLimit()
|
|
{
|
|
return ms_limitMegabytes;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
bool MemoryManager::isHardLimit()
|
|
{
|
|
return ms_hardLimit;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Install the MemoryManager.
|
|
* @param newMaxBytes Maximum number of bytes in the memory pool
|
|
* @param newMaxBlocks Number of blocks (free or allocated)
|
|
* @param commitAll Commit all the specified ram to the application
|
|
*/
|
|
|
|
MemoryManager::MemoryManager()
|
|
{
|
|
// any allocation will install the memory manager if it has yet to be, so we want to bail safely here
|
|
if (ms_installed)
|
|
return;
|
|
|
|
#if defined(PLATFORM_WIN32) && defined(_DEBUG)
|
|
/*
|
|
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
|
|
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW);
|
|
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW);
|
|
int crtHeapFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
|
crtHeapFlags &= ~_CRTDBG_ALLOC_MEM_DF;
|
|
crtHeapFlags &= ~_CRTDBG_CHECK_ALWAYS_DF;
|
|
crtHeapFlags &= ~_CRTDBG_CHECK_CRT_DF;
|
|
crtHeapFlags &= ~_CRTDBG_DELAY_FREE_MEM_DF;
|
|
crtHeapFlags &= ~_CRTDBG_LEAK_CHECK_DF;
|
|
_CrtSetDbgFlag(crtHeapFlags);
|
|
*/
|
|
#endif
|
|
|
|
OsMemory::install();
|
|
DebugHelp::install();
|
|
|
|
#if !DISABLED
|
|
|
|
// this buffer must remain local to this function, otherwise it will get clobbered.
|
|
// the problem is this function gets called before the library gets properly initialized,
|
|
// and then when the library gets initialized, it will clear the global memory, thus
|
|
// wiping out our mutex.
|
|
static char s_criticalSectionBuffer[sizeof(RecursiveMutex)];
|
|
ms_criticalSection = new(s_criticalSectionBuffer) RecursiveMutex;
|
|
|
|
ms_reportAllocations = true;
|
|
ms_installed = true;
|
|
// ms_logEachAlloc = false;
|
|
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Remove the MemoryManager.
|
|
*/
|
|
|
|
MemoryManager::~MemoryManager()
|
|
{
|
|
#if DISABLED
|
|
return;
|
|
#else
|
|
|
|
DEBUG_FATAL(!ms_installed, ("not installed"));
|
|
|
|
ms_criticalSection->enter();
|
|
|
|
DEBUG_REPORT_LOG_PRINT(true, ("MM::remove %lu/%lu=bytes %d/%d=allocs\n", getCurrentNumberOfBytesAllocated(), getMaximumNumberOfBytesAllocated(), getCurrentNumberOfAllocations(), getMaximumNumberOfAllocations()));
|
|
DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("MM::remove %lu/%lu=bytes %d/%d=allocs\n", getCurrentNumberOfBytesAllocated(), getMaximumNumberOfBytesAllocated(), getCurrentNumberOfAllocations(), getMaximumNumberOfAllocations()));
|
|
|
|
#if DO_TRACK
|
|
if (!ConfigSharedFoundation::getDemoMode() && ms_allocations && ms_reportAllocations)
|
|
{
|
|
ms_criticalSection->leave();
|
|
int const leaks = MemoryManagerNamespace::report(true);
|
|
ms_criticalSection->enter();
|
|
if (leaks)
|
|
REPORT(true, Report::RF_log | Report::RF_dialog, ("%d memory leak%s", leaks, leaks > 1 ? "s" : ""));
|
|
}
|
|
#endif
|
|
|
|
// free all of the allocations from the OS
|
|
while (ms_firstSystemAllocation)
|
|
{
|
|
SystemAllocation * systemAllocation = ms_firstSystemAllocation;
|
|
ms_firstSystemAllocation = ms_firstSystemAllocation->getNext();
|
|
systemAllocation->~SystemAllocation();
|
|
}
|
|
|
|
ms_criticalSection->leave();
|
|
ms_installed = false;
|
|
|
|
#ifdef _DEBUG
|
|
DebugMonitor::remove();
|
|
#endif
|
|
|
|
ms_criticalSection->~RecursiveMutex();
|
|
|
|
#endif
|
|
|
|
DebugHelp::remove();
|
|
OsMemory::remove();
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManagerNamespace::convertBytesToMegabytesForSystemAllocation(int systemAllocationSize)
|
|
{
|
|
if (systemAllocationSize < cms_systemAllocationMinimumSize)
|
|
systemAllocationSize = cms_systemAllocationMinimumSize;
|
|
|
|
systemAllocationSize = (systemAllocationSize + (cms_systemAllocationRoundSize - 1)) & (~(cms_systemAllocationRoundSize - 1));
|
|
|
|
return systemAllocationSize / (1024 * 1024);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManagerNamespace::allocateSystemMemory(int megabytes)
|
|
{
|
|
if (ms_hardLimit && ms_systemMemoryAllocatedMegabytes + megabytes > ms_limitMegabytes)
|
|
{
|
|
megabytes = ms_limitMegabytes - ms_systemMemoryAllocatedMegabytes;
|
|
if (megabytes <= 0)
|
|
return;
|
|
}
|
|
|
|
// allocate the system memory
|
|
size_t systemAllocationSize = static_cast<size_t>(megabytes) * 1024 * 1024;
|
|
void * data = OsMemory::commit(0, systemAllocationSize);
|
|
|
|
// failed to allocate virtual memory. there is insufficient virtual memory or address space to satisfy systemAllocationSize.
|
|
if (!data)
|
|
return;
|
|
|
|
// Construct our tracking information
|
|
SystemAllocation * systemAllocation = new(data) SystemAllocation(systemAllocationSize);
|
|
++ms_numberOfSystemAllocations;
|
|
ms_systemMemoryAllocatedMegabytes += megabytes;
|
|
|
|
// insert the memory into the sorted linked list of system allocations
|
|
SystemAllocation * back = nullptr;
|
|
SystemAllocation * front = ms_firstSystemAllocation;
|
|
for ( ; front && front->getFirstMemoryBlock() < systemAllocation->getFirstMemoryBlock(); back = front, front = front->getNext())
|
|
{}
|
|
if (back)
|
|
back->setNext(systemAllocation);
|
|
else
|
|
ms_firstSystemAllocation = systemAllocation;
|
|
systemAllocation->setNext(front);
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
unsigned long MemoryManager::getCurrentNumberOfBytesAllocated(const int processId)
|
|
{
|
|
UNREF(processId);
|
|
return ms_currentBytesAllocated;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
unsigned long MemoryManager::getCurrentNumberOfBytesAllocatedNoLeakTest()
|
|
{
|
|
#if DO_TRACK
|
|
return ms_currentBytesAllocatedNoLeakTest;
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
unsigned long MemoryManager::getMaximumNumberOfBytesAllocated()
|
|
{
|
|
return ms_maxBytesAllocated;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManager::getSystemMemoryAllocatedMegabytes()
|
|
{
|
|
return ms_systemMemoryAllocatedMegabytes;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManager::getCurrentNumberOfAllocations()
|
|
{
|
|
return ms_allocations;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManager::getMaximumNumberOfAllocations()
|
|
{
|
|
return ms_maxAllocations;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::setReportAllocations(bool reportAllocations)
|
|
{
|
|
ms_reportAllocations = reportAllocations;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::registerDebugFlags()
|
|
{
|
|
#ifdef _DEBUG
|
|
DebugFlags::registerFlag(ms_debugReportFlag, "SharedMemoryManager", "reportMemory", debugReport);
|
|
DebugFlags::registerFlag(ms_debugReportMapFlag, "SharedMemoryManager", "reportMemoryMap", debugReportMap);
|
|
DebugFlags::registerFlag(ms_debugReportAllocations, "SharedMemoryManager", "reportMemoryAllocations");
|
|
DebugFlags::registerFlag(ms_debugReportLogMemoryAllocFreePointers, "SharedMemoryManager", "reportLogMemoryAllocFreePointers");
|
|
DebugFlags::registerFlag(ms_debugLogAllocations, "SharedMemoryManager", "reportLogAllocations");
|
|
DebugFlags::registerFlag(ms_debugLogAllocationsNextFrame, "SharedMemoryManager", "reportLogAllocationsNextFrame", logAllocationsNextFrame);
|
|
DebugFlags::registerFlag(ms_debugVerifyGuardPatterns, "SharedMemoryManager", "verifyGuardPatterns");
|
|
DebugFlags::registerFlag(ms_debugVerifyFreePatterns, "SharedMemoryManager", "verifyFreePatterns");
|
|
DebugFlags::registerFlag(ms_debugProfileAllocate, "SharedMemoryManager", "profileAllocate");
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Dump debugging information about the MemoryManager.
|
|
*/
|
|
|
|
void MemoryManager::debugReport()
|
|
{
|
|
#if !DISABLED
|
|
DEBUG_FATAL(!ms_installed, ("not installed"));
|
|
DEBUG_REPORT_PRINT(ms_limitSet, ("MM: %9dmb (%s limit)\n", ms_limitMegabytes, ms_hardLimit ? "hard" : "soft"));
|
|
DEBUG_REPORT_PRINT(true, ("MM: %9d/%9d/%9d cur/max/tot allocs\n", ms_allocations, ms_maxAllocations, ms_allocateCalls));
|
|
DEBUG_REPORT_PRINT(true, ("MM: %9lu/%9lu/%9lu cur/max/tot bytes\n", ms_currentBytesAllocated, ms_maxBytesAllocated, ms_allocateBytesTotal));
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
#ifdef _DEBUG
|
|
void MemoryManagerNamespace::logAllocationsNextFrame()
|
|
{
|
|
if (!ms_debugLogAllocationsNextFrameStarted)
|
|
{
|
|
REPORT_LOG_PRINT(true, ("MemoryManager::logAllocationsNextFrame start\n"));
|
|
ms_debugLogAllocations = true;
|
|
ms_debugLogAllocationsNextFrameStarted = true;
|
|
}
|
|
else
|
|
{
|
|
REPORT_LOG_PRINT(true, ("MemoryManager::logAllocationsNextFrame end\n"));
|
|
ms_debugLogAllocations = false;
|
|
ms_debugLogAllocationsNextFrameStarted = false;
|
|
ms_debugLogAllocationsNextFrame = false;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManagerNamespace::emitCharacters(char * & buffer, int blockSize, int & carryOverFree, int & carryOverUsed, int newFree, int newUsed, char const * const bufferOverrunAddress)
|
|
{
|
|
while (carryOverFree + carryOverUsed + newFree + newUsed >= blockSize)
|
|
{
|
|
int const absorbNew = blockSize - (carryOverUsed + carryOverFree);
|
|
if (newFree)
|
|
{
|
|
carryOverFree += absorbNew;
|
|
newFree -= absorbNew;
|
|
}
|
|
else
|
|
{
|
|
carryOverUsed += absorbNew;
|
|
newUsed -= absorbNew;
|
|
}
|
|
|
|
if (carryOverFree == blockSize)
|
|
*buffer = '_';
|
|
else
|
|
if (carryOverUsed == blockSize)
|
|
*buffer = '*';
|
|
else
|
|
{
|
|
int const letter = (carryOverUsed * 26) / blockSize;
|
|
*buffer = static_cast<char>('A' + letter);
|
|
}
|
|
++buffer;
|
|
|
|
FATAL(buffer == bufferOverrunAddress, ("Buffer overrun! 0x%0X", buffer));
|
|
|
|
carryOverFree = 0;
|
|
carryOverUsed = 0;
|
|
}
|
|
|
|
carryOverFree += newFree;
|
|
carryOverUsed += newUsed;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Dump an image of the memory state to the report system.
|
|
*/
|
|
|
|
void MemoryManager::debugReportMap()
|
|
{
|
|
for (SystemAllocation * systemAllocation = ms_firstSystemAllocation; systemAllocation; systemAllocation = systemAllocation->getNext())
|
|
{
|
|
char buffer[128];
|
|
int const numberOfCharactersPerRow = 64;
|
|
|
|
snprintf(buffer, sizeof(buffer)-1, "%8p %3dmb : ", systemAllocation, systemAllocation->getSize() / (1024 * 1024));
|
|
buffer[sizeof(buffer)-1] = '\0';
|
|
|
|
int const headerLength = strlen(buffer);
|
|
int const characterSize = systemAllocation->getSize() / numberOfCharactersPerRow;
|
|
|
|
int carryOverUsed = 0;
|
|
int carryOverFree = 0;
|
|
char * b = buffer + headerLength;
|
|
char const * const bufferOverrunAddress = buffer + sizeof(buffer);
|
|
for (Block * block = systemAllocation->getFirstMemoryBlock()->getNext(); block != systemAllocation->getLastMemoryBlock(); block = block->getNext())
|
|
{
|
|
if (block->isFree())
|
|
emitCharacters(b, characterSize, carryOverFree, carryOverUsed, block->getSize(), 0, bufferOverrunAddress);
|
|
else
|
|
emitCharacters(b, characterSize, carryOverFree, carryOverUsed, 0, block->getSize(), bufferOverrunAddress);
|
|
}
|
|
|
|
if (carryOverFree || carryOverUsed)
|
|
emitCharacters(b, carryOverFree + carryOverUsed, carryOverFree, carryOverUsed, 0, 0, bufferOverrunAddress);
|
|
|
|
b[0] = '\0';
|
|
|
|
DEBUG_REPORT_PRINT(true, ("%s\n", buffer));
|
|
}
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Add a block to the free memory chain list.
|
|
*
|
|
* @param block Block of memory that is being
|
|
*/
|
|
|
|
void MemoryManagerNamespace::addToFreeList(Block * block)
|
|
{
|
|
NOT_NULL(block);
|
|
|
|
// @todo consider making the ternary search tree balanced
|
|
|
|
FreeBlock * freeBlock = static_cast<FreeBlock *>(block);
|
|
int const freeBlockSize = freeBlock->getSize();
|
|
FreeBlock * parent = nullptr;
|
|
FreeBlock * * next = &ms_firstFreeBlock;
|
|
FreeBlock * same = 0;
|
|
|
|
for (FreeBlock * current = ms_firstFreeBlock; current; parent = current, current = *next)
|
|
{
|
|
int const currentBlockSize = current->getSize();
|
|
if (freeBlockSize < currentBlockSize)
|
|
next = ¤t->m_smallerFreeBlock;
|
|
else
|
|
if (freeBlockSize > currentBlockSize)
|
|
next = ¤t->m_largerFreeBlock;
|
|
else
|
|
{
|
|
// quick out for same size blocks
|
|
same = current;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (same)
|
|
{
|
|
|
|
freeBlock->m_smallerFreeBlock = same->m_smallerFreeBlock;
|
|
same->m_smallerFreeBlock = nullptr;
|
|
if (freeBlock->m_smallerFreeBlock)
|
|
freeBlock->m_smallerFreeBlock->m_parentFreeBlock = freeBlock;
|
|
|
|
freeBlock->m_sameFreeBlock = same;
|
|
same->m_parentFreeBlock = freeBlock;
|
|
|
|
freeBlock->m_largerFreeBlock = same->m_largerFreeBlock;
|
|
same->m_largerFreeBlock = nullptr;
|
|
if (freeBlock->m_largerFreeBlock)
|
|
freeBlock->m_largerFreeBlock->m_parentFreeBlock = freeBlock;
|
|
|
|
*next = freeBlock;
|
|
freeBlock->m_parentFreeBlock = parent;
|
|
}
|
|
else
|
|
{
|
|
*next = freeBlock;
|
|
freeBlock->m_smallerFreeBlock = nullptr;
|
|
freeBlock->m_sameFreeBlock = nullptr;
|
|
freeBlock->m_largerFreeBlock = nullptr;
|
|
freeBlock->m_parentFreeBlock = parent;
|
|
}
|
|
|
|
if (++ms_freeBlocks > ms_maxFreeBlocks)
|
|
ms_maxFreeBlocks = ms_freeBlocks;
|
|
|
|
#ifdef _DEBUG
|
|
int treeHeight = 0;
|
|
while (freeBlock)
|
|
{
|
|
++treeHeight;
|
|
freeBlock = freeBlock->m_parentFreeBlock;
|
|
}
|
|
if (treeHeight > ms_maxTreeHeight)
|
|
ms_maxTreeHeight = treeHeight;
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Remove a block from the free memory chain list.
|
|
*
|
|
* @param block Block of memory that is being allocated
|
|
*/
|
|
|
|
void MemoryManagerNamespace::removeFromFreeList(FreeBlock * block)
|
|
{
|
|
NOT_NULL(block);
|
|
|
|
// find the pointer that points to block
|
|
FreeBlock * * parentPointer = nullptr;
|
|
FreeBlock * parent = block->m_parentFreeBlock;
|
|
if (parent)
|
|
{
|
|
if (parent->m_smallerFreeBlock == block)
|
|
parentPointer = &parent->m_smallerFreeBlock;
|
|
else
|
|
if (parent->m_sameFreeBlock == block)
|
|
parentPointer = &parent->m_sameFreeBlock;
|
|
else
|
|
{
|
|
DEBUG_FATAL(parent->m_largerFreeBlock != block, ("parent doesn't know about child"));
|
|
parentPointer = &parent->m_largerFreeBlock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
DEBUG_FATAL(ms_firstFreeBlock != block, ("no parent but not first"));
|
|
parentPointer = &ms_firstFreeBlock;
|
|
}
|
|
|
|
// now we need to replace our node with one from underneath us
|
|
FreeBlock * same = block->m_sameFreeBlock;
|
|
if (same)
|
|
{
|
|
// making use of the same size block is pretty easy and doesn't disturb tree balance so its our prefered choice
|
|
same->m_smallerFreeBlock = block->m_smallerFreeBlock;
|
|
if (same->m_smallerFreeBlock)
|
|
same->m_smallerFreeBlock->m_parentFreeBlock = same;
|
|
|
|
same->m_largerFreeBlock = block->m_largerFreeBlock;
|
|
if (same->m_largerFreeBlock)
|
|
same->m_largerFreeBlock->m_parentFreeBlock = same;
|
|
|
|
same->m_parentFreeBlock = parent;
|
|
|
|
*parentPointer = same;
|
|
}
|
|
else
|
|
{
|
|
if (block->m_smallerFreeBlock)
|
|
{
|
|
if (block->m_largerFreeBlock)
|
|
{
|
|
// this is the worst case. this free block has smaller and larger children, but not any same sized children
|
|
// we're going to take the smallest block off the larger list and use that to replace the current node
|
|
FreeBlock * back = nullptr;
|
|
FreeBlock * replacement = block->m_largerFreeBlock;
|
|
while (replacement->m_smallerFreeBlock)
|
|
{
|
|
back = replacement;
|
|
replacement = replacement->m_smallerFreeBlock;
|
|
}
|
|
|
|
// disconnect the link between its parent and out replacement block
|
|
if (back)
|
|
{
|
|
back->m_smallerFreeBlock = replacement->m_largerFreeBlock;
|
|
if (back->m_smallerFreeBlock)
|
|
back->m_smallerFreeBlock->m_parentFreeBlock = back;
|
|
|
|
replacement->m_largerFreeBlock = block->m_largerFreeBlock;
|
|
replacement->m_largerFreeBlock->m_parentFreeBlock = replacement;
|
|
}
|
|
|
|
// put the replacement block in its new place
|
|
replacement->m_smallerFreeBlock = block->m_smallerFreeBlock;
|
|
replacement->m_smallerFreeBlock->m_parentFreeBlock = replacement;
|
|
|
|
replacement->m_parentFreeBlock = parent;
|
|
|
|
*parentPointer = replacement;
|
|
}
|
|
else
|
|
{
|
|
// this block only has one child, which is smaller. an easy case.
|
|
block->m_smallerFreeBlock->m_parentFreeBlock = parent;
|
|
*parentPointer = block->m_smallerFreeBlock;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (block->m_largerFreeBlock)
|
|
{
|
|
// this block only has one child, which is larger. an easy case.
|
|
block->m_largerFreeBlock->m_parentFreeBlock = parent;
|
|
*parentPointer = block->m_largerFreeBlock;
|
|
}
|
|
else
|
|
{
|
|
// this block has no children
|
|
*parentPointer = nullptr;
|
|
}
|
|
}
|
|
}
|
|
|
|
// remove all the pointers the block may have had
|
|
block->m_smallerFreeBlock = nullptr;
|
|
block->m_sameFreeBlock = nullptr;
|
|
block->m_largerFreeBlock = nullptr;
|
|
block->m_parentFreeBlock = nullptr;
|
|
|
|
--ms_freeBlocks;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
FreeBlock *MemoryManagerNamespace::searchFreeList(int blockSize)
|
|
{
|
|
FreeBlock * result = nullptr;
|
|
FreeBlock * current = ms_firstFreeBlock;
|
|
while (current)
|
|
{
|
|
int const currentBlockSize = current->getSize();
|
|
|
|
if (currentBlockSize < blockSize)
|
|
{
|
|
// this block is too small so check blocks larger than this
|
|
current = current->m_largerFreeBlock;
|
|
}
|
|
else
|
|
if (currentBlockSize > blockSize)
|
|
{
|
|
// this block is too large but will be the best fit so far. continue checking smaller blocks.
|
|
result = current;
|
|
current = current->m_smallerFreeBlock;
|
|
}
|
|
else
|
|
{
|
|
// this block is a perfect fit, return it immediately.
|
|
return current;
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Dynamically allocate memory.
|
|
*
|
|
* Users should not call this routine directly. It should only be called
|
|
* by operator new.
|
|
*
|
|
* @param size Number of bytes to allocate
|
|
* @param owner Line number on which the allocation was made
|
|
* @param array True if the array form of operator new was used, false if the scalar form was used
|
|
*/
|
|
|
|
void * MemoryManager::allocate(size_t size, uint32 owner, bool array, bool leakTest)
|
|
{
|
|
if (!ms_installed)
|
|
new(ms_memoryManagerBuffer) MemoryManager;
|
|
|
|
UNREF(owner);
|
|
UNREF(array);
|
|
UNREF(leakTest);
|
|
|
|
#if DISABLED
|
|
return operator new(size);
|
|
#else
|
|
|
|
DEBUG_FATAL(!ms_installed, ("not installed"));
|
|
|
|
#ifdef _DEBUG
|
|
if (ms_debugProfileAllocate)
|
|
PROFILER_BLOCK_ENTER(ms_allocateProfilerBlock);
|
|
|
|
if (ms_debugVerifyGuardPatterns || ms_debugVerifyFreePatterns)
|
|
verify(ms_debugVerifyGuardPatterns, ms_debugVerifyFreePatterns);
|
|
|
|
if (ms_debugReportAllocations || ms_debugLogAllocations)
|
|
{
|
|
char libName[256];
|
|
char fileName[256];
|
|
int line;
|
|
if (ms_allowNameLookup && DebugHelp::lookupAddress(owner, libName, fileName, sizeof(fileName), line))
|
|
{
|
|
DEBUG_REPORT(true, (ms_debugReportAllocations ? Report::RF_print : 0) | (ms_debugLogAllocations ? Report::RF_log : 0), ("%s(%d): alloc %d=bytes %d=array\n", fileName, line, size, static_cast<int>(array)));
|
|
}
|
|
else
|
|
{
|
|
DEBUG_REPORT(true, (ms_debugReportAllocations ? Report::RF_print : 0) | (ms_debugLogAllocations ? Report::RF_log : 0), ("%08x: alloc %d=bytes %d=array\n", static_cast<int>(owner), size, static_cast<int>(array)));
|
|
}
|
|
}
|
|
#endif
|
|
|
|
ms_criticalSection->enter();
|
|
|
|
// get the size of the allocation
|
|
int allocSize = (cms_allocatedBlockSize + cms_guardBandSize + (size ? static_cast<int>(size) : 1) + cms_guardBandSize + 15) & ~15;
|
|
|
|
FreeBlock * bestFreeBlock = nullptr;
|
|
for (int tries = 0; !bestFreeBlock && tries < 2; ++tries)
|
|
{
|
|
bestFreeBlock = searchFreeList(allocSize);
|
|
|
|
// if the memory allocation failed, try to get some more memory
|
|
if (!bestFreeBlock)
|
|
allocateSystemMemory(convertBytesToMegabytesForSystemAllocation(cms_blockSize + cms_blockSize + allocSize + cms_blockSize));
|
|
}
|
|
|
|
// make sure memory was available
|
|
if (!bestFreeBlock)
|
|
{
|
|
if (ConfigSharedFoundation::getMemoryManagerReportOnOutOfMemory())
|
|
{
|
|
// avoid deadlock from FATAL calling free
|
|
(*LogMessage)("Out of memory, dumping current allocations:\n");
|
|
IGNORE_RETURN(MemoryManagerNamespace::report(false));
|
|
}
|
|
|
|
ms_criticalSection->leave();
|
|
FATAL(true, ("failed allocation attempt for %d (%d actual)", allocSize, size));
|
|
}
|
|
|
|
removeFromFreeList(bestFreeBlock);
|
|
|
|
// setup the allocation record
|
|
bestFreeBlock->setFree(false);
|
|
|
|
// check to see if we should subdivide this block
|
|
if (bestFreeBlock->getSize() > (allocSize + cms_allocatedBlockSize + cms_guardBandSize + 1 + cms_guardBandSize))
|
|
{
|
|
Block *block = reinterpret_cast<Block *>(reinterpret_cast<byte *>(bestFreeBlock) + allocSize);
|
|
block->setPrevious(bestFreeBlock);
|
|
block->setNext(bestFreeBlock->getNext());
|
|
block->setFree(true);
|
|
|
|
bestFreeBlock->getNext()->setPrevious(block);
|
|
bestFreeBlock->setNext(block);
|
|
|
|
addToFreeList(block);
|
|
}
|
|
|
|
AllocatedBlock * best = reinterpret_cast<AllocatedBlock *>(bestFreeBlock);
|
|
|
|
#if DO_SCALAR
|
|
best->setAllocatedAsArray(array);
|
|
#endif
|
|
|
|
#if DO_TRACK
|
|
best->setCheckForLeaks(leakTest);
|
|
best->setOwner(0, owner);
|
|
#endif
|
|
#if DO_TRACK > 1
|
|
{
|
|
enum { OFFSET = 3 };
|
|
uint32 owners[DO_TRACK + OFFSET];
|
|
DebugHelp::getCallStack(owners, DO_TRACK + OFFSET);
|
|
|
|
for (int i = 1; i < DO_TRACK; ++i)
|
|
{
|
|
best->setOwner(i, owners[i + OFFSET]);
|
|
|
|
#ifdef _DEBUG
|
|
if (ms_debugReportAllocations || ms_debugLogAllocations)
|
|
{
|
|
char libName[256];
|
|
char fileName[256];
|
|
int line;
|
|
if (ms_allowNameLookup && DebugHelp::lookupAddress(owners[i + OFFSET], libName, fileName, sizeof(fileName), line))
|
|
{
|
|
DEBUG_REPORT(true, (ms_debugReportAllocations ? Report::RF_print : 0) | (ms_debugLogAllocations ? Report::RF_log : 0), (" %s(%d): caller %d\n", fileName, line, i));
|
|
}
|
|
else
|
|
{
|
|
DEBUG_REPORT(true, (ms_debugReportAllocations ? Report::RF_print : 0) | (ms_debugLogAllocations ? Report::RF_log : 0), (" %08x: caller %d\n", static_cast<int>(owners[i + OFFSET]), i));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
#endif
|
|
#if DO_TRACK || DO_GUARDS
|
|
best->setRequestedSize(static_cast<int>(size));
|
|
ms_currentBytesRequested += size;
|
|
DEBUG_FATAL(best->getRequestedSize() != static_cast<int>(size), ("allocated more memory at once than the memory manager supports (%d)", (1 << Block::cms_requestedSizeBits) - 1));
|
|
#endif
|
|
|
|
// update the size of the allocation because our block may not have been large enough to subdivide
|
|
allocSize = best->getSize();
|
|
|
|
// update the number of bytes allocated
|
|
++ms_allocateCalls;
|
|
ms_allocateBytesTotal += allocSize;
|
|
ms_currentBytesAllocated += allocSize;
|
|
|
|
#if DO_TRACK
|
|
if (!leakTest)
|
|
ms_currentBytesAllocatedNoLeakTest += allocSize;
|
|
#endif
|
|
|
|
if (++ms_allocations > ms_maxAllocations)
|
|
ms_maxAllocations = ms_allocations;
|
|
if (ms_currentBytesAllocated > ms_maxBytesAllocated)
|
|
ms_maxBytesAllocated = ms_currentBytesAllocated;
|
|
|
|
// get another pointer to the memory we allocated so we can tinker with it
|
|
byte * memory = reinterpret_cast<byte *>(best) + cms_allocatedBlockSize + cms_guardBandSize;
|
|
|
|
#if DO_GUARDS
|
|
// fill the prefix guard band
|
|
memset(memory - cms_guardBandSize, cms_guardFillPattern, cms_guardBandSize);
|
|
#endif
|
|
|
|
// fill the user memory with the initialize pattern
|
|
#if DO_INITIALIZE_FILLS
|
|
memset(memory, cms_initializeFillPattern, size);
|
|
#endif
|
|
|
|
#if DO_GUARDS
|
|
// fill the suffix guard band
|
|
memset(memory+size, cms_guardFillPattern, cms_guardBandSize);
|
|
#endif
|
|
|
|
ms_criticalSection->leave();
|
|
|
|
DEBUG_REPORT_LOG_PRINT(ms_debugReportLogMemoryAllocFreePointers, ("MM::alloc %08x\n", reinterpret_cast<int>(memory)));
|
|
|
|
#ifdef _DEBUG
|
|
if (ms_debugProfileAllocate)
|
|
PROFILER_BLOCK_LEAVE(ms_allocateProfilerBlock);
|
|
#endif
|
|
|
|
// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::allocate() requested_size=%d, alloc_size=%d, ptr=%p\n", size, allocSize, memory));
|
|
|
|
return memory;
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void *MemoryManager::reallocate(void *userPointer, size_t newSize)
|
|
{
|
|
AllocatedBlock *allocatedBlock = 0;
|
|
bool array = false;
|
|
int oldSize = 0;
|
|
|
|
if (userPointer)
|
|
{
|
|
allocatedBlock = reinterpret_cast<AllocatedBlock *>(reinterpret_cast<byte *>(userPointer) - (cms_allocatedBlockSize + cms_guardBandSize));
|
|
#if DO_SCALAR
|
|
array = allocatedBlock->isAllocatedAsArray();
|
|
#endif
|
|
if (!newSize)
|
|
{
|
|
MemoryManager::free(userPointer, array);
|
|
|
|
// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::reallocate() new_requested_size=%d, org ptr=%p, new ptr=nullptr\n", newSize, userPointer));
|
|
|
|
return 0;
|
|
}
|
|
#if DO_TRACK || DO_GUARDS
|
|
oldSize = allocatedBlock->getRequestedSize();
|
|
#else
|
|
oldSize = allocatedBlock->getSize()-cms_allocatedBlockSize;
|
|
#endif
|
|
}
|
|
|
|
if (newSize <= static_cast<size_t>(oldSize))
|
|
{
|
|
// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::reallocate() new_requested_size=%d, org ptr=%p, new ptr=%p\n", newSize, userPointer, userPointer));
|
|
|
|
return userPointer;
|
|
}
|
|
|
|
#if DO_TRACK
|
|
uint32 owner = allocatedBlock->getOwner(0);
|
|
bool leakTest = allocatedBlock->checkForLeaks();
|
|
#else
|
|
uint32 owner = 0;
|
|
bool leakTest = false;
|
|
#endif
|
|
|
|
void *newPointer = allocate(newSize, owner, array, leakTest);
|
|
if (oldSize)
|
|
memcpy(newPointer, userPointer, oldSize);
|
|
if (userPointer)
|
|
MemoryManager::free(userPointer, array);
|
|
|
|
// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::reallocate() new_requested_size=%d, org ptr=%p, new ptr=%p\n", newSize, userPointer, newPointer));
|
|
|
|
return newPointer;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Free dynamically allocated memory.
|
|
*
|
|
* Users should not call this routine directly. It should only be called
|
|
* by operator delete.
|
|
*
|
|
* This routine should not be called with the nullptr pointer.
|
|
*
|
|
* @param userPointer Pointer to the memory
|
|
* @param array True if the array form of operator new was used, false if the scalar form was used
|
|
*/
|
|
|
|
void MemoryManager::free(void * userPointer, bool array)
|
|
{
|
|
#if DISABLED
|
|
UNREF(array);
|
|
operator delete(userPointer);
|
|
return;
|
|
#else
|
|
|
|
DEBUG_FATAL(!ms_installed, ("not installed"));
|
|
NOT_NULL(userPointer);
|
|
|
|
#ifdef _DEBUG
|
|
if (ms_debugVerifyGuardPatterns || ms_debugVerifyFreePatterns)
|
|
verify(ms_debugVerifyGuardPatterns, ms_debugVerifyFreePatterns);
|
|
#endif
|
|
|
|
DEBUG_REPORT_LOG_PRINT(ms_debugReportLogMemoryAllocFreePointers, ("MM::free %08x\n", reinterpret_cast<int>(userPointer)));
|
|
|
|
UNREF(array);
|
|
|
|
ms_criticalSection->enter();
|
|
|
|
AllocatedBlock * allocatedBlock = reinterpret_cast<AllocatedBlock *>(reinterpret_cast<byte *>(userPointer) - (cms_allocatedBlockSize + cms_guardBandSize));
|
|
|
|
#if DEBUG_LEVEL == DEBUG_LEVEL_DEBUG
|
|
DEBUG_FATAL(allocatedBlock->getNext()->getPrevious() != allocatedBlock, ("Bad free (1) %p", userPointer));
|
|
DEBUG_FATAL(allocatedBlock->getPrevious()->getNext() != allocatedBlock, ("Bad free (2) %p", userPointer));
|
|
DEBUG_FATAL(allocatedBlock->isFree(), ("Freeing already free block %p", userPointer));
|
|
#endif
|
|
|
|
#if DO_TRACK
|
|
ms_bytesFreedPerFrame += allocatedBlock->getRequestedSize();
|
|
#endif
|
|
|
|
#if DO_GUARDS
|
|
{
|
|
// verify the guard bands
|
|
byte * guard = reinterpret_cast<byte *>(allocatedBlock) + cms_allocatedBlockSize;
|
|
bool corrupt = false;
|
|
|
|
// check the prefix guard band
|
|
for (int i = 0; i < cms_guardBandSize; ++i, ++guard)
|
|
if (*guard != cms_guardFillPattern)
|
|
{
|
|
corrupt = true;
|
|
DEBUG_REPORT_LOG_PRINT(true, ("MemoryManager::free corrupted guard prefix at position %3d = %02x\n", i - cms_guardBandSize, static_cast<int>(*guard)));
|
|
DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("MemoryManager::free corrupted guard prefix at position %3d = %02x\n", i - cms_guardBandSize, static_cast<int>(*guard)));
|
|
}
|
|
|
|
// advance past the user memory
|
|
guard += allocatedBlock->getRequestedSize();
|
|
|
|
// check the suffix guard band
|
|
for (int j = 0; j < cms_guardBandSize; ++j, ++guard)
|
|
if (*guard != cms_guardFillPattern)
|
|
{
|
|
corrupt = true;
|
|
DEBUG_REPORT_LOG_PRINT(true, ("MemoryManager::free corrupted guard suffix at position %3d = %02x\n", j, static_cast<int>(*guard)));
|
|
DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("MemoryManager::free corrupted guard suffix at position %3d = %02x\n", j, static_cast<int>(*guard)));
|
|
}
|
|
|
|
if (corrupt)
|
|
{
|
|
MemoryManagerNamespace::report(allocatedBlock, false);
|
|
ms_criticalSection->leave();
|
|
DEBUG_FATAL(true, ("corrupted guard pattern"));
|
|
return; //lint !e527 // Unreachable
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if DO_SCALAR
|
|
if (allocatedBlock->isAllocatedAsArray() != array) //lint !e731 // Info -- Boolean argument to equal/not equal
|
|
{
|
|
MemoryManagerNamespace::report(allocatedBlock, false);
|
|
#ifdef _DEBUG
|
|
bool const blockArray = allocatedBlock->isAllocatedAsArray();
|
|
ms_criticalSection->leave();
|
|
FATAL(true, ("allocated %s deleted %s", blockArray ? "array" : "scalar", array ? "array" : "scalar")); //lint !e731 // Info -- Boolean argument to equal/not equal
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
int const memorySize = allocatedBlock->getSize();
|
|
|
|
// wipe the user memory
|
|
#if DO_FREE_FILLS
|
|
imemset(reinterpret_cast<byte *>(allocatedBlock) + cms_allocatedBlockSize, cms_freeFillPattern, memorySize - cms_allocatedBlockSize);
|
|
#endif
|
|
|
|
allocatedBlock->setFree(true);
|
|
#if DO_SCALAR
|
|
allocatedBlock->setAllocatedAsArray(false);
|
|
#endif
|
|
|
|
// clear out the block records
|
|
#if DO_TRACK
|
|
allocatedBlock->fillOwnerWithFreePattern();
|
|
#endif
|
|
|
|
#if DO_TRACK || DO_GUARDS
|
|
ms_currentBytesRequested -= allocatedBlock->getRequestedSize();
|
|
allocatedBlock->setRequestedSize(0);
|
|
DEBUG_FATAL(allocatedBlock->getRequestedSize() != 0, ("bad size"));
|
|
#endif
|
|
|
|
// update the number of bytes allocated
|
|
++ms_freeCalls;
|
|
--ms_allocations;
|
|
|
|
DEBUG_FATAL((ms_currentBytesAllocated < static_cast<unsigned long>(memorySize)), ("currentBytesAllocated underflow"));
|
|
ms_currentBytesAllocated -= memorySize;
|
|
#if DO_TRACK
|
|
if (!allocatedBlock->checkForLeaks())
|
|
ms_currentBytesAllocatedNoLeakTest -= memorySize;
|
|
#endif
|
|
DEBUG_FATAL(ms_allocations < 0, ("allocations underflow"));
|
|
|
|
// DEBUG_REPORT_LOG(ms_logEachAlloc, ("MemoryManager::free() requested_size=%d, alloc_size=%d, userPointer=%p, allocatedBlock=%p\n", requestedSize, memorySize, userPointer, allocatedBlock));
|
|
|
|
Block *block = allocatedBlock;
|
|
|
|
// recombine with the previous block
|
|
if (block->getPrevious()->isFree())
|
|
{
|
|
FreeBlock * const previous = static_cast<FreeBlock *>(block->getPrevious());
|
|
removeFromFreeList(previous);
|
|
previous->setNext(block->getNext());
|
|
block->getNext()->setPrevious(previous);
|
|
|
|
#if DO_FREE_FILLS
|
|
memset(block, cms_freeFillPattern, cms_freeBlockSize);
|
|
#endif
|
|
block = previous;
|
|
}
|
|
|
|
// recombine with the following block
|
|
if (block->getNext()->isFree())
|
|
{
|
|
FreeBlock * const next = static_cast<FreeBlock *>(block->getNext());
|
|
removeFromFreeList(next);
|
|
block->setNext(next->getNext());
|
|
next->getNext()->setPrevious(block);
|
|
|
|
#if DO_FREE_FILLS
|
|
memset(next, cms_freeFillPattern, cms_freeBlockSize);
|
|
#endif
|
|
}
|
|
|
|
addToFreeList(block);
|
|
|
|
ms_criticalSection->leave();
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManagerNamespace::quickGuessIsValidPointerFromHeap(void const * userPointer)
|
|
{
|
|
int result = 0;
|
|
AllocatedBlock const * allocatedBlock = reinterpret_cast<AllocatedBlock const *>(reinterpret_cast<byte const *>(userPointer) - (cms_allocatedBlockSize + cms_guardBandSize));
|
|
|
|
ms_criticalSection->enter();
|
|
if (allocatedBlock->getNext()->getPrevious() != allocatedBlock) result = 1;
|
|
if (allocatedBlock->getPrevious()->getNext() != allocatedBlock) result = 2;
|
|
if (allocatedBlock->isFree()) result = 3;
|
|
ms_criticalSection->leave();
|
|
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManagerNamespace::authoritativeIsValidPointerFromHeap(void const * userPointer)
|
|
{
|
|
ms_criticalSection->enter();
|
|
|
|
int result = quickGuessIsValidPointerFromHeap(userPointer);
|
|
if (result != 0)
|
|
return result;
|
|
|
|
result = 16;
|
|
for (SystemAllocation * systemAllocation = ms_firstSystemAllocation; systemAllocation; systemAllocation = systemAllocation->getNext())
|
|
for (Block * block = systemAllocation->getFirstMemoryBlock()->getNext(); result != 0 && block != systemAllocation->getLastMemoryBlock(); block = block->getNext())
|
|
if (!block->isFree())
|
|
{
|
|
byte const * memory = reinterpret_cast<byte const *>(block) + cms_allocatedBlockSize + cms_guardBandSize;
|
|
if (memory == userPointer)
|
|
result = 0;
|
|
}
|
|
|
|
ms_criticalSection->leave();
|
|
|
|
return result;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Take ownership for a block of memory.
|
|
*
|
|
* @param userPointer Memory to take ownership of
|
|
*/
|
|
|
|
void MemoryManager::own(void * userPointer)
|
|
{
|
|
UNREF(userPointer);
|
|
|
|
#if DISABLED || !DO_TRACK
|
|
return;
|
|
#else
|
|
|
|
DEBUG_FATAL(!ms_installed, ("not installed"));
|
|
|
|
if (!userPointer)
|
|
return;
|
|
|
|
ms_criticalSection->enter();
|
|
|
|
// fetch the block pointer
|
|
AllocatedBlock * block = reinterpret_cast<AllocatedBlock *>(reinterpret_cast<byte *>(userPointer) - (cms_allocatedBlockSize + cms_guardBandSize));
|
|
DEBUG_FATAL(block->isFree(), ("cannot own a free block"));
|
|
|
|
// update the owners
|
|
{
|
|
enum { OFFSET = 2 };
|
|
uint32 owners[DO_TRACK + OFFSET];
|
|
DebugHelp::getCallStack(owners, DO_TRACK + OFFSET);
|
|
|
|
for (int i = 0; i < DO_TRACK; ++i)
|
|
block->setOwner(i, owners[i + OFFSET]);
|
|
}
|
|
|
|
ms_criticalSection->leave();
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::verify(bool guardPatterns, bool freePatterns)
|
|
{
|
|
UNREF(guardPatterns);
|
|
UNREF(freePatterns);
|
|
|
|
#if DO_FREE_FILLS || DO_GUARDS
|
|
|
|
ms_criticalSection->enter();
|
|
|
|
// search for the memory pointer
|
|
for (SystemAllocation * systemAllocation = ms_firstSystemAllocation; systemAllocation; systemAllocation = systemAllocation->getNext())
|
|
for (Block * block = systemAllocation->getFirstMemoryBlock()->getNext(); block != systemAllocation->getLastMemoryBlock(); block = block->getNext())
|
|
if (block->isFree())
|
|
{
|
|
#if DO_FREE_FILLS
|
|
if (freePatterns)
|
|
{
|
|
bool corrupt = false;
|
|
byte * memory = reinterpret_cast<byte *>(block) + cms_freeBlockSize;
|
|
int freeSize = block->getSize() - cms_freeBlockSize;
|
|
|
|
for (int i = 0; i < freeSize; ++i, ++memory)
|
|
if (*memory != cms_freeFillPattern)
|
|
{
|
|
corrupt = true;
|
|
DEBUG_REPORT_LOG_PRINT(true, ("corrupted free pattern at position %3d [membase=0x%x, memaddr=0x%x] = %02x\n", i, reinterpret_cast<unsigned int>(reinterpret_cast<byte *>(block) + cms_freeBlockSize), reinterpret_cast<unsigned int>(reinterpret_cast<byte *>(block) + cms_freeBlockSize + i), static_cast<int>(*memory)));
|
|
DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("corrupted free pattern at position %3d = %02x\n", i, static_cast<int>(*memory)));
|
|
}
|
|
|
|
if (corrupt)
|
|
{
|
|
ms_criticalSection->leave();
|
|
DEBUG_FATAL(true, ("corrupted free pattern"));
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
if (guardPatterns)
|
|
{
|
|
#if DO_GUARDS
|
|
// verify the guard bands
|
|
byte * guard = reinterpret_cast<byte *>(block) + cms_allocatedBlockSize;
|
|
bool corrupt = false;
|
|
|
|
// check the prefix guard band
|
|
for (int i = 0; i < cms_guardBandSize; ++i, ++guard)
|
|
if (*guard != cms_guardFillPattern)
|
|
{
|
|
corrupt = true;
|
|
DEBUG_REPORT_LOG_PRINT(true, ("corrupted guard prefix at position %3d = %02x\n", i - cms_guardBandSize, static_cast<int>(*guard)));
|
|
DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("corrupted guard prefix at position %3d = %02x\n", i - cms_guardBandSize, static_cast<int>(*guard)));
|
|
}
|
|
|
|
// advance past the user memory
|
|
guard += static_cast<AllocatedBlock *>(block)->getRequestedSize();
|
|
|
|
// check the suffix guard band
|
|
for (int j = 0; j < cms_guardBandSize; ++j, ++guard)
|
|
if (*guard != cms_guardFillPattern)
|
|
{
|
|
corrupt = true;
|
|
DEBUG_REPORT_LOG_PRINT(true, ("corrupted guard suffix at position %3d = %02x\n", j, static_cast<int>(*guard)));
|
|
DEBUG_OUTPUT_CHANNEL("Foundation\\MemoryManager", ("corrupted guard suffix at position %3d = %02x\n", j, static_cast<int>(*guard)));
|
|
}
|
|
|
|
if (corrupt)
|
|
{
|
|
MemoryManagerNamespace::report(static_cast<AllocatedBlock *>(block), false);
|
|
ms_criticalSection->leave();
|
|
DEBUG_FATAL(true, ("corrupted guard pattern"));
|
|
}
|
|
#endif
|
|
}
|
|
|
|
ms_criticalSection->leave();
|
|
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* @internal
|
|
*/
|
|
|
|
void MemoryManagerNamespace::report(AllocatedBlock const * block, bool leak)
|
|
{
|
|
#if DO_TRACK
|
|
uint32 const owner = block->getOwner(0);
|
|
#else
|
|
uint32 const owner = 0;
|
|
#endif
|
|
#if DO_TRACK || DO_GUARDS
|
|
int const requestedSize = block->getRequestedSize();;
|
|
#else
|
|
int const requestedSize = 0;
|
|
#endif
|
|
|
|
char buffer[256];
|
|
char libName[256];
|
|
char fileName[256];
|
|
int line = 0;
|
|
int const memory = reinterpret_cast<int>(reinterpret_cast<byte const *>(block) + cms_allocatedBlockSize + cms_guardBandSize);
|
|
|
|
if (ms_allowNameLookup && DebugHelp::lookupAddress(owner, libName, fileName, sizeof(fileName), line))
|
|
{
|
|
sprintf(buffer, "%s(%d) : %08X memory %s, %d bytes\n", fileName, line, memory, leak ? "leak" : "allocation", static_cast<int>(requestedSize));
|
|
}
|
|
else
|
|
{
|
|
sprintf(buffer, "unknown(0x%08X) : %08X memory %s, %d bytes\n", static_cast<unsigned int>(owner), memory, leak ? "leak" : "allocation", static_cast<int>(requestedSize));
|
|
}
|
|
|
|
(*LogMessage)(buffer);
|
|
|
|
#if DO_TRACK > 1
|
|
{
|
|
for (int i = 1; i < DO_TRACK; ++i)
|
|
if (block->getOwner(i))
|
|
{
|
|
if (ms_allowNameLookup && DebugHelp::lookupAddress(block->getOwner(i), libName, fileName, sizeof(fileName), line))
|
|
sprintf(buffer, " %s(%d) : caller %d\n", fileName, line, i);
|
|
else
|
|
sprintf(buffer, " 0x%08X : caller %d\n", static_cast<int>(block->getOwner(i)), i);
|
|
(*LogMessage)(buffer);
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Dump information about all outstanding allocations.
|
|
*/
|
|
|
|
int MemoryManagerNamespace::report(bool leak)
|
|
{
|
|
DEBUG_FATAL(!ms_installed, ("not installed"));
|
|
|
|
int count = 0;
|
|
ms_criticalSection->enter();
|
|
|
|
// search for the memory pointer
|
|
for (SystemAllocation * systemAllocation = ms_firstSystemAllocation; systemAllocation; systemAllocation = systemAllocation->getNext())
|
|
for (Block * block = systemAllocation->getFirstMemoryBlock()->getNext(); block != systemAllocation->getLastMemoryBlock(); block = block->getNext())
|
|
if (!block->isFree())
|
|
{
|
|
#if DO_TRACK
|
|
if (!leak || static_cast<AllocatedBlock*>(block)->checkForLeaks())
|
|
#else
|
|
if (!leak)
|
|
#endif
|
|
{
|
|
report(static_cast<AllocatedBlock *>(block), leak);
|
|
++count;
|
|
}
|
|
}
|
|
|
|
ms_criticalSection->leave();
|
|
|
|
return count;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
/**
|
|
* Dump information about all outstanding allocations.
|
|
*/
|
|
|
|
void MemoryManager::report()
|
|
{
|
|
IGNORE_RETURN(MemoryManagerNamespace::report(false));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
void MemoryManagerNamespace::logMessageToFd(char const *message)
|
|
{
|
|
if (ms_logMessageFd >= 0)
|
|
_write(ms_logMessageFd, message, strlen(message));
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool MemoryManagerNamespace::reportToFile(char const *fileName, bool leak)
|
|
{
|
|
if (ms_logMessageFd >= 0)
|
|
return false;
|
|
|
|
ms_logMessageFd = _open(fileName, O_CREAT | O_TRUNC | O_WRONLY, S_IREAD | S_IWRITE);
|
|
if (ms_logMessageFd < 0)
|
|
return false;
|
|
|
|
void (*oldLogMessage)(char const * message) = LogMessage;
|
|
LogMessage = &logMessageToFd;
|
|
|
|
report(leak);
|
|
|
|
LogMessage = oldLogMessage;
|
|
|
|
_close(ms_logMessageFd);
|
|
ms_logMessageFd = -1;
|
|
|
|
return true;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------
|
|
|
|
bool MemoryManager::reportToFile(char const *fileName, bool leak)
|
|
{
|
|
return MemoryManagerNamespace::reportToFile(fileName, leak);
|
|
}
|
|
|
|
// ======================================================================
|
|
// DISABLE_MEMORY_MANAGER
|
|
#else
|
|
// ======================================================================
|
|
namespace MemoryManagerNamespace
|
|
{
|
|
// Memory tracking variables avialble when memory manger is running
|
|
// in disabled mode for linux
|
|
int ms_allocateCalls;
|
|
int ms_freeCalls;
|
|
unsigned long ms_allocateBytesTotal;
|
|
#ifndef _WIN32
|
|
int ms_processVmSizeKBytes;
|
|
#endif
|
|
unsigned long ms_maxBytesAllocated;
|
|
int ms_allocations;
|
|
int ms_maxAllocations;
|
|
bool ms_installed;
|
|
|
|
struct Statm //cache for /proc/statm data
|
|
{
|
|
int tpsr; //total program size resident
|
|
int rss; //resident set size share
|
|
};
|
|
|
|
Statm s_statm;
|
|
int s_pagesize;
|
|
|
|
RecursiveMutex * ms_criticalSection;
|
|
}
|
|
|
|
using namespace MemoryManagerNamespace;
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
MemoryManager::MemoryManager()
|
|
{
|
|
// Initialize all memory tracking vars
|
|
MemoryManagerNamespace::ms_allocateCalls = 0;
|
|
MemoryManagerNamespace::ms_freeCalls = 0;
|
|
MemoryManagerNamespace::ms_allocateBytesTotal = 0;
|
|
#ifndef _WIN32
|
|
MemoryManagerNamespace::ms_processVmSizeKBytes = 0;
|
|
#endif
|
|
MemoryManagerNamespace::ms_maxBytesAllocated = 0;
|
|
MemoryManagerNamespace::ms_allocations = 0;
|
|
MemoryManagerNamespace::ms_maxAllocations= 0;
|
|
|
|
DEBUG_FATAL(MemoryManagerNamespace::ms_installed, ("already installed"));
|
|
|
|
ms_criticalSection = new RecursiveMutex;
|
|
|
|
MemoryManagerNamespace::ms_installed = true;
|
|
|
|
#if defined(PLATFORM_WIN32) && defined(_DEBUG)
|
|
_CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_DEBUG);
|
|
_CrtSetReportMode(_CRT_ERROR, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW);
|
|
_CrtSetReportMode(_CRT_ASSERT, _CRTDBG_MODE_DEBUG | _CRTDBG_MODE_WNDW);
|
|
|
|
int crtHeapFlags = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
|
|
crtHeapFlags |= _CRTDBG_ALLOC_MEM_DF;
|
|
//crtHeapFlags |= _CRTDBG_CHECK_ALWAYS_DF;
|
|
crtHeapFlags |= _CRTDBG_LEAK_CHECK_DF;
|
|
_CrtSetDbgFlag(crtHeapFlags);
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
MemoryManager::~MemoryManager()
|
|
{
|
|
delete ms_criticalSection;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::verify(bool, bool)
|
|
{
|
|
#if defined(PLATFORM_WIN32) && defined(_DEBUG)
|
|
const int result = _CrtCheckMemory();
|
|
FATAL(result==FALSE, ("Heap corruption detected!\n"));
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
unsigned long MemoryManager::getCurrentNumberOfBytesAllocated(const int processId)
|
|
{
|
|
|
|
#ifdef _WIN32
|
|
UNREF(processId);
|
|
return 0;
|
|
#else
|
|
|
|
static char filename[30];
|
|
static FILE *inStatmFile;
|
|
static time_t seconds = 0;
|
|
|
|
unsigned long retVal = 0;
|
|
|
|
ms_criticalSection->enter();
|
|
|
|
if (processId)
|
|
{
|
|
if ((time (0) - seconds) > 60 )
|
|
{
|
|
if (seconds == 0)
|
|
{
|
|
snprintf(filename, sizeof(filename)-1, "/proc/%d/statm", processId);
|
|
filename[sizeof(filename)-1] = '\0';
|
|
s_pagesize = getpagesize();
|
|
}
|
|
|
|
seconds = time (0);
|
|
|
|
inStatmFile = fopen(filename,"rt");
|
|
|
|
if (inStatmFile)
|
|
{
|
|
if (fscanf (inStatmFile, "%d %d", &s_statm.tpsr, &s_statm.rss) == 2)
|
|
{
|
|
retVal = static_cast<unsigned long>(s_statm.tpsr) * static_cast<unsigned long>(s_pagesize);
|
|
}
|
|
else
|
|
{
|
|
retVal = 0;
|
|
}
|
|
fclose(inStatmFile);
|
|
}
|
|
else
|
|
{
|
|
retVal = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
retVal = static_cast<unsigned long>(s_statm.tpsr) * static_cast<unsigned long>(s_pagesize);
|
|
}
|
|
}
|
|
|
|
ms_criticalSection->leave();
|
|
|
|
return retVal;
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
unsigned long MemoryManager::getCurrentNumberOfBytesAllocatedNoLeakTest()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
unsigned long MemoryManager::getMaximumNumberOfBytesAllocated()
|
|
{
|
|
#ifdef _WIN32
|
|
return 0;
|
|
#else
|
|
return MemoryManagerNamespace::ms_maxBytesAllocated;
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManager::getSystemMemoryAllocatedMegabytes()
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManager::getCurrentNumberOfAllocations()
|
|
{
|
|
#ifdef _WIN32
|
|
return 0;
|
|
#else
|
|
return MemoryManagerNamespace::ms_allocations;
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManager::getMaximumNumberOfAllocations()
|
|
{
|
|
#ifdef _WIN32
|
|
return 0;
|
|
#else
|
|
return MemoryManagerNamespace::ms_maxAllocations;
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::setLimit(int, bool, bool)
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
int MemoryManager::getLimit()
|
|
{
|
|
return 2048;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::own(void * userPointer)
|
|
{
|
|
UNREF(userPointer);
|
|
// __asm int 3;
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::setReportAllocations(bool)
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::registerDebugFlags()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::report()
|
|
{
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void * MemoryManager::allocate(size_t size, uint32, bool, bool)
|
|
{
|
|
#ifdef _WIN32
|
|
return _malloc_dbg(size, _NORMAL_BLOCK, __FILE__, __LINE__);
|
|
//return (void *)LocalAlloc(LMEM_FIXED, size);
|
|
#else
|
|
return ::malloc(size);
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::free(void * userPointer, bool)
|
|
{
|
|
#ifdef _WIN32
|
|
_free_dbg(userPointer, _NORMAL_BLOCK);
|
|
//LocalFree((HLOCAL)userPointer);
|
|
#else
|
|
::free(userPointer);
|
|
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void *MemoryManager::reallocate(void *userPointer, size_t newSize)
|
|
{
|
|
#ifdef _WIN32
|
|
return _realloc_dbg(userPointer, newSize, _NORMAL_BLOCK, __FILE__, __LINE__);
|
|
//return (void *)LocalReAlloc((HLOCAL)userPointer, newSize, 0);
|
|
#else
|
|
return ::realloc(userPointer, newSize);
|
|
#endif
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::debugReport(void)
|
|
{
|
|
|
|
}
|
|
|
|
// ----------------------------------------------------------------------
|
|
|
|
void MemoryManager::debugReportMap(void)
|
|
{
|
|
|
|
}
|
|
#endif
|
|
|
|
// ----------------------------------------------------------------------
|
|
// this function is available regardless of the DISABLE_MEMORY_MANAGER setting
|
|
|
|
#ifndef _WIN32
|
|
int MemoryManager::getProcessVmSizeKBytes(const int processId)
|
|
{
|
|
static char filename[30];
|
|
static FILE *inStatusFile;
|
|
static char buffer[128];
|
|
static time_t seconds = 0;
|
|
|
|
ms_criticalSection->enter();
|
|
|
|
if (processId)
|
|
{
|
|
if ((time (0) - seconds) > 60)
|
|
{
|
|
MemoryManagerNamespace::ms_processVmSizeKBytes = 0;
|
|
|
|
if (seconds == 0)
|
|
{
|
|
snprintf(filename, sizeof(filename)-1, "/proc/%d/status", processId);
|
|
filename[sizeof(filename)-1] = '\0';
|
|
}
|
|
|
|
seconds = time(0);
|
|
|
|
inStatusFile = fopen(filename,"rt");
|
|
|
|
if (inStatusFile)
|
|
{
|
|
int temp;
|
|
|
|
while (fgets(buffer, sizeof(buffer), inStatusFile))
|
|
{
|
|
if (1 == sscanf(buffer, "VmSize: %d", &temp))
|
|
{
|
|
MemoryManagerNamespace::ms_processVmSizeKBytes = temp;
|
|
break;
|
|
}
|
|
}
|
|
|
|
fclose(inStatusFile);
|
|
}
|
|
}
|
|
}
|
|
|
|
ms_criticalSection->leave();
|
|
|
|
return MemoryManagerNamespace::ms_processVmSizeKBytes;
|
|
}
|
|
#endif
|
|
|
|
// ======================================================================
|