678 lines
24 KiB
C++
678 lines
24 KiB
C++
//========= Copyright Valve Corporation, All rights reserved. ============//
|
|
//
|
|
// Purpose: This header should never be used directly from leaf code!!!
|
|
// Instead, just add the file memoverride.cpp into your project and all this
|
|
// will automagically be used
|
|
//
|
|
// $NoKeywords: $
|
|
//=============================================================================//
|
|
|
|
#ifndef TIER0_MEMALLOC_H
|
|
#define TIER0_MEMALLOC_H
|
|
|
|
#ifdef _WIN32
|
|
#pragma once
|
|
#endif
|
|
|
|
// These memory debugging switches aren't relevant under Linux builds since
|
|
// memoverride.cpp isn't built into Linux projects
|
|
#ifndef POSIX
|
|
// Define this in release to get memory tracking even in release builds
|
|
//#define USE_MEM_DEBUG 1
|
|
#endif
|
|
|
|
#if defined(_MEMTEST)
|
|
#ifdef _WIN32
|
|
#define USE_MEM_DEBUG 1
|
|
#endif
|
|
#endif
|
|
|
|
// Undefine this if using a compiler lacking threadsafe RTTI (like vc6)
|
|
#define MEM_DEBUG_CLASSNAME 1
|
|
|
|
#include <stddef.h>
|
|
#if defined(OSX)
|
|
#include <malloc/malloc.h>
|
|
#endif
|
|
|
|
#include "mem.h"
|
|
|
|
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
|
|
|
struct _CrtMemState;
|
|
|
|
#define MEMALLOC_VERSION 1
|
|
|
|
typedef size_t (*MemAllocFailHandler_t)(size_t);
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// NOTE! This should never be called directly from leaf code
|
|
// Just use new,delete,malloc,free etc. They will call into this eventually
|
|
//-----------------------------------------------------------------------------
|
|
abstract_class IMemAlloc {
|
|
public:
|
|
// Release versions
|
|
virtual void *Alloc(size_t nSize) = 0;
|
|
virtual void *Realloc(void *pMem, size_t nSize) = 0;
|
|
virtual void Free(void *pMem) = 0;
|
|
virtual void *Expand_NoLongerSupported(void *pMem, size_t nSize) = 0;
|
|
|
|
// Debug versions
|
|
virtual void *Alloc(size_t nSize, const char *pFileName, int nLine) = 0;
|
|
virtual void *Realloc(void *pMem, size_t nSize, const char *pFileName,
|
|
int nLine) = 0;
|
|
virtual void Free(void *pMem, const char *pFileName, int nLine) = 0;
|
|
virtual void *Expand_NoLongerSupported(
|
|
void *pMem, size_t nSize, const char *pFileName, int nLine) = 0;
|
|
|
|
// Returns size of a particular allocation
|
|
virtual size_t GetSize(void *pMem) = 0;
|
|
|
|
// Force file + line information for an allocation
|
|
virtual void PushAllocDbgInfo(const char *pFileName, int nLine) = 0;
|
|
virtual void PopAllocDbgInfo() = 0;
|
|
|
|
// FIXME: Remove when we have our own allocator
|
|
// these methods of the Crt debug code is used in our codebase currently
|
|
virtual long CrtSetBreakAlloc(long lNewBreakAlloc) = 0;
|
|
virtual int CrtSetReportMode(int nReportType, int nReportMode) = 0;
|
|
virtual int CrtIsValidHeapPointer(const void *pMem) = 0;
|
|
virtual int CrtIsValidPointer(const void *pMem, unsigned int size,
|
|
int access) = 0;
|
|
virtual int CrtCheckMemory(void) = 0;
|
|
virtual int CrtSetDbgFlag(int nNewFlag) = 0;
|
|
virtual void CrtMemCheckpoint(_CrtMemState * pState) = 0;
|
|
|
|
// FIXME: Make a better stats interface
|
|
virtual void DumpStats() = 0;
|
|
virtual void DumpStatsFileBase(char const *pchFileBase) = 0;
|
|
|
|
// FIXME: Remove when we have our own allocator
|
|
virtual void *CrtSetReportFile(int nRptType, void *hFile) = 0;
|
|
virtual void *CrtSetReportHook(void *pfnNewHook) = 0;
|
|
virtual int CrtDbgReport(int nRptType, const char *szFile, int nLine,
|
|
const char *szModule, const char *pMsg) = 0;
|
|
|
|
virtual int heapchk() = 0;
|
|
|
|
virtual bool IsDebugHeap() = 0;
|
|
|
|
virtual void GetActualDbgInfo(const char *&pFileName, int &nLine) = 0;
|
|
virtual void RegisterAllocation(const char *pFileName, int nLine,
|
|
int nLogicalSize, int nActualSize,
|
|
unsigned nTime) = 0;
|
|
virtual void RegisterDeallocation(const char *pFileName, int nLine,
|
|
int nLogicalSize, int nActualSize,
|
|
unsigned nTime) = 0;
|
|
|
|
virtual int GetVersion() = 0;
|
|
|
|
virtual void CompactHeap() = 0;
|
|
|
|
// Function called when malloc fails or memory limits hit to attempt to free
|
|
// up memory (can come in any thread)
|
|
virtual MemAllocFailHandler_t SetAllocFailHandler(
|
|
MemAllocFailHandler_t pfnMemAllocFailHandler) = 0;
|
|
|
|
virtual void DumpBlockStats(void *) = 0;
|
|
|
|
#if defined(_MEMTEST)
|
|
virtual void SetStatsExtraInfo(const char *pMapName,
|
|
const char *pComment) = 0;
|
|
#endif
|
|
|
|
// Returns 0 if no failure, otherwise the size_t of the last requested chunk
|
|
// "I'm sure this is completely thread safe!" Brian Deen 7/19/2012.
|
|
virtual size_t MemoryAllocFailed() = 0;
|
|
|
|
// handles storing allocation info for coroutines
|
|
virtual uint32 GetDebugInfoSize() = 0;
|
|
virtual void SaveDebugInfo(void *pvDebugInfo) = 0;
|
|
virtual void RestoreDebugInfo(const void *pvDebugInfo) = 0;
|
|
virtual void InitDebugInfo(void *pvDebugInfo, const char *pchRootFileName,
|
|
int nLine) = 0;
|
|
|
|
// Replacement for ::GlobalMemoryStatus which accounts for unused memory in
|
|
// our system
|
|
virtual void GlobalMemoryStatus(size_t * pUsedMemory,
|
|
size_t * pFreeMemory) = 0;
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Singleton interface
|
|
//-----------------------------------------------------------------------------
|
|
MEM_INTERFACE IMemAlloc *g_pMemAlloc;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef MEMALLOC_REGIONS
|
|
#ifndef MEMALLOC_REGION
|
|
#define MEMALLOC_REGION 0
|
|
#endif
|
|
inline void *MemAlloc_Alloc(size_t nSize) {
|
|
return g_pMemAlloc->RegionAlloc(MEMALLOC_REGION, nSize);
|
|
}
|
|
|
|
inline void *MemAlloc_Alloc(size_t nSize, const char *pFileName, int nLine) {
|
|
return g_pMemAlloc->RegionAlloc(MEMALLOC_REGION, nSize, pFileName, nLine);
|
|
}
|
|
#else
|
|
#undef MEMALLOC_REGION
|
|
inline void *MemAlloc_Alloc(size_t nSize) { return g_pMemAlloc->Alloc(nSize); }
|
|
|
|
inline void *MemAlloc_Alloc(size_t nSize, const char *pFileName, int nLine) {
|
|
return g_pMemAlloc->Alloc(nSize, pFileName, nLine);
|
|
}
|
|
#endif
|
|
inline void MemAlloc_Free(void *ptr) { g_pMemAlloc->Free(ptr); }
|
|
inline void MemAlloc_Free(void *ptr, const char *pFileName, int nLine) {
|
|
g_pMemAlloc->Free(ptr, pFileName, nLine);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
inline bool ValueIsPowerOfTwo(
|
|
size_t value) // don't clash with mathlib definition
|
|
{
|
|
return (value & (value - 1)) == 0;
|
|
}
|
|
|
|
inline void *MemAlloc_AllocAligned(size_t size, size_t align) {
|
|
unsigned char *pAlloc, *pResult;
|
|
|
|
if (!IsPowerOfTwo(align)) return NULL;
|
|
|
|
align = (align > sizeof(void *) ? align : sizeof(void *)) - 1;
|
|
|
|
if ((pAlloc = (unsigned char *)g_pMemAlloc->Alloc(
|
|
sizeof(void *) + align + size)) == (unsigned char *)NULL)
|
|
return NULL;
|
|
|
|
pResult =
|
|
(unsigned char *)((size_t)(pAlloc + sizeof(void *) + align) & ~align);
|
|
((unsigned char **)(pResult))[-1] = pAlloc;
|
|
|
|
return (void *)pResult;
|
|
}
|
|
|
|
inline void *MemAlloc_AllocAligned(size_t size, size_t align,
|
|
const char *pszFile, int nLine) {
|
|
unsigned char *pAlloc, *pResult;
|
|
|
|
if (!IsPowerOfTwo(align)) return NULL;
|
|
|
|
align = (align > sizeof(void *) ? align : sizeof(void *)) - 1;
|
|
|
|
if ((pAlloc = (unsigned char *)g_pMemAlloc->Alloc(
|
|
sizeof(void *) + align + size, pszFile, nLine)) ==
|
|
(unsigned char *)NULL)
|
|
return NULL;
|
|
|
|
pResult =
|
|
(unsigned char *)((size_t)(pAlloc + sizeof(void *) + align) & ~align);
|
|
((unsigned char **)(pResult))[-1] = pAlloc;
|
|
|
|
return (void *)pResult;
|
|
}
|
|
|
|
inline void *MemAlloc_AllocAlignedUnattributed(size_t size, size_t align) {
|
|
unsigned char *pAlloc, *pResult;
|
|
|
|
if (!ValueIsPowerOfTwo(align)) return NULL;
|
|
|
|
align = (align > sizeof(void *) ? align : sizeof(void *)) - 1;
|
|
|
|
if ((pAlloc = (unsigned char *)MemAlloc_Alloc(
|
|
sizeof(void *) + align + size)) == (unsigned char *)NULL)
|
|
return NULL;
|
|
|
|
pResult =
|
|
(unsigned char *)((size_t)(pAlloc + sizeof(void *) + align) & ~align);
|
|
((unsigned char **)(pResult))[-1] = pAlloc;
|
|
|
|
return (void *)pResult;
|
|
}
|
|
|
|
inline void *MemAlloc_AllocAlignedFileLine(size_t size, size_t align,
|
|
const char *pszFile, int nLine) {
|
|
unsigned char *pAlloc, *pResult;
|
|
|
|
if (!ValueIsPowerOfTwo(align)) return NULL;
|
|
|
|
align = (align > sizeof(void *) ? align : sizeof(void *)) - 1;
|
|
|
|
if ((pAlloc = (unsigned char *)MemAlloc_Alloc(sizeof(void *) + align + size,
|
|
pszFile, nLine)) ==
|
|
(unsigned char *)NULL)
|
|
return NULL;
|
|
|
|
pResult =
|
|
(unsigned char *)((size_t)(pAlloc + sizeof(void *) + align) & ~align);
|
|
((unsigned char **)(pResult))[-1] = pAlloc;
|
|
|
|
return (void *)pResult;
|
|
}
|
|
|
|
inline void *MemAlloc_ReallocAligned(void *ptr, size_t size, size_t align) {
|
|
if (!IsPowerOfTwo(align)) return NULL;
|
|
|
|
// Don't change alignment between allocation + reallocation.
|
|
if (((size_t)ptr & (align - 1)) != 0) return NULL;
|
|
|
|
if (!ptr) return MemAlloc_AllocAligned(size, align);
|
|
|
|
void *pAlloc, *pResult;
|
|
|
|
// Figure out the actual allocation point
|
|
pAlloc = ptr;
|
|
pAlloc =
|
|
(void *)(((size_t)pAlloc & ~(sizeof(void *) - 1)) - sizeof(void *));
|
|
pAlloc = *((void **)pAlloc);
|
|
|
|
// See if we have enough space
|
|
size_t nOffset = (size_t)ptr - (size_t)pAlloc;
|
|
size_t nOldSize = g_pMemAlloc->GetSize(pAlloc);
|
|
if (nOldSize >= size + nOffset) return ptr;
|
|
|
|
pResult = MemAlloc_AllocAligned(size, align);
|
|
memcpy(pResult, ptr, nOldSize - nOffset);
|
|
g_pMemAlloc->Free(pAlloc);
|
|
return pResult;
|
|
}
|
|
|
|
inline void MemAlloc_FreeAligned(void *pMemBlock) {
|
|
void *pAlloc;
|
|
|
|
if (pMemBlock == NULL) return;
|
|
|
|
pAlloc = pMemBlock;
|
|
|
|
// pAlloc points to the pointer to starting of the memory block
|
|
pAlloc =
|
|
(void *)(((size_t)pAlloc & ~(sizeof(void *) - 1)) - sizeof(void *));
|
|
|
|
// pAlloc is the pointer to the start of memory block
|
|
pAlloc = *((void **)pAlloc);
|
|
g_pMemAlloc->Free(pAlloc);
|
|
}
|
|
|
|
inline void MemAlloc_FreeAligned(void *pMemBlock, const char *pFileName,
|
|
int nLine) {
|
|
void *pAlloc;
|
|
|
|
if (pMemBlock == NULL) return;
|
|
|
|
pAlloc = pMemBlock;
|
|
|
|
// pAlloc points to the pointer to starting of the memory block
|
|
pAlloc =
|
|
(void *)(((size_t)pAlloc & ~(sizeof(void *) - 1)) - sizeof(void *));
|
|
|
|
// pAlloc is the pointer to the start of memory block
|
|
pAlloc = *((void **)pAlloc);
|
|
g_pMemAlloc->Free(pAlloc, pFileName, nLine);
|
|
}
|
|
|
|
inline size_t MemAlloc_GetSizeAligned(void *pMemBlock) {
|
|
void *pAlloc;
|
|
|
|
if (pMemBlock == NULL) return 0;
|
|
|
|
pAlloc = pMemBlock;
|
|
|
|
// pAlloc points to the pointer to starting of the memory block
|
|
pAlloc =
|
|
(void *)(((size_t)pAlloc & ~(sizeof(void *) - 1)) - sizeof(void *));
|
|
|
|
// pAlloc is the pointer to the start of memory block
|
|
pAlloc = *((void **)pAlloc);
|
|
return g_pMemAlloc->GetSize(pAlloc) - ((byte *)pMemBlock - (byte *)pAlloc);
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
|
|
#define MEM_ALLOC_CREDIT_(tag) \
|
|
CMemAllocAttributeAlloction memAllocAttributeAlloction(tag, __LINE__)
|
|
#define MemAlloc_PushAllocDbgInfo(pszFile, line) \
|
|
g_pMemAlloc->PushAllocDbgInfo(pszFile, line)
|
|
#define MemAlloc_PopAllocDbgInfo() g_pMemAlloc->PopAllocDbgInfo()
|
|
#define MemAlloc_RegisterAllocation(pFileName, nLine, nLogicalSize, \
|
|
nActualSize, nTime) \
|
|
g_pMemAlloc->RegisterAllocation(pFileName, nLine, nLogicalSize, \
|
|
nActualSize, nTime)
|
|
#define MemAlloc_RegisterDeallocation(pFileName, nLine, nLogicalSize, \
|
|
nActualSize, nTime) \
|
|
g_pMemAlloc->RegisterDeallocation(pFileName, nLine, nLogicalSize, \
|
|
nActualSize, nTime)
|
|
#else
|
|
#define MEM_ALLOC_CREDIT_(tag) ((void)0)
|
|
#define MemAlloc_PushAllocDbgInfo(pszFile, line) ((void)0)
|
|
#define MemAlloc_PopAllocDbgInfo() ((void)0)
|
|
#define MemAlloc_RegisterAllocation(pFileName, nLine, nLogicalSize, \
|
|
nActualSize, nTime) \
|
|
((void)0)
|
|
#define MemAlloc_RegisterDeallocation(pFileName, nLine, nLogicalSize, \
|
|
nActualSize, nTime) \
|
|
((void)0)
|
|
#endif
|
|
|
|
#define MemAlloc_DumpStats() g_pMemAlloc->DumpStats()
|
|
#define MemAlloc_CompactHeap() g_pMemAlloc->CompactHeap()
|
|
#define MemAlloc_OutOfMemory() g_pMemAlloc->OutOfMemory()
|
|
#define MemAlloc_CompactIncremental() g_pMemAlloc->CompactIncremental()
|
|
#define MemAlloc_DumpStatsFileBase(_filename) \
|
|
g_pMemAlloc->DumpStatsFileBase(_filename)
|
|
#define MemAlloc_CrtCheckMemory() g_pMemAlloc->CrtCheckMemory()
|
|
#define MemAlloc_GlobalMemoryStatus(_usedMemory, _freeMemory) \
|
|
g_pMemAlloc->GlobalMemoryStatus(_usedMemory, _freeMemory)
|
|
#define MemAlloc_MemoryAllocFailed() g_pMemAlloc->MemoryAllocFailed()
|
|
|
|
#define MemAlloc_GetDebugInfoSize() g_pMemAlloc->GetDebugInfoSize()
|
|
#define MemAlloc_SaveDebugInfo(pvDebugInfo) \
|
|
g_pMemAlloc->SaveDebugInfo(pvDebugInfo)
|
|
#define MemAlloc_RestoreDebugInfo(pvDebugInfo) \
|
|
g_pMemAlloc->RestoreDebugInfo(pvDebugInfo)
|
|
#define MemAlloc_InitDebugInfo(pvDebugInfo, pchRootFileName, nLine) \
|
|
g_pMemAlloc->InitDebugInfo(pvDebugInfo, pchRootFileName, nLine)
|
|
#define MemAlloc_GetSize(x) g_pMemAlloc->GetSize(x);
|
|
//-----------------------------------------------------------------------------
|
|
|
|
class CMemAllocAttributeAlloction {
|
|
public:
|
|
CMemAllocAttributeAlloction(const char *pszFile, int line) {
|
|
MemAlloc_PushAllocDbgInfo(pszFile, line);
|
|
}
|
|
|
|
~CMemAllocAttributeAlloction() { MemAlloc_PopAllocDbgInfo(); }
|
|
};
|
|
|
|
#define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__)
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if defined(_WIN32) && (defined(_DEBUG) || defined(USE_MEM_DEBUG))
|
|
|
|
#pragma warning(disable : 4290)
|
|
#pragma warning(push)
|
|
#include <typeinfo.h>
|
|
|
|
// MEM_DEBUG_CLASSNAME is opt-in.
|
|
// Note: typeid().name() is not threadsafe, so if the project needs to access it
|
|
// in multiple threads simultaneously, it'll need a mutex.
|
|
#if defined(_CPPRTTI) && defined(MEM_DEBUG_CLASSNAME)
|
|
#define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_(typeid(*this).name())
|
|
#define MEM_ALLOC_CLASSNAME(type) (typeid((type *)(0)).name())
|
|
#else
|
|
#define MEM_ALLOC_CREDIT_CLASS() MEM_ALLOC_CREDIT_(__FILE__)
|
|
#define MEM_ALLOC_CLASSNAME(type) (__FILE__)
|
|
#endif
|
|
|
|
// MEM_ALLOC_CREDIT_FUNCTION is used when no this pointer is available ( inside
|
|
// 'new' overloads, for example )
|
|
#ifdef _MSC_VER
|
|
#define MEM_ALLOC_CREDIT_FUNCTION() MEM_ALLOC_CREDIT_(__FUNCTION__)
|
|
#else
|
|
#define MEM_ALLOC_CREDIT_FUNCTION() (__FILE__)
|
|
#endif
|
|
|
|
#pragma warning(pop)
|
|
#else
|
|
#define MEM_ALLOC_CREDIT_CLASS()
|
|
#define MEM_ALLOC_CLASSNAME(type) NULL
|
|
#define MEM_ALLOC_CREDIT_FUNCTION()
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
|
|
struct MemAllocFileLine_t {
|
|
const char *pszFile;
|
|
int line;
|
|
};
|
|
|
|
#define MEMALLOC_DEFINE_EXTERNAL_TRACKING(tag) \
|
|
static CUtlMap<void *, MemAllocFileLine_t, int> g_##tag##Allocs( \
|
|
DefLessFunc(void *)); \
|
|
static const char *g_psz##tag##Alloc = \
|
|
strcpy((char *)g_pMemAlloc->Alloc(strlen(#tag "Alloc") + 1, \
|
|
"intentional leak", 0), \
|
|
#tag "Alloc");
|
|
|
|
#define MemAlloc_RegisterExternalAllocation(tag, p, size) \
|
|
if (!p) \
|
|
; \
|
|
else { \
|
|
MemAllocFileLine_t fileLine = {g_psz##tag##Alloc, 0}; \
|
|
g_pMemAlloc->GetActualDbgInfo(fileLine.pszFile, fileLine.line); \
|
|
if (fileLine.pszFile != g_psz##tag##Alloc) { \
|
|
g_##tag##Allocs.Insert(p, fileLine); \
|
|
} \
|
|
\
|
|
MemAlloc_RegisterAllocation(fileLine.pszFile, fileLine.line, size, \
|
|
size, 0); \
|
|
}
|
|
|
|
#define MemAlloc_RegisterExternalDeallocation(tag, p, size) \
|
|
if (!p) \
|
|
; \
|
|
else { \
|
|
MemAllocFileLine_t fileLine = {g_psz##tag##Alloc, 0}; \
|
|
CUtlMap<void *, MemAllocFileLine_t, int>::IndexType_t \
|
|
iRecordedFileLine = g_##tag##Allocs.Find(p); \
|
|
if (iRecordedFileLine != g_##tag##Allocs.InvalidIndex()) { \
|
|
fileLine = g_##tag##Allocs[iRecordedFileLine]; \
|
|
g_##tag##Allocs.RemoveAt(iRecordedFileLine); \
|
|
} \
|
|
\
|
|
MemAlloc_RegisterDeallocation(fileLine.pszFile, fileLine.line, size, \
|
|
size, 0); \
|
|
}
|
|
|
|
#else
|
|
|
|
#define MEMALLOC_DEFINE_EXTERNAL_TRACKING(tag)
|
|
#define MemAlloc_RegisterExternalAllocation(tag, p, size) ((void)0)
|
|
#define MemAlloc_RegisterExternalDeallocation(tag, p, size) ((void)0)
|
|
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#elif defined(POSIX)
|
|
|
|
#if defined(OSX)
|
|
// Mac always aligns allocs, don't need to call posix_memalign which doesn't
|
|
// exist in 10.5.8 which TF2 still needs to run on
|
|
// inline void *memalign(size_t alignment, size_t size) {void *pTmp=NULL;
|
|
// posix_memalign(&pTmp, alignment, size); return pTmp;}
|
|
inline void *memalign(size_t alignment, size_t size) {
|
|
void *pTmp = NULL;
|
|
pTmp = malloc(size);
|
|
return pTmp;
|
|
}
|
|
#endif
|
|
|
|
inline void *_aligned_malloc(size_t nSize, size_t align) {
|
|
return memalign(align, nSize);
|
|
}
|
|
inline void _aligned_free(void *ptr) { free(ptr); }
|
|
|
|
inline void *MemAlloc_Alloc(size_t nSize, const char *pFileName = NULL,
|
|
int nLine = 0) {
|
|
return malloc(nSize);
|
|
}
|
|
inline void MemAlloc_Free(void *ptr, const char *pFileName = NULL,
|
|
int nLine = 0) {
|
|
free(ptr);
|
|
}
|
|
|
|
inline void *MemAlloc_AllocAligned(size_t size, size_t align,
|
|
const char *pszFile = NULL, int nLine = 0) {
|
|
return memalign(align, size);
|
|
}
|
|
inline void *MemAlloc_AllocAlignedFileLine(size_t size, size_t align,
|
|
const char *pszFile = NULL,
|
|
int nLine = 0) {
|
|
return memalign(align, size);
|
|
}
|
|
inline void MemAlloc_FreeAligned(void *pMemBlock, const char *pszFile = NULL,
|
|
int nLine = 0) {
|
|
free(pMemBlock);
|
|
}
|
|
|
|
#if defined(OSX)
|
|
inline size_t _msize(void *ptr) { return malloc_size(ptr); }
|
|
#else
|
|
inline size_t _msize(void *ptr) { return malloc_usable_size(ptr); }
|
|
#endif
|
|
|
|
inline void *MemAlloc_ReallocAligned(void *ptr, size_t size, size_t align) {
|
|
void *ptr_new_aligned = memalign(align, size);
|
|
|
|
if (ptr_new_aligned) {
|
|
size_t old_size = _msize(ptr);
|
|
size_t copy_size = (size < old_size) ? size : old_size;
|
|
|
|
memcpy(ptr_new_aligned, ptr, copy_size);
|
|
free(ptr);
|
|
}
|
|
|
|
return ptr_new_aligned;
|
|
}
|
|
#else
|
|
#define MemAlloc_GetDebugInfoSize() g_pMemAlloc->GetDebugInfoSize()
|
|
#define MemAlloc_SaveDebugInfo(pvDebugInfo) \
|
|
g_pMemAlloc->SaveDebugInfo(pvDebugInfo)
|
|
#define MemAlloc_RestoreDebugInfo(pvDebugInfo) \
|
|
g_pMemAlloc->RestoreDebugInfo(pvDebugInfo)
|
|
#define MemAlloc_InitDebugInfo(pvDebugInfo, pchRootFileName, nLine) \
|
|
g_pMemAlloc->InitDebugInfo(pvDebugInfo, pchRootFileName, nLine)
|
|
|
|
#endif // !STEAM && !NO_MALLOC_OVERRIDE
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if !defined(STEAM) && defined(NO_MALLOC_OVERRIDE)
|
|
|
|
#define MEM_ALLOC_CREDIT_(tag) ((void)0)
|
|
#define MEM_ALLOC_CREDIT() MEM_ALLOC_CREDIT_(__FILE__)
|
|
#define MEM_ALLOC_CREDIT_FUNCTION()
|
|
#define MEM_ALLOC_CREDIT_CLASS()
|
|
#define MEM_ALLOC_CLASSNAME(type) NULL
|
|
|
|
#define MemAlloc_PushAllocDbgInfo(pszFile, line)
|
|
#define MemAlloc_PopAllocDbgInfo()
|
|
#define MemAlloc_RegisterAllocation(pFileName, nLine, nLogicalSize, \
|
|
nActualSize, nTime) \
|
|
((void)0)
|
|
#define MemAlloc_RegisterDeallocation(pFileName, nLine, nLogicalSize, \
|
|
nActualSize, nTime) \
|
|
((void)0)
|
|
#define MemAlloc_DumpStats() ((void)0)
|
|
#define MemAlloc_CompactHeap() ((void)0)
|
|
#define MemAlloc_OutOfMemory() ((void)0)
|
|
#define MemAlloc_CompactIncremental() ((void)0)
|
|
#define MemAlloc_DumpStatsFileBase(_filename) ((void)0)
|
|
inline bool MemAlloc_CrtCheckMemory() { return true; }
|
|
inline void MemAlloc_GlobalMemoryStatus(size_t *pusedMemory,
|
|
size_t *pfreeMemory) {
|
|
*pusedMemory = 0;
|
|
*pfreeMemory = 0;
|
|
}
|
|
#define MemAlloc_MemoryAllocFailed() 0
|
|
|
|
#define MemAlloc_GetDebugInfoSize() 0
|
|
#define MemAlloc_SaveDebugInfo(pvDebugInfo) ((void)0)
|
|
#define MemAlloc_RestoreDebugInfo(pvDebugInfo) ((void)0)
|
|
#define MemAlloc_InitDebugInfo(pvDebugInfo, pchRootFileName, nLine) ((void)0)
|
|
|
|
#define MEMALLOC_DEFINE_EXTERNAL_TRACKING(tag)
|
|
#define MemAlloc_RegisterExternalAllocation(tag, p, size) ((void)0)
|
|
#define MemAlloc_RegisterExternalDeallocation(tag, p, size) ((void)0)
|
|
|
|
#endif // !STEAM && NO_MALLOC_OVERRIDE
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// linux memory tracking via hooks.
|
|
#if defined(POSIX) && !defined(NO_HOOK_MALLOC)
|
|
PLATFORM_INTERFACE void MemoryLogMessage(
|
|
char const *s); // throw a message into the memory log
|
|
PLATFORM_INTERFACE void EnableMemoryLogging(bool bOnOff);
|
|
PLATFORM_INTERFACE void DumpMemoryLog(int nThresh);
|
|
PLATFORM_INTERFACE void DumpMemorySummary(void);
|
|
PLATFORM_INTERFACE void SetMemoryMark(void);
|
|
PLATFORM_INTERFACE void DumpChangedMemory(int nThresh);
|
|
|
|
#else
|
|
FORCEINLINE void MemoryLogMessage(char const *s) {}
|
|
|
|
FORCEINLINE void EnableMemoryLogging(bool bOnOff) {}
|
|
FORCEINLINE void DumpMemoryLog(int nThresh) {}
|
|
FORCEINLINE void DumpMemorySummary(void) {}
|
|
FORCEINLINE void SetMemoryMark(void) {}
|
|
FORCEINLINE void DumpChangedMemory(int nThresh) {}
|
|
|
|
#endif
|
|
|
|
#ifdef POSIX
|
|
// ApproximateProcessMemoryUsage returns the approximate memory footprint of
|
|
// this process.
|
|
PLATFORM_INTERFACE size_t ApproximateProcessMemoryUsage(void);
|
|
#else
|
|
FORCEINLINE size_t ApproximateProcessMemoryUsage(void) { return 0; }
|
|
|
|
#endif
|
|
|
|
struct aligned_tmp_t {
|
|
// empty base class
|
|
};
|
|
|
|
/*
|
|
This class used to be required if you wanted an object to be allocated with a
|
|
specific alignment. ALIGN16 and ALIGN16_POST are not actually sufficient for
|
|
this because they guarantee that the globals, statics, locals, and function
|
|
parameters are appropriately aligned they do not affect memory allocation
|
|
alignment. However this class is usually not needed because as of 2012 our
|
|
policy is that our allocator should take care of this automatically. Any object
|
|
whose size is a multiple of 16 will be 16-byte aligned. Existing uses of this
|
|
class were not changed because the cost/benefit did not justify it.
|
|
*/
|
|
// template here to allow adding alignment at levels of hierarchy that aren't
|
|
// the base
|
|
template <int bytesAlignment = 16, class T = aligned_tmp_t>
|
|
class CAlignedNewDelete : public T {
|
|
public:
|
|
/*
|
|
Note that this class does not overload operator new[] and delete[] which
|
|
means that classes that depend on this for alignment may end up misaligned
|
|
if an array is allocated. This problem is now mostly theoretical because
|
|
this class is mostly obsolete.
|
|
*/
|
|
void *operator new(size_t nSize) {
|
|
return MemAlloc_AllocAligned(nSize, bytesAlignment);
|
|
}
|
|
|
|
void *operator new(size_t nSize, int nBlockUse, const char *pFileName,
|
|
int nLine) {
|
|
return MemAlloc_AllocAlignedFileLine(nSize, bytesAlignment, pFileName,
|
|
nLine);
|
|
}
|
|
|
|
void operator delete(void *pData) {
|
|
if (pData) {
|
|
MemAlloc_FreeAligned(pData);
|
|
}
|
|
}
|
|
|
|
void operator delete(void *pData, int nBlockUse, const char *pFileName,
|
|
int nLine) {
|
|
if (pData) {
|
|
MemAlloc_FreeAligned(pData, pFileName, nLine);
|
|
}
|
|
}
|
|
};
|
|
|
|
#endif /* TIER0_MEMALLOC_H */
|