You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2812 lines
80 KiB
2812 lines
80 KiB
//===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
|
|
//
|
|
// Purpose: Memory allocation!
|
|
//
|
|
// $NoKeywords: $
|
|
//===========================================================================//
|
|
|
|
|
|
#include "pch_tier0.h"
|
|
|
|
#if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|
|
|
|
//#include <malloc.h>
|
|
#include <string.h>
|
|
#include "tier0/dbg.h"
|
|
#include "tier0/stackstats.h"
|
|
#include "tier0/memalloc.h"
|
|
#include "tier0/fasttimer.h"
|
|
#include "mem_helpers.h"
|
|
#ifdef PLATFORM_WINDOWS_PC
|
|
#undef WIN32_LEAN_AND_MEAN
|
|
#include <windows.h>
|
|
#include <crtdbg.h>
|
|
#include <errno.h>
|
|
#include <io.h>
|
|
#endif
|
|
#ifdef OSX
|
|
#include <malloc/malloc.h>
|
|
#include <stdlib.h>
|
|
#endif
|
|
|
|
#include <map>
|
|
#include <set>
|
|
#include <limits.h>
|
|
#include "tier0/threadtools.h"
|
|
#ifdef _X360
|
|
#include "xbox/xbox_console.h"
|
|
#endif
|
|
|
|
#ifdef _PS3
|
|
#include "sys/memory.h"
|
|
#include "tls_ps3.h"
|
|
#include "ps3/ps3_helpers.h"
|
|
#include "memoverride_ps3.h"
|
|
#endif
|
|
|
|
#ifdef USE_LIGHT_MEM_DEBUG
|
|
#undef USE_MEM_DEBUG
|
|
#endif
|
|
|
|
#if (!defined( POSIX ) && (defined(_DEBUG) || defined(USE_MEM_DEBUG)))
|
|
#pragma message ("USE_MEM_DEBUG is enabled in a release build. Don't check this in!")
|
|
#endif
|
|
|
|
#include "mem_impl_type.h"
|
|
|
|
#if MEM_IMPL_TYPE_DBG
|
|
|
|
#if defined(_WIN32) && ( !defined(_X360) && !defined(_WIN64) )
|
|
//be sure to disable frame pointer omission for all projects. "vpc /nofpo" when using stack traces
|
|
//#define USE_STACK_TRACES
|
|
// or:
|
|
//#define USE_STACK_TRACES_DETAILED
|
|
const size_t STACK_TRACE_LENGTH = 32;
|
|
#endif
|
|
|
|
//prevent stupid bugs from checking one and not the other
|
|
#if defined( USE_STACK_TRACES_DETAILED ) && !defined( USE_STACK_TRACES )
|
|
#define USE_STACK_TRACES //don't comment me. I'm a safety check
|
|
#endif
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
#define SORT_STACK_TRACE_DESCRIPTION_DUMPS
|
|
#endif
|
|
|
|
#if (defined( USE_STACK_TRACES )) && !(defined( TIER0_FPO_DISABLED ) || defined( _DEBUG ))
|
|
#error Stack traces will not work unless FPO is disabled for every function traced through. Rebuild everything with FPO disabled "vpc /nofpo"
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#ifdef _PS3
|
|
MemOverrideRawCrtFunctions_t *g_pMemOverrideRawCrtFns;
|
|
#define DebugAlloc (g_pMemOverrideRawCrtFns->pfn_malloc)
|
|
#define DebugFree (g_pMemOverrideRawCrtFns->pfn_free)
|
|
#elif defined( _X360 )
|
|
#define DebugAlloc DmAllocatePool
|
|
#define DebugFree DmFreePool
|
|
#else
|
|
#define DebugAlloc malloc
|
|
#define DebugFree free
|
|
#endif
|
|
|
|
#ifdef _WIN32
|
|
int g_DefaultHeapFlags = _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_ALLOC_MEM_DF );
|
|
#else
|
|
int g_DefaultHeapFlags = 0;
|
|
#endif // win32
|
|
|
|
#if defined( _MEMTEST )
|
|
static char s_szStatsMapName[32];
|
|
static char s_szStatsComment[256];
|
|
#endif
|
|
|
|
#pragma optimize( "", off )
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
|
|
bool GetModuleFromAddress( void *address, char *pResult, int iLength )
|
|
{
|
|
return GetModuleNameFromAddress( address, pResult, iLength );
|
|
}
|
|
|
|
bool GetCallerModule( char *pDest, int iLength )
|
|
{
|
|
void *pCaller;
|
|
GetCallStack_Fast( &pCaller, 1, 2 );
|
|
|
|
return ( pCaller != 0 && GetModuleFromAddress( pCaller, pDest, iLength ) );
|
|
}
|
|
|
|
//
|
|
// Note: StackDescribe function is non-reentrant:
|
|
// Reason: Stack description is stored in a static buffer.
|
|
// Solution: Passing caller-allocated buffers would allow the
|
|
// function to become reentrant, however the current only client (FindOrCreateFilename)
|
|
// is synchronized with a heap mutex, after retrieving stack description the
|
|
// heap memory will be allocated to copy the text.
|
|
//
|
|
|
|
char * StackDescribe( void * const *ppAddresses, int nMaxAddresses )
|
|
{
|
|
static char s_chStackDescription[ 32 * 1024 ];
|
|
char *pchBuffer = s_chStackDescription;
|
|
|
|
#if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS ) //Assuming StackDescribe is called iteratively on a sorted set of stacks (as in DumpStackStats()). We can save work by skipping unchanged parts at the beginning of the string.
|
|
static void *LastCallStack[STACK_TRACE_LENGTH] = { NULL };
|
|
static char *pEndPos[STACK_TRACE_LENGTH] = { NULL };
|
|
bool bUseExistingString = true;
|
|
#else
|
|
s_chStackDescription[ 0 ] = 0;
|
|
#endif
|
|
|
|
int k;
|
|
for ( k = 0; k < nMaxAddresses; ++ k )
|
|
{
|
|
if ( !ppAddresses[k] )
|
|
break;
|
|
|
|
#if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
|
|
if( bUseExistingString && (k < STACK_TRACE_LENGTH) )
|
|
{
|
|
if( ppAddresses[k] == LastCallStack[k] )
|
|
{
|
|
pchBuffer = pEndPos[k];
|
|
continue;
|
|
}
|
|
else
|
|
{
|
|
//everything from here on is invalidated
|
|
bUseExistingString = false;
|
|
for( int clearEntries = k; clearEntries < STACK_TRACE_LENGTH; ++clearEntries ) //wipe out unused entries
|
|
{
|
|
LastCallStack[clearEntries] = NULL;
|
|
pEndPos[clearEntries] = NULL;
|
|
}
|
|
//fall through to existing code
|
|
|
|
if( k == 0 )
|
|
*pchBuffer = '\0';
|
|
else
|
|
sprintf( pchBuffer, "<--" );
|
|
}
|
|
}
|
|
#endif
|
|
{
|
|
pchBuffer += strlen( pchBuffer );
|
|
|
|
char szTemp[MAX_PATH];
|
|
szTemp[0] = '\0';
|
|
uint32 iLine = 0;
|
|
uint32 iLineDisplacement = 0;
|
|
uint64 iSymbolDisplacement = 0;
|
|
if ( GetFileAndLineFromAddress( ppAddresses[k], szTemp, MAX_PATH, iLine, &iLineDisplacement ) )
|
|
{
|
|
char const *pchFileName = szTemp + strlen( szTemp );
|
|
for ( size_t numSlashesAllowed = 2; pchFileName > szTemp; --pchFileName )
|
|
{
|
|
if ( *pchFileName == '\\' )
|
|
{
|
|
if ( numSlashesAllowed-- )
|
|
continue;
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
sprintf( pchBuffer, iLineDisplacement ? "%s:%d+0x%I32X" : "%s:%d", pchFileName, iLine, iLineDisplacement );
|
|
}
|
|
else if ( GetSymbolNameFromAddress( ppAddresses[k], szTemp, MAX_PATH, &iSymbolDisplacement ) )
|
|
{
|
|
sprintf( pchBuffer, ( iSymbolDisplacement > 0 && !( iSymbolDisplacement >> 63 ) ) ? "%s+0x%llX" : "%s", szTemp, iSymbolDisplacement );
|
|
}
|
|
else
|
|
{
|
|
sprintf( pchBuffer, "#0x%08p", ppAddresses[k] );
|
|
}
|
|
|
|
pchBuffer += strlen( pchBuffer );
|
|
sprintf( pchBuffer, "<--" );
|
|
|
|
#if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
|
|
if( k < STACK_TRACE_LENGTH )
|
|
{
|
|
LastCallStack[k] = ppAddresses[k];
|
|
pEndPos[k] = pchBuffer;
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
*pchBuffer = 0;
|
|
|
|
#if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
|
|
for( ; k < STACK_TRACE_LENGTH; ++k ) //wipe out unused entries
|
|
{
|
|
LastCallStack[k] = NULL;
|
|
pEndPos[k] = NULL;
|
|
}
|
|
#endif
|
|
|
|
return s_chStackDescription;
|
|
}
|
|
|
|
#else
|
|
|
|
#define GetModuleFromAddress( address, pResult, iLength ) ( ( *pResult = 0 ), 0)
|
|
#define GetCallerModule( pDest, iLength ) false
|
|
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
// NOTE: This exactly mirrors the dbg header in the MSDEV crt
|
|
// eventually when we write our own allocator, we can kill this
|
|
struct CrtDbgMemHeader_t
|
|
{
|
|
unsigned char m_Reserved[8];
|
|
const char *m_pFileName;
|
|
int m_nLineNumber;
|
|
unsigned char m_Reserved2[16];
|
|
};
|
|
|
|
struct Sentinal_t
|
|
{
|
|
DWORD value[4];
|
|
};
|
|
|
|
Sentinal_t g_HeadSentinelAllocated =
|
|
{
|
|
0xeee1beef,
|
|
0xeee1f00d,
|
|
0xbd122969,
|
|
0xbeefbeef,
|
|
};
|
|
|
|
Sentinal_t g_HeadSentinelFree =
|
|
{
|
|
0xdeadbeef,
|
|
0xbaadf00d,
|
|
0xbd122969,
|
|
0xdeadbeef,
|
|
};
|
|
|
|
Sentinal_t g_TailSentinel =
|
|
{
|
|
0xbaadf00d,
|
|
0xbd122969,
|
|
0xdeadbeef,
|
|
0xbaadf00d,
|
|
};
|
|
|
|
const byte g_FreeFill = 0xdd;
|
|
|
|
enum DbgMemHeaderBlockType_t
|
|
{
|
|
BLOCKTYPE_FREE,
|
|
BLOCKTYPE_ALLOCATED
|
|
};
|
|
|
|
struct DbgMemHeader_t
|
|
#if !defined( _DEBUG ) || defined( _PS3 )
|
|
: CrtDbgMemHeader_t
|
|
#endif
|
|
{
|
|
size_t nLogicalSize;
|
|
#if defined( USE_STACK_TRACES )
|
|
unsigned int nStatIndex;
|
|
byte reserved[ 16 - (sizeof(unsigned int) + sizeof( size_t ) ) ]; // MS allocator always returns mem aligned on 16 bytes, which some of our code depends on
|
|
#else
|
|
byte reserved[16 - sizeof( size_t )]; // MS allocator always returns mem aligned on 16 bytes, which some of our code depends on
|
|
#endif
|
|
Sentinal_t sentinal;
|
|
};
|
|
|
|
const int g_nRecentFrees = ( IsPC() ) ? 8192 : 512;
|
|
DbgMemHeader_t ** GetRecentFrees() { static DbgMemHeader_t **g_pRecentFrees = (DbgMemHeader_t**)
|
|
#ifdef _PS3
|
|
g_pMemOverrideRawCrtFns->pfn_calloc
|
|
#else
|
|
calloc
|
|
#endif
|
|
( g_nRecentFrees, sizeof(DbgMemHeader_t *) );
|
|
return g_pRecentFrees; }
|
|
uint32 volatile g_iNextFreeSlot;
|
|
|
|
uint32 volatile g_break_BytesFree = 0xffffffff;
|
|
|
|
void LMDReportInvalidBlock( DbgMemHeader_t *pHeader, const char *pszMessage )
|
|
{
|
|
char szMsg[256];
|
|
if ( pHeader )
|
|
{
|
|
sprintf( szMsg, "HEAP IS CORRUPT: %s (block 0x%x, %d bytes)\n", pszMessage, (size_t)( ((byte*) pHeader) + sizeof( DbgMemHeader_t ) ), pHeader->nLogicalSize );
|
|
}
|
|
else
|
|
{
|
|
sprintf( szMsg, "HEAP IS CORRUPT: %s\n", pszMessage );
|
|
}
|
|
Assert( !"HEAP IS CORRUPT!" );
|
|
DebuggerBreak();
|
|
}
|
|
|
|
void LMDValidateBlock( DbgMemHeader_t *pHeader, bool bFreeList )
|
|
{
|
|
if ( memcmp( &pHeader->sentinal, bFreeList ? &g_HeadSentinelFree : &g_HeadSentinelAllocated, sizeof(Sentinal_t) ) != 0 )
|
|
{
|
|
LMDReportInvalidBlock( pHeader, "Head sentinel corrupt" );
|
|
}
|
|
if ( memcmp( ((Sentinal_t *)(( ((byte*) pHeader) + sizeof( DbgMemHeader_t ) + pHeader->nLogicalSize ))), &g_TailSentinel, sizeof(Sentinal_t) ) != 0 )
|
|
{
|
|
LMDReportInvalidBlock( pHeader, "Tail sentinel corrupt" );
|
|
}
|
|
if ( bFreeList )
|
|
{
|
|
byte *pCur = (byte *)pHeader + sizeof(DbgMemHeader_t);
|
|
byte *pLimit = pCur + pHeader->nLogicalSize;
|
|
while ( pCur != pLimit )
|
|
{
|
|
if ( *pCur++ != g_FreeFill )
|
|
{
|
|
LMDReportInvalidBlock( pHeader, "Write after free" );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#if defined( _DEBUG ) && !defined( POSIX )
|
|
#define GetCrtDbgMemHeader( pMem ) ((CrtDbgMemHeader_t*)((DbgMemHeader_t*)pMem - 1) - 1)
|
|
#elif defined( OSX )
|
|
DbgMemHeader_t *GetCrtDbgMemHeader( void *pMem );
|
|
#else
|
|
#define GetCrtDbgMemHeader( pMem ) ((DbgMemHeader_t*)(pMem) - 1)
|
|
#endif
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
#define GetAllocationStatIndex_Internal( pMem ) ( ((DbgMemHeader_t*)pMem - 1)->nStatIndex )
|
|
#endif
|
|
|
|
#ifdef OSX
|
|
DbgMemHeader_t *GetCrtDbgMemHeader( void *pMem )
|
|
{
|
|
size_t msize = malloc_size( pMem );
|
|
return (DbgMemHeader_t *)( (char *)pMem + msize - sizeof(DbgMemHeader_t) );
|
|
}
|
|
#endif
|
|
|
|
|
|
inline void *InternalMalloc( size_t nSize, const char *pFileName, int nLine )
|
|
{
|
|
#if defined( POSIX ) || defined( _PS3 )
|
|
void *pAllocedMem = NULL;
|
|
#ifdef OSX
|
|
pAllocedMem = malloc_zone_malloc( malloc_default_zone(), nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pAllocedMem );
|
|
#elif defined( _PS3 )
|
|
pAllocedMem = (g_pMemOverrideRawCrtFns->pfn_malloc)( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
|
|
*((void**)pInternalMem->m_Reserved2) = pAllocedMem;
|
|
#else
|
|
pAllocedMem = malloc( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
|
|
#endif
|
|
|
|
pInternalMem->m_pFileName = pFileName;
|
|
pInternalMem->m_nLineNumber = nLine;
|
|
pInternalMem->nLogicalSize = nSize;
|
|
*((int*)pInternalMem->m_Reserved) = 0xf00df00d;
|
|
|
|
pInternalMem->sentinal = g_HeadSentinelAllocated;
|
|
*( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nSize ) ) = g_TailSentinel;
|
|
LMDValidateBlock( pInternalMem, false );
|
|
|
|
#ifdef OSX
|
|
return pAllocedMem;
|
|
#else
|
|
return pInternalMem + 1;
|
|
#endif
|
|
|
|
#else // WIN32
|
|
DbgMemHeader_t *pInternalMem;
|
|
#if !defined( _DEBUG )
|
|
pInternalMem = (DbgMemHeader_t *)malloc( nSize + sizeof(DbgMemHeader_t) );
|
|
pInternalMem->m_pFileName = pFileName;
|
|
pInternalMem->m_nLineNumber = nLine;
|
|
#else
|
|
pInternalMem = (DbgMemHeader_t *)_malloc_dbg( nSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
|
|
#endif
|
|
|
|
pInternalMem->nLogicalSize = nSize;
|
|
return pInternalMem + 1;
|
|
#endif // WIN32
|
|
}
|
|
|
|
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
|
inline void *InternalMallocAligned( size_t nSize, size_t align, const char *pFileName, int nLine )
|
|
{
|
|
#if defined( POSIX ) || defined( _PS3 )
|
|
void *pAllocedMem = NULL;
|
|
#ifdef OSX
|
|
pAllocedMem = malloc_zone_malloc( malloc_default_zone(), nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pAllocedMem );
|
|
#elif defined( _PS3 )
|
|
size_t numWastedAlignPages = ( sizeof( DbgMemHeader_t ) / align );
|
|
if ( align * numWastedAlignPages < sizeof( DbgMemHeader_t ) )
|
|
++ numWastedAlignPages;
|
|
size_t nSizeRequired = nSize + numWastedAlignPages*align + sizeof( Sentinal_t );
|
|
pAllocedMem = (g_pMemOverrideRawCrtFns->pfn_memalign)( align, nSizeRequired );
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( ((char*)pAllocedMem) + numWastedAlignPages*align );
|
|
*((void**)pInternalMem->m_Reserved2) = pAllocedMem;
|
|
#else
|
|
pAllocedMem = malloc( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
|
|
#endif
|
|
|
|
pInternalMem->m_pFileName = pFileName;
|
|
pInternalMem->m_nLineNumber = nLine;
|
|
pInternalMem->nLogicalSize = nSize;
|
|
*((int*)pInternalMem->m_Reserved) = 0xf00df00d;
|
|
|
|
pInternalMem->sentinal = g_HeadSentinelAllocated;
|
|
*( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nSize ) ) = g_TailSentinel;
|
|
LMDValidateBlock( pInternalMem, false );
|
|
|
|
#ifdef OSX
|
|
return pAllocedMem;
|
|
#else
|
|
return pInternalMem + 1;
|
|
#endif
|
|
|
|
#else // WIN32
|
|
DbgMemHeader_t *pInternalMem;
|
|
#if !defined( _DEBUG )
|
|
pInternalMem = (DbgMemHeader_t *)malloc( nSize + sizeof(DbgMemHeader_t) );
|
|
pInternalMem->m_pFileName = pFileName;
|
|
pInternalMem->m_nLineNumber = nLine;
|
|
#else
|
|
pInternalMem = (DbgMemHeader_t *)_malloc_dbg( nSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
|
|
#endif
|
|
|
|
pInternalMem->nLogicalSize = nSize;
|
|
return pInternalMem + 1;
|
|
#endif // WIN32
|
|
}
|
|
#endif
|
|
|
|
inline void *InternalRealloc( void *pMem, size_t nNewSize, const char *pFileName, int nLine )
|
|
{
|
|
if ( !pMem )
|
|
return InternalMalloc( nNewSize, pFileName, nLine );
|
|
|
|
#ifdef POSIX
|
|
void *pNewAllocedMem = NULL;
|
|
#ifdef OSX
|
|
pNewAllocedMem = (DbgMemHeader_t *)malloc_zone_realloc( malloc_default_zone(), pMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pNewAllocedMem );
|
|
#elif defined( _PS3 )
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
|
|
pNewAllocedMem = (DbgMemHeader_t *)(g_pMemOverrideRawCrtFns->pfn_realloc)( *((void**)pInternalMem->m_Reserved2), nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
|
|
*((void**)pInternalMem->m_Reserved2) = pNewAllocedMem;
|
|
#else
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
|
|
pNewAllocedMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
|
|
#endif
|
|
|
|
pInternalMem->m_pFileName = pFileName;
|
|
pInternalMem->m_nLineNumber = nLine;
|
|
pInternalMem->nLogicalSize = static_cast<unsigned int>( nNewSize );
|
|
*((int*)pInternalMem->m_Reserved) = 0xf00df00d;
|
|
|
|
pInternalMem->sentinal = g_HeadSentinelAllocated;
|
|
*( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nNewSize ) ) = g_TailSentinel;
|
|
LMDValidateBlock( pInternalMem, false );
|
|
|
|
#ifdef OSX
|
|
return pNewAllocedMem;
|
|
#else
|
|
return pInternalMem + 1;
|
|
#endif
|
|
|
|
#else // WIN32
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
|
|
#if !defined( _DEBUG )
|
|
pInternalMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) );
|
|
pInternalMem->m_pFileName = pFileName;
|
|
pInternalMem->m_nLineNumber = nLine;
|
|
#else
|
|
pInternalMem = (DbgMemHeader_t *)_realloc_dbg( pInternalMem, nNewSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
|
|
#endif
|
|
|
|
pInternalMem->nLogicalSize = nNewSize;
|
|
return pInternalMem + 1;
|
|
#endif // WIN32
|
|
}
|
|
|
|
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
|
inline void *InternalReallocAligned( void *pMem, size_t nNewSize, size_t align, const char *pFileName, int nLine )
|
|
{
|
|
if ( !pMem )
|
|
return InternalMallocAligned( nNewSize, align, pFileName, nLine );
|
|
|
|
#ifdef POSIX
|
|
void *pNewAllocedMem = NULL;
|
|
#ifdef OSX
|
|
pNewAllocedMem = (DbgMemHeader_t *)malloc_zone_realloc( malloc_default_zone(), pMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pNewAllocedMem );
|
|
#elif defined( _PS3 )
|
|
size_t numWastedAlignPages = ( sizeof( DbgMemHeader_t ) / align );
|
|
if ( align * numWastedAlignPages < sizeof( DbgMemHeader_t ) )
|
|
++ numWastedAlignPages;
|
|
size_t nSizeRequired = nNewSize + numWastedAlignPages*align + sizeof( Sentinal_t );
|
|
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
|
|
pNewAllocedMem = (DbgMemHeader_t *)(g_pMemOverrideRawCrtFns->pfn_reallocalign)( *((void**)pInternalMem->m_Reserved2), nSizeRequired, align );
|
|
pInternalMem = GetCrtDbgMemHeader( ((char*)pNewAllocedMem) + numWastedAlignPages*align );
|
|
*((void**)pInternalMem->m_Reserved2) = pNewAllocedMem;
|
|
#else
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
|
|
pNewAllocedMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
|
|
pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
|
|
#endif
|
|
|
|
pInternalMem->m_pFileName = pFileName;
|
|
pInternalMem->m_nLineNumber = nLine;
|
|
pInternalMem->nLogicalSize = static_cast<unsigned int>( nNewSize );
|
|
*((int*)pInternalMem->m_Reserved) = 0xf00df00d;
|
|
|
|
pInternalMem->sentinal = g_HeadSentinelAllocated;
|
|
*( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nNewSize ) ) = g_TailSentinel;
|
|
LMDValidateBlock( pInternalMem, false );
|
|
|
|
#ifdef OSX
|
|
return pNewAllocedMem;
|
|
#else
|
|
return pInternalMem + 1;
|
|
#endif
|
|
|
|
#else // WIN32
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
|
|
#if !defined( _DEBUG )
|
|
pInternalMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) );
|
|
pInternalMem->m_pFileName = pFileName;
|
|
pInternalMem->m_nLineNumber = nLine;
|
|
#else
|
|
pInternalMem = (DbgMemHeader_t *)_realloc_dbg( pInternalMem, nNewSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
|
|
#endif
|
|
|
|
pInternalMem->nLogicalSize = nNewSize;
|
|
return pInternalMem + 1;
|
|
#endif // WIN32
|
|
}
|
|
#endif
|
|
|
|
inline void InternalFree( void *pMem )
|
|
{
|
|
if ( !pMem )
|
|
return;
|
|
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
|
|
|
|
#if defined( POSIX )
|
|
// Record it in recent free blocks list
|
|
DbgMemHeader_t **pRecentFrees = GetRecentFrees();
|
|
uint32 iNextSlot = ThreadInterlockedIncrement( &g_iNextFreeSlot );
|
|
iNextSlot %= g_nRecentFrees;
|
|
|
|
if ( memcmp( &pInternalMem->sentinal, &g_HeadSentinelAllocated, sizeof( Sentinal_t ) ) != 0 )
|
|
{
|
|
Assert( !"Double Free or Corrupt Block Header!" );
|
|
DebuggerBreak();
|
|
}
|
|
LMDValidateBlock( pInternalMem, false );
|
|
if ( g_break_BytesFree == pInternalMem->nLogicalSize )
|
|
{
|
|
DebuggerBreak();
|
|
}
|
|
pInternalMem->sentinal = g_HeadSentinelFree;
|
|
memset( pMem, g_FreeFill, pInternalMem->nLogicalSize );
|
|
|
|
DbgMemHeader_t *pToFree = pInternalMem;
|
|
if ( pInternalMem->nLogicalSize < 16*1024 )
|
|
{
|
|
pToFree = pRecentFrees[iNextSlot];
|
|
pRecentFrees[iNextSlot] = pInternalMem;
|
|
|
|
if ( pToFree )
|
|
{
|
|
LMDValidateBlock( pToFree, true );
|
|
}
|
|
}
|
|
|
|
// Validate several last frees
|
|
for ( uint32 k = iNextSlot - 1, iteration = 0; iteration < 10; ++ iteration, -- k )
|
|
{
|
|
if ( DbgMemHeader_t *pLastFree = pRecentFrees[ k % g_nRecentFrees ] )
|
|
{
|
|
LMDValidateBlock( pLastFree, true );
|
|
}
|
|
}
|
|
|
|
if ( !pToFree )
|
|
return;
|
|
|
|
#ifdef OSX
|
|
malloc_zone_free( malloc_default_zone(), pToFree );
|
|
#elif defined( _PS3 )
|
|
(g_pMemOverrideRawCrtFns->pfn_free)( *((void**)pToFree->m_Reserved2) );
|
|
#elif LINUX
|
|
free( pToFree );
|
|
#else
|
|
free( pToFree );
|
|
#endif
|
|
#elif defined( _DEBUG )
|
|
_free_dbg( pInternalMem, _NORMAL_BLOCK );
|
|
#else
|
|
free( pInternalMem );
|
|
#endif
|
|
}
|
|
|
|
inline size_t InternalMSize( void *pMem )
|
|
{
|
|
#if defined( _PS3 )
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
|
|
return pInternalMem->nLogicalSize;
|
|
#elif defined(POSIX)
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
|
|
return pInternalMem->nLogicalSize;
|
|
#elif !defined(_DEBUG)
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
|
|
return _msize( pInternalMem ) - sizeof(DbgMemHeader_t);
|
|
#else
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
|
|
return _msize_dbg( pInternalMem, _NORMAL_BLOCK ) - sizeof(DbgMemHeader_t);
|
|
#endif
|
|
}
|
|
|
|
inline size_t InternalLogicalSize( void *pMem )
|
|
{
|
|
#if defined(POSIX)
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
|
|
#elif !defined(_DEBUG)
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
|
|
#else
|
|
DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
|
|
#endif
|
|
return pInternalMem->nLogicalSize;
|
|
}
|
|
|
|
#ifndef _DEBUG
|
|
#define _CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg ) 0
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
|
|
// Custom allocator protects this module from recursing on operator new
|
|
template <class T>
|
|
class CNoRecurseAllocator
|
|
{
|
|
public:
|
|
// type definitions
|
|
typedef T value_type;
|
|
typedef T* pointer;
|
|
typedef const T* const_pointer;
|
|
typedef T& reference;
|
|
typedef const T& const_reference;
|
|
typedef std::size_t size_type;
|
|
typedef std::ptrdiff_t difference_type;
|
|
|
|
CNoRecurseAllocator() {}
|
|
CNoRecurseAllocator(const CNoRecurseAllocator&) {}
|
|
template <class U> CNoRecurseAllocator(const CNoRecurseAllocator<U>&) {}
|
|
~CNoRecurseAllocator(){}
|
|
|
|
// rebind allocator to type U
|
|
template <class U > struct rebind { typedef CNoRecurseAllocator<U> other; };
|
|
|
|
// return address of values
|
|
pointer address (reference value) const { return &value; }
|
|
|
|
const_pointer address (const_reference value) const { return &value;}
|
|
size_type max_size() const { return INT_MAX; }
|
|
|
|
pointer allocate(size_type num, const void* = 0) { return (pointer)DebugAlloc(num * sizeof(T)); }
|
|
void deallocate (pointer p, size_type num) { DebugFree(p); }
|
|
void construct(pointer p, const T& value) { new((void*)p)T(value); }
|
|
void destroy (pointer p) { p->~T(); }
|
|
};
|
|
|
|
template <class T1, class T2>
|
|
bool operator==(const CNoRecurseAllocator<T1>&, const CNoRecurseAllocator<T2>&)
|
|
{
|
|
return true;
|
|
}
|
|
|
|
template <class T1, class T2>
|
|
bool operator!=(const CNoRecurseAllocator<T1>&, const CNoRecurseAllocator<T2>&)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
class CStringLess
|
|
{
|
|
public:
|
|
bool operator()(const char *pszLeft, const char *pszRight ) const
|
|
{
|
|
return ( V_tier0_stricmp( pszLeft, pszRight ) < 0 );
|
|
}
|
|
};
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#pragma warning( disable:4074 ) // warning C4074: initializers put in compiler reserved initialization area
|
|
#pragma init_seg( compiler )
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// NOTE! This should never be called directly from leaf code
|
|
// Just use new,delete,malloc,free etc. They will call into this eventually
|
|
//-----------------------------------------------------------------------------
|
|
class CDbgMemAlloc : public IMemAlloc
|
|
{
|
|
public:
|
|
CDbgMemAlloc();
|
|
virtual ~CDbgMemAlloc();
|
|
|
|
// Release versions
|
|
virtual void *Alloc( size_t nSize );
|
|
virtual void *Realloc( void *pMem, size_t nSize );
|
|
virtual void Free( void *pMem );
|
|
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
|
|
|
|
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
|
virtual void *AllocAlign( size_t nSize, size_t align );
|
|
virtual void *AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine );
|
|
virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align );
|
|
virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine );
|
|
#endif
|
|
|
|
// Debug versions
|
|
virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
|
|
virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
|
virtual void Free( void *pMem, const char *pFileName, int nLine );
|
|
virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
|
|
|
|
virtual void *RegionAlloc( int region, size_t nSize ) { return Alloc( nSize ); }
|
|
virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) { return Alloc( nSize, pFileName, nLine ); }
|
|
|
|
// Returns the size of a particular allocation (NOTE: may be larger than the size requested!)
|
|
virtual size_t GetSize( void *pMem );
|
|
|
|
// Force file + line information for an allocation
|
|
virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
|
|
virtual void PopAllocDbgInfo();
|
|
|
|
virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc );
|
|
virtual int CrtSetReportMode( int nReportType, int nReportMode );
|
|
virtual int CrtIsValidHeapPointer( const void *pMem );
|
|
virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
|
|
virtual int CrtCheckMemory( void );
|
|
virtual int CrtSetDbgFlag( int nNewFlag );
|
|
virtual void CrtMemCheckpoint( _CrtMemState *pState );
|
|
|
|
// handles storing allocation info for coroutines
|
|
virtual uint32 GetDebugInfoSize();
|
|
virtual void SaveDebugInfo( void *pvDebugInfo );
|
|
virtual void RestoreDebugInfo( const void *pvDebugInfo );
|
|
virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine );
|
|
|
|
// FIXME: Remove when we have our own allocator
|
|
virtual void* CrtSetReportFile( int nRptType, void* hFile );
|
|
virtual void* CrtSetReportHook( void* pfnNewHook );
|
|
virtual int CrtDbgReport( int nRptType, const char * szFile,
|
|
int nLine, const char * szModule, const char * szFormat );
|
|
|
|
virtual int heapchk();
|
|
|
|
virtual bool IsDebugHeap() { return true; }
|
|
|
|
virtual int GetVersion() { return MEMALLOC_VERSION; }
|
|
|
|
virtual void CompactHeap()
|
|
{
|
|
#if defined( _X360 ) && defined( _DEBUG )
|
|
HeapCompact( GetProcessHeap(), 0 );
|
|
#endif
|
|
}
|
|
|
|
virtual void CompactIncremental() {}
|
|
virtual void OutOfMemory( size_t nBytesAttempted = 0 ) {}
|
|
|
|
virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) { return NULL; } // debug heap doesn't attempt retries
|
|
|
|
void SetStatsExtraInfo( const char *pMapName, const char *pComment )
|
|
{
|
|
#if defined( _MEMTEST )
|
|
strncpy( s_szStatsMapName, pMapName, sizeof( s_szStatsMapName ) );
|
|
s_szStatsMapName[sizeof( s_szStatsMapName ) - 1] = '\0';
|
|
|
|
strncpy( s_szStatsComment, pComment, sizeof( s_szStatsComment ) );
|
|
s_szStatsComment[sizeof( s_szStatsComment ) - 1] = '\0';
|
|
#endif
|
|
}
|
|
|
|
virtual size_t MemoryAllocFailed();
|
|
void SetCRTAllocFailed( size_t nMemSize );
|
|
|
|
enum
|
|
{
|
|
BYTE_COUNT_16 = 0,
|
|
BYTE_COUNT_32,
|
|
BYTE_COUNT_128,
|
|
BYTE_COUNT_2048,
|
|
BYTE_COUNT_GREATER,
|
|
|
|
NUM_BYTE_COUNT_BUCKETS
|
|
};
|
|
|
|
private:
|
|
struct MemInfo_t
|
|
{
|
|
#if defined( USE_STACK_TRACES )
|
|
DECLARE_CALLSTACKSTATSTRUCT();
|
|
DECLARE_CALLSTACKSTATSTRUCT_FIELDDESCRIPTION();
|
|
#endif
|
|
|
|
MemInfo_t()
|
|
{
|
|
memset( this, 0, sizeof(*this) );
|
|
}
|
|
|
|
// Size in bytes
|
|
size_t m_nCurrentSize;
|
|
size_t m_nPeakSize;
|
|
size_t m_nTotalSize;
|
|
size_t m_nOverheadSize;
|
|
size_t m_nPeakOverheadSize;
|
|
|
|
// Count in terms of # of allocations
|
|
int m_nCurrentCount;
|
|
int m_nPeakCount;
|
|
int m_nTotalCount;
|
|
|
|
int m_nSumTargetRange;
|
|
int m_nCurTargetRange;
|
|
int m_nMaxTargetRange;
|
|
|
|
// Count in terms of # of allocations of a particular size
|
|
int m_pCount[NUM_BYTE_COUNT_BUCKETS];
|
|
|
|
// Time spent allocating + deallocating (microseconds)
|
|
int64 m_nTime;
|
|
};
|
|
|
|
struct MemInfoKey_FileLine_t
|
|
{
|
|
MemInfoKey_FileLine_t( const char *pFileName, int line ) : m_pFileName(pFileName), m_nLine(line) {}
|
|
bool operator<( const MemInfoKey_FileLine_t &key ) const
|
|
{
|
|
int iret = V_tier0_stricmp( m_pFileName, key.m_pFileName );
|
|
if ( iret < 0 )
|
|
return true;
|
|
|
|
if ( iret > 0 )
|
|
return false;
|
|
|
|
return m_nLine < key.m_nLine;
|
|
}
|
|
|
|
const char *m_pFileName;
|
|
int m_nLine;
|
|
};
|
|
|
|
// NOTE: Deliberately using STL here because the UTL stuff
|
|
// is a client of this library; want to avoid circular dependency
|
|
|
|
// Maps file name to info
|
|
typedef std::map< MemInfoKey_FileLine_t, MemInfo_t, std::less<MemInfoKey_FileLine_t>, CNoRecurseAllocator<std::pair<const MemInfoKey_FileLine_t, MemInfo_t> > > StatMap_FileLine_t;
|
|
typedef StatMap_FileLine_t::iterator StatMapIter_FileLine_t;
|
|
typedef StatMap_FileLine_t::value_type StatMapEntry_FileLine_t;
|
|
|
|
typedef std::set<const char *, CStringLess, CNoRecurseAllocator<const char *> > Filenames_t;
|
|
|
|
// Heap reporting method
|
|
typedef void (*HeapReportFunc_t)( char const *pFormat, ... );
|
|
|
|
private:
|
|
// Returns the actual debug info
|
|
virtual void GetActualDbgInfo( const char *&pFileName, int &nLine );
|
|
|
|
// Finds the file in our map
|
|
MemInfo_t &FindOrCreateEntry( const char *pFileName, int line );
|
|
const char *FindOrCreateFilename( const char *pFileName );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
int GetCallStackForIndex( unsigned int index, void **pCallStackOut, int iMaxEntriesOut );
|
|
friend int GetAllocationCallStack( void *mem, void **pCallStackOut, int iMaxEntriesOut );
|
|
#endif
|
|
|
|
// Updates stats
|
|
virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
|
|
virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
|
|
#if defined( USE_STACK_TRACES )
|
|
void RegisterAllocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
|
|
void RegisterDeallocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
|
|
#endif
|
|
void RegisterAllocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
|
|
void RegisterDeallocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
|
|
|
|
// Gets the allocation file name
|
|
const char *GetAllocatonFileName( void *pMem );
|
|
int GetAllocatonLineNumber( void *pMem );
|
|
|
|
// FIXME: specify a spew output func for dumping stats
|
|
// Stat output
|
|
void DumpMemInfo( const char *pAllocationName, int line, const MemInfo_t &info );
|
|
void DumpFileStats();
|
|
#if defined( USE_STACK_TRACES )
|
|
void DumpMemInfo( void * const CallStack[STACK_TRACE_LENGTH], const MemInfo_t &info );
|
|
void DumpCallStackFlow( char const *pchFileBase );
|
|
#endif
|
|
virtual void DumpStats();
|
|
virtual void DumpStatsFileBase( char const *pchFileBase, DumpStatsFormat_t nFormat = FORMAT_TEXT ) OVERRIDE;
|
|
virtual void DumpBlockStats( void *p );
|
|
virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory );
|
|
|
|
virtual size_t ComputeMemoryUsedBy( char const *pchSubStr );
|
|
|
|
virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes )
|
|
{
|
|
#if defined( _GAMECONSOLE ) || defined( _WIN32 )
|
|
extern IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes );
|
|
return VirtualMemoryManager_AllocateVirtualMemorySection( numMaxBytes );
|
|
#else
|
|
return NULL;
|
|
#endif
|
|
}
|
|
|
|
virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats )
|
|
{
|
|
// TODO: reuse code from GlobalMemoryStatus (though this is only really useful when using CStdMemAlloc...)
|
|
return 0;
|
|
}
|
|
|
|
private:
|
|
StatMap_FileLine_t m_StatMap_FileLine;
|
|
#if defined( USE_STACK_TRACES )
|
|
typedef CCallStackStatsGatherer<MemInfo_t, STACK_TRACE_LENGTH, GetCallStack_Fast, CCallStackStatsGatherer_StatMutexPool<128>, CNoRecurseAllocator> CallStackStatsType_t;
|
|
CallStackStatsType_t m_CallStackStats;
|
|
#endif
|
|
|
|
MemInfo_t m_GlobalInfo;
|
|
CFastTimer m_Timer;
|
|
bool m_bInitialized;
|
|
Filenames_t m_Filenames;
|
|
|
|
HeapReportFunc_t m_OutputFunc;
|
|
|
|
static size_t s_pCountSizes[NUM_BYTE_COUNT_BUCKETS];
|
|
static const char *s_pCountHeader[NUM_BYTE_COUNT_BUCKETS];
|
|
|
|
size_t m_sMemoryAllocFailed;
|
|
};
|
|
|
|
static char const *g_pszUnknown = "unknown";
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
BEGIN_STATSTRUCTDESCRIPTION( CDbgMemAlloc::MemInfo_t )
|
|
WRITE_STATSTRUCT_FIELDDESCRIPTION();
|
|
END_STATSTRUCTDESCRIPTION()
|
|
|
|
|
|
BEGIN_STATSTRUCTFIELDDESCRIPTION( CDbgMemAlloc::MemInfo_t )
|
|
DEFINE_STATSTRUCTFIELD( m_nCurrentSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nPeakSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nTotalSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nOverheadSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nPeakOverheadSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nCurrentCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nPeakCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nTotalCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nSumTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nCurTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nMaxTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_16, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_32, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_128, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_2048, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_GREATER, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
|
|
DEFINE_STATSTRUCTFIELD( m_nTime, BasicStatStructFieldDesc, ( BSSFT_INT64, BSSFCM_ADD ) )
|
|
END_STATSTRUCTFIELDDESCRIPTION()
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const int DBG_INFO_STACK_DEPTH = 32;
|
|
|
|
struct DbgInfoStack_t
|
|
{
|
|
const char *m_pFileName;
|
|
int m_nLine;
|
|
};
|
|
|
|
#ifdef _PS3
|
|
#ifndef _CERT
|
|
extern TLSGlobals * ( *g_pfnElfGetTlsGlobals )();
|
|
#define IfDbgInfoIsReady() if ( TLSGlobals *IfDbgInfoIsReady_pTlsGlobals = g_pfnElfGetTlsGlobals ? g_pfnElfGetTlsGlobals() : NULL )
|
|
#else
|
|
#define IfDbgInfoIsReady() if ( TLSGlobals *IfDbgInfoIsReady_pTlsGlobals = GetTLSGlobals() )
|
|
#endif
|
|
#define g_DbgInfoStack ( ( DbgInfoStack_t *& ) IfDbgInfoIsReady_pTlsGlobals->pMallocDbgInfoStack )
|
|
#define g_nDbgInfoStackDepth ( IfDbgInfoIsReady_pTlsGlobals->nMallocDbgInfoStackDepth )
|
|
#else
|
|
CTHREADLOCALPTR( DbgInfoStack_t) g_DbgInfoStack CONSTRUCT_EARLY;
|
|
CTHREADLOCALINT g_nDbgInfoStackDepth CONSTRUCT_EARLY;
|
|
#define IfDbgInfoIsReady() if (true)
|
|
#endif
|
|
|
|
#ifdef _PS3
|
|
struct CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early
|
|
{
|
|
CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early()
|
|
{
|
|
malloc_managed_size mms;
|
|
mms.current_inuse_size = 0x12345678;
|
|
mms.current_system_size = 0x09ABCDEF;
|
|
mms.max_system_size = 0;
|
|
int iResult = malloc_stats( &mms );
|
|
g_pMemOverrideRawCrtFns = reinterpret_cast< MemOverrideRawCrtFunctions_t * >( iResult );
|
|
}
|
|
}
|
|
g_CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early CONSTRUCT_EARLY;
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Singleton...
|
|
//-----------------------------------------------------------------------------
|
|
static CDbgMemAlloc s_DbgMemAlloc CONSTRUCT_EARLY;
|
|
|
|
#ifdef _PS3
|
|
|
|
IMemAlloc *g_pMemAllocInternalPS3 = &s_DbgMemAlloc;
|
|
PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3_IMPL
|
|
|
|
#else // !_PS3
|
|
|
|
#ifndef TIER0_VALIDATE_HEAP
|
|
IMemAlloc *g_pMemAlloc CONSTRUCT_EARLY = &s_DbgMemAlloc;
|
|
void SetAllocatorObject( IMemAlloc* pAllocator )
|
|
{
|
|
g_pMemAlloc = pAllocator;
|
|
}
|
|
#else
|
|
IMemAlloc *g_pActualAlloc = &s_DbgMemAlloc;
|
|
void SetAllocatorObject( IMemAlloc* pAllocator )
|
|
{
|
|
g_pActualAlloc = pAllocator;
|
|
}
|
|
#endif
|
|
|
|
#endif // _PS3
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
|
|
CThreadMutex g_DbgMemMutex CONSTRUCT_EARLY;
|
|
|
|
#define HEAP_LOCK() AUTO_LOCK( g_DbgMemMutex )
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Byte count buckets
|
|
//-----------------------------------------------------------------------------
|
|
size_t CDbgMemAlloc::s_pCountSizes[CDbgMemAlloc::NUM_BYTE_COUNT_BUCKETS] =
|
|
{
|
|
16, 32, 128, 2048, INT_MAX
|
|
};
|
|
|
|
const char *CDbgMemAlloc::s_pCountHeader[CDbgMemAlloc::NUM_BYTE_COUNT_BUCKETS] =
|
|
{
|
|
"<=16 byte allocations",
|
|
"17-32 byte allocations",
|
|
"33-128 byte allocations",
|
|
"129-2048 byte allocations",
|
|
">2048 byte allocations"
|
|
};
|
|
|
|
|
|
size_t g_TargetCountRangeMin = 0, g_TargetCountRangeMax = 0;
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Standard output
|
|
//-----------------------------------------------------------------------------
|
|
static FILE* s_DbgFile;
|
|
|
|
static void DefaultHeapReportFunc( char const *pFormat, ... )
|
|
{
|
|
va_list args;
|
|
va_start( args, pFormat );
|
|
vfprintf( s_DbgFile, pFormat, args );
|
|
va_end( args );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Constructor
|
|
//-----------------------------------------------------------------------------
|
|
CDbgMemAlloc::CDbgMemAlloc() : m_sMemoryAllocFailed( (size_t)0 )
|
|
{
|
|
CClockSpeedInit::Init();
|
|
|
|
m_OutputFunc = DefaultHeapReportFunc;
|
|
m_bInitialized = true;
|
|
|
|
if ( !IsDebug() && !IsX360() )
|
|
{
|
|
Plat_DebugString( "USE_MEM_DEBUG is enabled in a release build. Don't check this in!\n" );
|
|
}
|
|
|
|
#ifdef _PS3
|
|
g_pMemAllocInternalPS3 = &s_DbgMemAlloc;
|
|
PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3.m_pMemAllocCached = &s_DbgMemAlloc;
|
|
malloc_managed_size mms;
|
|
mms.current_inuse_size = 0x12345678;
|
|
mms.current_system_size = 0x09ABCDEF;
|
|
mms.max_system_size = reinterpret_cast< size_t >( this );
|
|
int iResult = malloc_stats( &mms );
|
|
g_pMemOverrideRawCrtFns = reinterpret_cast< MemOverrideRawCrtFunctions_t * >( iResult );
|
|
#elif IsPlatformWindowsPC()
|
|
char *pStr = (char*)Plat_GetCommandLineA();
|
|
if ( pStr )
|
|
{
|
|
char tempStr[512];
|
|
strncpy( tempStr, pStr, sizeof( tempStr ) - 1 );
|
|
tempStr[ sizeof( tempStr ) - 1 ] = 0;
|
|
_strupr( tempStr );
|
|
CheckWindowsAllocSettings( tempStr );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
CDbgMemAlloc::~CDbgMemAlloc()
|
|
{
|
|
Filenames_t::const_iterator iter = m_Filenames.begin();
|
|
while(iter != m_Filenames.end())
|
|
{
|
|
char *pFileName = (char*)(*iter);
|
|
free( pFileName );
|
|
iter++;
|
|
}
|
|
m_bInitialized = false;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Release versions
|
|
//-----------------------------------------------------------------------------
|
|
|
|
void *CDbgMemAlloc::Alloc( size_t nSize )
|
|
{
|
|
/*
|
|
// NOTE: Uncomment this to find unknown allocations
|
|
const char *pFileName = g_pszUnknown;
|
|
int nLine;
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
if (pFileName == g_pszUnknown)
|
|
{
|
|
int x = 3;
|
|
}
|
|
*/
|
|
char szModule[MAX_PATH];
|
|
if ( GetCallerModule( szModule, MAX_PATH ) )
|
|
{
|
|
return Alloc( nSize, szModule, 0 );
|
|
}
|
|
else
|
|
{
|
|
return Alloc( nSize, g_pszUnknown, 0 );
|
|
}
|
|
// return malloc( nSize );
|
|
}
|
|
|
|
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
|
void *CDbgMemAlloc::AllocAlign( size_t nSize, size_t align )
|
|
{
|
|
/*
|
|
// NOTE: Uncomment this to find unknown allocations
|
|
const char *pFileName = g_pszUnknown;
|
|
int nLine;
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
if (pFileName == g_pszUnknown)
|
|
{
|
|
int x = 3;
|
|
}
|
|
*/
|
|
char szModule[MAX_PATH];
|
|
if ( GetCallerModule( szModule, MAX_PATH ) )
|
|
{
|
|
return AllocAlign( nSize, align, szModule, 0 );
|
|
}
|
|
else
|
|
{
|
|
return AllocAlign( nSize, align, g_pszUnknown, 0 );
|
|
}
|
|
// return malloc( nSize );
|
|
}
|
|
#endif
|
|
|
|
void *CDbgMemAlloc::Realloc( void *pMem, size_t nSize )
|
|
{
|
|
/*
|
|
// NOTE: Uncomment this to find unknown allocations
|
|
const char *pFileName = g_pszUnknown;
|
|
int nLine;
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
if (pFileName == g_pszUnknown)
|
|
{
|
|
int x = 3;
|
|
}
|
|
*/
|
|
// FIXME: Should these gather stats?
|
|
char szModule[MAX_PATH];
|
|
if ( GetCallerModule( szModule, MAX_PATH ) )
|
|
{
|
|
return Realloc( pMem, nSize, szModule, 0 );
|
|
}
|
|
else
|
|
{
|
|
return Realloc( pMem, nSize, g_pszUnknown, 0 );
|
|
}
|
|
// return realloc( pMem, nSize );
|
|
}
|
|
|
|
void CDbgMemAlloc::Free( void *pMem )
|
|
{
|
|
// FIXME: Should these gather stats?
|
|
Free( pMem, g_pszUnknown, 0 );
|
|
// free( pMem );
|
|
}
|
|
|
|
void *CDbgMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Force file + line information for an allocation
|
|
//-----------------------------------------------------------------------------
|
|
void CDbgMemAlloc::PushAllocDbgInfo( const char *pFileName, int nLine )
|
|
{
|
|
IfDbgInfoIsReady()
|
|
{
|
|
|
|
if ( g_DbgInfoStack == NULL )
|
|
{
|
|
g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
|
|
g_nDbgInfoStackDepth = -1;
|
|
}
|
|
|
|
++g_nDbgInfoStackDepth;
|
|
Assert( g_nDbgInfoStackDepth < DBG_INFO_STACK_DEPTH );
|
|
g_DbgInfoStack[g_nDbgInfoStackDepth].m_pFileName = FindOrCreateFilename( pFileName );
|
|
g_DbgInfoStack[g_nDbgInfoStackDepth].m_nLine = nLine;
|
|
|
|
}
|
|
}
|
|
|
|
void CDbgMemAlloc::PopAllocDbgInfo()
|
|
{
|
|
IfDbgInfoIsReady()
|
|
{
|
|
|
|
if ( g_DbgInfoStack == NULL )
|
|
{
|
|
g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
|
|
g_nDbgInfoStackDepth = -1;
|
|
}
|
|
|
|
--g_nDbgInfoStackDepth;
|
|
Assert( g_nDbgInfoStackDepth >= -1 );
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// handles storing allocation info for coroutines
|
|
//-----------------------------------------------------------------------------
|
|
uint32 CDbgMemAlloc::GetDebugInfoSize()
|
|
{
|
|
return sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH + sizeof( int32 );
|
|
}
|
|
|
|
void CDbgMemAlloc::SaveDebugInfo( void *pvDebugInfo )
|
|
{
|
|
IfDbgInfoIsReady()
|
|
{
|
|
if ( g_DbgInfoStack == NULL )
|
|
{
|
|
g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
|
|
g_nDbgInfoStackDepth = -1;
|
|
}
|
|
|
|
int32 *pnStackDepth = (int32*) pvDebugInfo;
|
|
*pnStackDepth = g_nDbgInfoStackDepth;
|
|
memcpy( pnStackDepth+1, &g_DbgInfoStack[0], sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH );
|
|
}
|
|
}
|
|
|
|
void CDbgMemAlloc::RestoreDebugInfo( const void *pvDebugInfo )
|
|
{
|
|
IfDbgInfoIsReady()
|
|
{
|
|
if ( g_DbgInfoStack == NULL )
|
|
{
|
|
g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
|
|
g_nDbgInfoStackDepth = -1;
|
|
}
|
|
|
|
const int32 *pnStackDepth = (const int32*) pvDebugInfo;
|
|
g_nDbgInfoStackDepth = *pnStackDepth;
|
|
memcpy( &g_DbgInfoStack[0], pnStackDepth+1, sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH );
|
|
}
|
|
}
|
|
|
|
void CDbgMemAlloc::InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine )
|
|
{
|
|
int32 *pnStackDepth = (int32*) pvDebugInfo;
|
|
|
|
if( pchRootFileName )
|
|
{
|
|
*pnStackDepth = 0;
|
|
|
|
DbgInfoStack_t *pStackRoot = (DbgInfoStack_t *)(pnStackDepth + 1);
|
|
pStackRoot->m_pFileName = FindOrCreateFilename( pchRootFileName );
|
|
pStackRoot->m_nLine = nLine;
|
|
}
|
|
else
|
|
{
|
|
*pnStackDepth = -1;
|
|
}
|
|
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the actual debug info
|
|
//-----------------------------------------------------------------------------
|
|
void CDbgMemAlloc::GetActualDbgInfo( const char *&pFileName, int &nLine )
|
|
{
|
|
#if defined( USE_STACK_TRACES_DETAILED )
|
|
return;
|
|
#endif
|
|
|
|
IfDbgInfoIsReady()
|
|
{
|
|
|
|
if ( g_DbgInfoStack == NULL )
|
|
{
|
|
g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
|
|
g_nDbgInfoStackDepth = -1;
|
|
}
|
|
|
|
if ( g_nDbgInfoStackDepth >= 0 && g_DbgInfoStack[0].m_pFileName)
|
|
{
|
|
pFileName = g_DbgInfoStack[0].m_pFileName;
|
|
nLine = g_DbgInfoStack[0].m_nLine;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
//
|
|
//-----------------------------------------------------------------------------
|
|
const char *CDbgMemAlloc::FindOrCreateFilename( const char *pFileName )
|
|
{
|
|
// If we created it for the first time, actually *allocate* the filename memory
|
|
HEAP_LOCK();
|
|
// This is necessary for shutdown conditions: the file name is stored
|
|
// in some piece of memory in a DLL; if that DLL becomes unloaded,
|
|
// we'll have a pointer to crap memory
|
|
|
|
if ( !pFileName )
|
|
{
|
|
pFileName = g_pszUnknown;
|
|
}
|
|
|
|
#if defined( USE_STACK_TRACES_DETAILED )
|
|
{
|
|
|
|
// Walk the stack to determine what's causing the allocation
|
|
void *arrStackAddresses[ 10 ] = { 0 };
|
|
int numStackAddrRetrieved = GetCallStack_Fast( arrStackAddresses, 10, 2 ); //Skip this function, and either CDbgMemAlloc::Alloc() or CDbgMemAlloc::Realloc()
|
|
char *szStack = StackDescribe( arrStackAddresses, numStackAddrRetrieved );
|
|
if ( szStack && *szStack )
|
|
{
|
|
pFileName = szStack; // Use the stack description for the allocation
|
|
}
|
|
|
|
}
|
|
#endif // #if defined( USE_STACK_TRACES_DETAILED )
|
|
|
|
char *pszFilenameCopy;
|
|
Filenames_t::const_iterator iter = m_Filenames.find( pFileName );
|
|
if ( iter == m_Filenames.end() )
|
|
{
|
|
size_t nLen = strlen(pFileName) + 1;
|
|
pszFilenameCopy = (char *)DebugAlloc( nLen );
|
|
memcpy( pszFilenameCopy, pFileName, nLen );
|
|
m_Filenames.insert( pszFilenameCopy );
|
|
}
|
|
else
|
|
{
|
|
pszFilenameCopy = (char *)(*iter);
|
|
}
|
|
|
|
return pszFilenameCopy;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Finds the file in our map
|
|
//-----------------------------------------------------------------------------
|
|
CDbgMemAlloc::MemInfo_t &CDbgMemAlloc::FindOrCreateEntry( const char *pFileName, int line )
|
|
{
|
|
// Oh how I love crazy STL. retval.first == the StatMapIter_t in the std::pair
|
|
// retval.first->second == the MemInfo_t that's part of the StatMapIter_t
|
|
std::pair<StatMapIter_FileLine_t, bool> retval;
|
|
retval = m_StatMap_FileLine.insert( StatMapEntry_FileLine_t( MemInfoKey_FileLine_t( pFileName, line ), MemInfo_t() ) );
|
|
return retval.first->second;
|
|
}
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
int CDbgMemAlloc::GetCallStackForIndex( unsigned int index, void **pCallStackOut, int iMaxEntriesOut )
|
|
{
|
|
if( iMaxEntriesOut > STACK_TRACE_LENGTH )
|
|
iMaxEntriesOut = STACK_TRACE_LENGTH;
|
|
|
|
CallStackStatsType_t::StackReference stackRef = m_CallStackStats.GetCallStackForIndex( index );
|
|
|
|
memcpy( pCallStackOut, stackRef, iMaxEntriesOut * sizeof( void * ) );
|
|
for( int i = 0; i != iMaxEntriesOut; ++i )
|
|
{
|
|
if( pCallStackOut[i] == NULL )
|
|
return i;
|
|
}
|
|
return iMaxEntriesOut;
|
|
}
|
|
#endif
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Updates stats
|
|
//-----------------------------------------------------------------------------
|
|
void CDbgMemAlloc::RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
|
|
{
|
|
HEAP_LOCK();
|
|
RegisterAllocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
|
|
RegisterAllocation( FindOrCreateEntry( pFileName, nLine ), nLogicalSize, nActualSize, nTime );
|
|
}
|
|
|
|
void CDbgMemAlloc::RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
|
|
{
|
|
HEAP_LOCK();
|
|
RegisterDeallocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
|
|
RegisterDeallocation( FindOrCreateEntry( pFileName, nLine ), nLogicalSize, nActualSize, nTime );
|
|
}
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
void CDbgMemAlloc::RegisterAllocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
|
|
{
|
|
HEAP_LOCK();
|
|
RegisterAllocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
|
|
CCallStackStatsGatherer_StructAccessor_AutoLock<MemInfo_t> entryAccessor = m_CallStackStats.GetEntry( nStatIndex );
|
|
RegisterAllocation( *entryAccessor.GetStruct(), nLogicalSize, nActualSize, nTime );
|
|
}
|
|
|
|
void CDbgMemAlloc::RegisterDeallocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
|
|
{
|
|
HEAP_LOCK();
|
|
RegisterDeallocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
|
|
CCallStackStatsGatherer_StructAccessor_AutoLock<MemInfo_t> entryAccessor = m_CallStackStats.GetEntry( nStatIndex );
|
|
RegisterDeallocation( *entryAccessor.GetStruct(), nLogicalSize, nActualSize, nTime );
|
|
}
|
|
#endif
|
|
|
|
void CDbgMemAlloc::RegisterAllocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
|
|
{
|
|
++info.m_nCurrentCount;
|
|
++info.m_nTotalCount;
|
|
if (info.m_nCurrentCount > info.m_nPeakCount)
|
|
{
|
|
info.m_nPeakCount = info.m_nCurrentCount;
|
|
}
|
|
|
|
info.m_nCurrentSize += nLogicalSize;
|
|
info.m_nTotalSize += nLogicalSize;
|
|
if (info.m_nCurrentSize > info.m_nPeakSize)
|
|
{
|
|
info.m_nPeakSize = info.m_nCurrentSize;
|
|
}
|
|
|
|
if ( nLogicalSize > g_TargetCountRangeMin && nLogicalSize <= g_TargetCountRangeMax )
|
|
{
|
|
info.m_nSumTargetRange++;
|
|
info.m_nCurTargetRange++;
|
|
if ( info.m_nCurTargetRange > info.m_nMaxTargetRange )
|
|
{
|
|
info.m_nMaxTargetRange = info.m_nCurTargetRange;
|
|
}
|
|
}
|
|
|
|
for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
|
|
{
|
|
if (nLogicalSize <= s_pCountSizes[i])
|
|
{
|
|
++info.m_pCount[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
Assert( info.m_nPeakCount >= info.m_nCurrentCount );
|
|
Assert( info.m_nPeakSize >= info.m_nCurrentSize );
|
|
|
|
info.m_nOverheadSize += (nActualSize - nLogicalSize);
|
|
if (info.m_nOverheadSize > info.m_nPeakOverheadSize)
|
|
{
|
|
info.m_nPeakOverheadSize = info.m_nOverheadSize;
|
|
}
|
|
|
|
info.m_nTime += nTime;
|
|
}
|
|
|
|
void CDbgMemAlloc::RegisterDeallocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
|
|
{
|
|
--info.m_nCurrentCount;
|
|
info.m_nCurrentSize -= nLogicalSize;
|
|
|
|
for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
|
|
{
|
|
if (nLogicalSize <= s_pCountSizes[i])
|
|
{
|
|
--info.m_pCount[i];
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( nLogicalSize > g_TargetCountRangeMin && nLogicalSize <= g_TargetCountRangeMax )
|
|
{
|
|
info.m_nCurTargetRange--;
|
|
}
|
|
|
|
Assert( info.m_nPeakCount >= info.m_nCurrentCount );
|
|
Assert( info.m_nPeakSize >= info.m_nCurrentSize );
|
|
Assert( info.m_nCurrentCount >= 0 );
|
|
Assert( info.m_nCurrentSize >= 0 );
|
|
|
|
info.m_nOverheadSize -= (nActualSize - nLogicalSize);
|
|
|
|
info.m_nTime += nTime;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the allocation file name
|
|
//-----------------------------------------------------------------------------
|
|
|
|
const char *CDbgMemAlloc::GetAllocatonFileName( void *pMem )
|
|
{
|
|
if (!pMem)
|
|
return "";
|
|
|
|
CrtDbgMemHeader_t *pHeader = GetCrtDbgMemHeader( pMem );
|
|
if ( pHeader->m_pFileName )
|
|
return pHeader->m_pFileName;
|
|
else
|
|
return g_pszUnknown;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Gets the allocation file name
|
|
//-----------------------------------------------------------------------------
|
|
int CDbgMemAlloc::GetAllocatonLineNumber( void *pMem )
|
|
{
|
|
if ( !pMem )
|
|
return 0;
|
|
|
|
CrtDbgMemHeader_t *pHeader = GetCrtDbgMemHeader( pMem );
|
|
return pHeader->m_nLineNumber;
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Debug versions of the main allocation methods
|
|
//-----------------------------------------------------------------------------
|
|
void *CDbgMemAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
|
|
{
|
|
HEAP_LOCK();
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndex( CCallStackStorage( m_CallStackStats.StackFunction, 1 ) );
|
|
#endif
|
|
|
|
if ( !m_bInitialized )
|
|
{
|
|
void *pRetval = InternalMalloc( nSize, pFileName, nLine );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
if( pRetval )
|
|
{
|
|
GetAllocationStatIndex_Internal( pRetval ) = iStatEntryIndex;
|
|
}
|
|
#endif
|
|
|
|
return pRetval;
|
|
}
|
|
|
|
|
|
|
|
if ( pFileName != g_pszUnknown )
|
|
pFileName = FindOrCreateFilename( pFileName );
|
|
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
|
|
/*
|
|
if ( strcmp( pFileName, "class CUtlVector<int,class CUtlMemory<int> >" ) == 0)
|
|
{
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
}
|
|
*/
|
|
|
|
m_Timer.Start();
|
|
void *pMem = InternalMalloc( nSize, pFileName, nLine );
|
|
m_Timer.End();
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
if( pMem )
|
|
{
|
|
GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
|
|
}
|
|
#endif
|
|
|
|
ApplyMemoryInitializations( pMem, nSize );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
|
|
#else
|
|
RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
|
|
#endif
|
|
|
|
if ( !pMem )
|
|
{
|
|
SetCRTAllocFailed( nSize );
|
|
}
|
|
return pMem;
|
|
}
|
|
|
|
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
|
void *CDbgMemAlloc::AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine )
|
|
{
|
|
HEAP_LOCK();
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndexForCurrentCallStack( 1 );
|
|
#endif
|
|
|
|
if ( !m_bInitialized )
|
|
{
|
|
void *pRetval = InternalMalloc( nSize, pFileName, nLine );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
if( pRetval )
|
|
{
|
|
GetAllocationStatIndex_Internal( pRetval ) = iStatEntryIndex;
|
|
}
|
|
#endif
|
|
|
|
return pRetval;
|
|
}
|
|
|
|
|
|
|
|
if ( pFileName != g_pszUnknown )
|
|
pFileName = FindOrCreateFilename( pFileName );
|
|
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
|
|
/*
|
|
if ( strcmp( pFileName, "class CUtlVector<int,class CUtlMemory<int> >" ) == 0)
|
|
{
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
}
|
|
*/
|
|
|
|
m_Timer.Start();
|
|
void *pMem = InternalMallocAligned( nSize, align, pFileName, nLine );
|
|
m_Timer.End();
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
if( pMem )
|
|
{
|
|
GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
|
|
}
|
|
#endif
|
|
|
|
ApplyMemoryInitializations( pMem, nSize );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
|
|
#else
|
|
RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
|
|
#endif
|
|
|
|
if ( !pMem )
|
|
{
|
|
SetCRTAllocFailed( nSize );
|
|
}
|
|
return pMem;
|
|
}
|
|
#endif
|
|
|
|
void *CDbgMemAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
|
|
{
|
|
HEAP_LOCK();
|
|
|
|
pFileName = FindOrCreateFilename( pFileName );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndex( CCallStackStorage( m_CallStackStats.StackFunction, 1 ) );
|
|
#endif
|
|
|
|
if ( !m_bInitialized )
|
|
{
|
|
pMem = InternalRealloc( pMem, nSize, pFileName, nLine );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
if( pMem )
|
|
{
|
|
GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
|
|
}
|
|
#endif
|
|
return pMem;
|
|
}
|
|
|
|
if ( pMem != 0 )
|
|
{
|
|
#if defined( USE_STACK_TRACES )
|
|
RegisterDeallocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
|
|
#else
|
|
RegisterDeallocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
|
|
#endif
|
|
}
|
|
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
|
|
m_Timer.Start();
|
|
pMem = InternalRealloc( pMem, nSize, pFileName, nLine );
|
|
m_Timer.End();
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
if( pMem )
|
|
{
|
|
GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
|
|
}
|
|
#endif
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
|
|
#else
|
|
RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
|
|
#endif
|
|
|
|
if ( !pMem )
|
|
{
|
|
SetCRTAllocFailed( nSize );
|
|
}
|
|
return pMem;
|
|
}
|
|
|
|
#ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
|
|
void *CDbgMemAlloc::ReallocAlign( void *pMem, size_t nSize, size_t align )
|
|
{
|
|
/*
|
|
// NOTE: Uncomment this to find unknown allocations
|
|
const char *pFileName = g_pszUnknown;
|
|
int nLine;
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
if (pFileName == g_pszUnknown)
|
|
{
|
|
int x = 3;
|
|
}
|
|
*/
|
|
char szModule[MAX_PATH];
|
|
if ( GetCallerModule( szModule, MAX_PATH ) )
|
|
{
|
|
return ReallocAlign( pMem, nSize, align, szModule, 0 );
|
|
}
|
|
else
|
|
{
|
|
return ReallocAlign( pMem, nSize, align, g_pszUnknown, 0 );
|
|
}
|
|
// return malloc( nSize );
|
|
}
|
|
void *CDbgMemAlloc::ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine )
|
|
{
|
|
HEAP_LOCK();
|
|
|
|
pFileName = FindOrCreateFilename( pFileName );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndexForCurrentCallStack( 1 );
|
|
#endif
|
|
|
|
if ( !m_bInitialized )
|
|
{
|
|
pMem = InternalReallocAligned( pMem, nSize, align, pFileName, nLine );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
if( pMem )
|
|
{
|
|
GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
|
|
}
|
|
#endif
|
|
return pMem;
|
|
}
|
|
|
|
if ( pMem != 0 )
|
|
{
|
|
#if defined( USE_STACK_TRACES )
|
|
RegisterDeallocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
|
|
#else
|
|
RegisterDeallocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
|
|
#endif
|
|
}
|
|
|
|
GetActualDbgInfo( pFileName, nLine );
|
|
|
|
m_Timer.Start();
|
|
pMem = InternalReallocAligned( pMem, nSize, align, pFileName, nLine );
|
|
m_Timer.End();
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
if( pMem )
|
|
{
|
|
GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
|
|
}
|
|
#endif
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
|
|
#else
|
|
RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
|
|
#endif
|
|
|
|
if ( !pMem )
|
|
{
|
|
SetCRTAllocFailed( nSize );
|
|
}
|
|
return pMem;
|
|
}
|
|
#endif
|
|
|
|
void CDbgMemAlloc::Free( void *pMem, const char * /*pFileName*/, int nLine )
|
|
{
|
|
if ( !pMem )
|
|
return;
|
|
|
|
HEAP_LOCK();
|
|
|
|
if ( !m_bInitialized )
|
|
{
|
|
InternalFree( pMem );
|
|
return;
|
|
}
|
|
|
|
size_t nOldLogicalSize = InternalLogicalSize( pMem );
|
|
size_t nOldSize = InternalMSize( pMem );
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
unsigned int oldStatIndex = GetAllocationStatIndex_Internal( pMem );
|
|
#else
|
|
const char *pOldFileName = GetAllocatonFileName( pMem );
|
|
int oldLine = GetAllocatonLineNumber( pMem );
|
|
#endif
|
|
|
|
|
|
m_Timer.Start();
|
|
InternalFree( pMem );
|
|
m_Timer.End();
|
|
|
|
#if defined( USE_STACK_TRACES )
|
|
RegisterDeallocation( oldStatIndex, nOldLogicalSize, nOldSize, m_Timer.GetDuration().GetMicroseconds() );
|
|
#else
|
|
RegisterDeallocation( pOldFileName, oldLine, nOldLogicalSize, nOldSize, m_Timer.GetDuration().GetMicroseconds() );
|
|
#endif
|
|
}
|
|
|
|
void *CDbgMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine )
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Returns the size of a particular allocation (NOTE: may be larger than the size requested!)
|
|
//-----------------------------------------------------------------------------
|
|
size_t CDbgMemAlloc::GetSize( void *pMem )
|
|
{
|
|
HEAP_LOCK();
|
|
|
|
if ( !pMem )
|
|
return m_GlobalInfo.m_nCurrentSize;
|
|
|
|
return InternalMSize( pMem );
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// FIXME: Remove when we make our own heap! Crt stuff we're currently using
|
|
//-----------------------------------------------------------------------------
|
|
int32 CDbgMemAlloc::CrtSetBreakAlloc( int32 lNewBreakAlloc )
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
return _CrtSetBreakAlloc( lNewBreakAlloc );
|
|
#endif
|
|
}
|
|
|
|
int CDbgMemAlloc::CrtSetReportMode( int nReportType, int nReportMode )
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
return _CrtSetReportMode( nReportType, nReportMode );
|
|
#endif
|
|
}
|
|
|
|
int CDbgMemAlloc::CrtIsValidHeapPointer( const void *pMem )
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
return _CrtIsValidHeapPointer( pMem );
|
|
#endif
|
|
}
|
|
|
|
int CDbgMemAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access )
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
return _CrtIsValidPointer( pMem, size, access );
|
|
#endif
|
|
}
|
|
|
|
#define DBGMEM_CHECKMEMORY 1
|
|
|
|
int CDbgMemAlloc::CrtCheckMemory( void )
|
|
{
|
|
#if !defined( DBGMEM_CHECKMEMORY ) || defined( POSIX )
|
|
return 1;
|
|
#elif defined( _WIN32 )
|
|
if ( !_CrtCheckMemory())
|
|
{
|
|
Msg( "Memory check failed!\n" );
|
|
return 0;
|
|
}
|
|
return 1;
|
|
#else
|
|
return 1;
|
|
#endif
|
|
}
|
|
|
|
int CDbgMemAlloc::CrtSetDbgFlag( int nNewFlag )
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
return _CrtSetDbgFlag( nNewFlag );
|
|
#endif
|
|
}
|
|
|
|
void CDbgMemAlloc::CrtMemCheckpoint( _CrtMemState *pState )
|
|
{
|
|
#ifndef POSIX
|
|
_CrtMemCheckpoint( pState );
|
|
#endif
|
|
}
|
|
|
|
// FIXME: Remove when we have our own allocator
|
|
void* CDbgMemAlloc::CrtSetReportFile( int nRptType, void* hFile )
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
return (void*)_CrtSetReportFile( nRptType, (_HFILE)hFile );
|
|
#endif
|
|
}
|
|
|
|
void* CDbgMemAlloc::CrtSetReportHook( void* pfnNewHook )
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
return (void*)_CrtSetReportHook( (_CRT_REPORT_HOOK)pfnNewHook );
|
|
#endif
|
|
}
|
|
|
|
int CDbgMemAlloc::CrtDbgReport( int nRptType, const char * szFile,
|
|
int nLine, const char * szModule, const char * pMsg )
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
return _CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg );
|
|
#endif
|
|
}
|
|
|
|
int CDbgMemAlloc::heapchk()
|
|
{
|
|
#ifdef POSIX
|
|
return 0;
|
|
#else
|
|
if ( CrtCheckMemory() )
|
|
return _HEAPOK;
|
|
else
|
|
return _HEAPBADPTR;
|
|
#endif
|
|
}
|
|
|
|
void CDbgMemAlloc::DumpBlockStats( void *p )
|
|
{
|
|
DbgMemHeader_t *pBlock = (DbgMemHeader_t *)p - 1;
|
|
if ( !CrtIsValidHeapPointer( pBlock ) )
|
|
{
|
|
Msg( "0x%p is not valid heap pointer\n", p );
|
|
return;
|
|
}
|
|
|
|
const char *pFileName = GetAllocatonFileName( p );
|
|
int line = GetAllocatonLineNumber( p );
|
|
|
|
Msg( "0x%p allocated by %s line %d, %d bytes\n", p, pFileName, line, GetSize( p ) );
|
|
}
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Stat output
|
|
//-----------------------------------------------------------------------------
|
|
void CDbgMemAlloc::DumpMemInfo( const char *pAllocationName, int line, const MemInfo_t &info )
|
|
{
|
|
m_OutputFunc("%s, line %i\t%.1f\t%.1f\t%.1f\t%.1f\t%.1f\t%d\t%d\t%d\t%d\t%d\t%d\t%d",
|
|
pAllocationName,
|
|
line,
|
|
info.m_nCurrentSize / 1024.0f,
|
|
info.m_nPeakSize / 1024.0f,
|
|
info.m_nTotalSize / 1024.0f,
|
|
info.m_nOverheadSize / 1024.0f,
|
|
info.m_nPeakOverheadSize / 1024.0f,
|
|
(int)(info.m_nTime / 1000),
|
|
info.m_nCurrentCount,
|
|
info.m_nPeakCount,
|
|
info.m_nTotalCount,
|
|
info.m_nSumTargetRange,
|
|
info.m_nCurTargetRange,
|
|
info.m_nMaxTargetRange
|
|
);
|
|
|
|
for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
|
|
{
|
|
m_OutputFunc( "\t%d", info.m_pCount[i] );
|
|
}
|
|
|
|
m_OutputFunc("\n");
|
|
}
|
|
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Stat output
|
|
//-----------------------------------------------------------------------------
|
|
size_t CDbgMemAlloc::ComputeMemoryUsedBy( char const *pchSubStr)
|
|
{
|
|
size_t total = 0;
|
|
StatMapIter_FileLine_t iter = m_StatMap_FileLine.begin();
|
|
while(iter != m_StatMap_FileLine.end())
|
|
{
|
|
if(!pchSubStr || strstr(iter->first.m_pFileName,pchSubStr))
|
|
{
|
|
total += iter->second.m_nCurrentSize;
|
|
}
|
|
iter++;
|
|
}
|
|
return total;
|
|
}
|
|
|
|
void CDbgMemAlloc::DumpFileStats()
|
|
{
|
|
StatMapIter_FileLine_t iter = m_StatMap_FileLine.begin();
|
|
while(iter != m_StatMap_FileLine.end())
|
|
{
|
|
DumpMemInfo( iter->first.m_pFileName, iter->first.m_nLine, iter->second );
|
|
iter++;
|
|
}
|
|
}
|
|
|
|
void CDbgMemAlloc::DumpStatsFileBase( char const *pchFileBase, DumpStatsFormat_t nFormat )
|
|
{
|
|
char szFileName[MAX_PATH];
|
|
static int s_FileCount = 0;
|
|
if (m_OutputFunc == DefaultHeapReportFunc)
|
|
{
|
|
char *pPath = "";
|
|
#ifdef _X360
|
|
pPath = "D:\\";
|
|
#elif defined( _PS3 )
|
|
pPath = "/app_home/";
|
|
#endif
|
|
|
|
|
|
|
|
// [mhansen] Give out a unique filename for mem dumps
|
|
#if defined( _MEMTEST )
|
|
char szXboxName[32];
|
|
strcpy( szXboxName, "memdump" );
|
|
|
|
#if defined( _PS3 )
|
|
_snprintf( szFileName, sizeof( szFileName ), "%s%s_%d.txt", pPath, s_szStatsMapName, s_FileCount );
|
|
#else
|
|
DWORD numChars = sizeof( szXboxName );
|
|
DmGetXboxName( szXboxName, &numChars );
|
|
char *pXboxName = strstr( szXboxName, "_360" );
|
|
if ( pXboxName )
|
|
{
|
|
*pXboxName = '\0';
|
|
}
|
|
|
|
SYSTEMTIME systemTime;
|
|
GetLocalTime( &systemTime );
|
|
_snprintf( szFileName, sizeof( szFileName ), "%s%s_%2.2d%2.2d_%2.2d%2.2d%2.2d_%d.txt", pPath, s_szStatsMapName, systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wMinute, systemTime.wSecond, s_FileCount );
|
|
#endif
|
|
|
|
#else // _MEMTEST
|
|
|
|
#if defined( _WIN32 ) && !defined( _X360 )
|
|
bool fileExists = true;
|
|
while (fileExists)
|
|
{
|
|
_snprintf( szFileName, sizeof( szFileName ), "%s%s%d.txt", pPath, pchFileBase, s_FileCount );
|
|
szFileName[ ARRAYSIZE(szFileName) - 1 ] = 0;
|
|
if (_access_s(szFileName, 0) == ENOENT)
|
|
{
|
|
fileExists = false;
|
|
}
|
|
else
|
|
{
|
|
++s_FileCount;
|
|
}
|
|
}
|
|
#else // _WIN32
|
|
_snprintf( szFileName, sizeof( szFileName ), "%s%s%d.txt", pPath, pchFileBase, s_FileCount );
|
|
#endif // _WIN32
|
|
|
|
#endif // _MEMTEST
|
|
|
|
|
|
szFileName[ ARRAYSIZE(szFileName) - 1 ] = 0;
|
|
|
|
|
|
++s_FileCount;
|
|
|
|
s_DbgFile = fopen(szFileName, "wt");
|
|
if (!s_DbgFile)
|
|
return;
|
|
}
|
|
|
|
{
|
|
HEAP_LOCK();
|
|
|
|
m_OutputFunc("Allocation type\tCurrent Size(k)\tPeak Size(k)\tTotal Allocations(k)\tOverhead Size(k)\tPeak Overhead Size(k)\tTime(ms)\tCurrent Count\tPeak Count\tTotal Count\tTNum\tTCur\tTMax");
|
|
|
|
for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
|
|
{
|
|
m_OutputFunc( "\t%s", s_pCountHeader[i] );
|
|
}
|
|
|
|
m_OutputFunc("\n");
|
|
|
|
MemInfo_t totals = m_GlobalInfo;
|
|
#ifdef _PS3
|
|
{
|
|
// Add a line for system heap stats
|
|
static malloc_managed_size mms;
|
|
(g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
|
|
|
|
MemInfo_t info;
|
|
info.m_nCurrentSize = mms.current_inuse_size;
|
|
info.m_nPeakSize = mms.max_system_size;
|
|
info.m_nOverheadSize = mms.current_system_size - mms.current_inuse_size;
|
|
DumpMemInfo( "||PS3 malloc_stats||", 0, info );
|
|
|
|
// Add a line for PRXs
|
|
char prxFilename[256];
|
|
sys_prx_id_t prxIDs[256];
|
|
sys_prx_segment_info_t prxSegments[32];
|
|
sys_prx_get_module_list_t prxList = { sizeof( sys_prx_get_module_list_t ), ARRAYSIZE( prxIDs ), 0, prxIDs, NULL };
|
|
sys_prx_get_module_list( 0, &prxList );
|
|
Assert( prxList.count < ARRAYSIZE( prxIDs ) );
|
|
memset( &info, 0, sizeof( info ) );
|
|
for ( int i = 0; i < prxList.count; i++ )
|
|
{
|
|
sys_prx_module_info_t prxInfo;
|
|
prxInfo.size = sizeof( sys_prx_module_info_t );
|
|
prxInfo.filename = prxFilename;
|
|
prxInfo.filename_size = sizeof( prxFilename );
|
|
prxInfo.segments = prxSegments;
|
|
prxInfo.segments_num = ARRAYSIZE( prxSegments );
|
|
sys_prx_get_module_info( prxList.idlist[i], 0, &prxInfo );
|
|
Assert( prxInfo.segments_num < ARRAYSIZE( prxSegments ) );
|
|
for ( int j = 0; j < prxInfo.segments_num; j++ )
|
|
{
|
|
info.m_nCurrentSize += prxInfo.segments[j].memsz;
|
|
}
|
|
}
|
|
DumpMemInfo( "PS3 PRXs", 0, info );
|
|
|
|
// Add PRX sizes to our global tracked total:
|
|
totals.m_nCurrentSize += info.m_nCurrentSize;
|
|
}
|
|
#endif // _PS3
|
|
|
|
// The total of all memory usage we know about:
|
|
DumpMemInfo( "||Totals||", 0, totals );
|
|
|
|
if ( IsGameConsole() )
|
|
{
|
|
// Add a line showing total system memory usage from the OS (if this is more than
|
|
// "||Totals||", then there is unknown memory usage that we need to track down):
|
|
size_t usedMemory, freeMemory;
|
|
GlobalMemoryStatus( &usedMemory, &freeMemory );
|
|
MemInfo_t info;
|
|
info.m_nCurrentSize = usedMemory;
|
|
DumpMemInfo( "||Used Memory||", 0, info );
|
|
}
|
|
|
|
#ifdef _MEMTEST
|
|
{
|
|
// Add lines for GPU allocations
|
|
int nGPUMemSize, nGPUMemFree, nTextureSize, nRTSize, nVBSize, nIBSize, nUnknown;
|
|
if ( 7 == sscanf( s_szStatsComment, "%d %d %d %d %d %d %d", &nGPUMemSize, &nGPUMemFree, &nTextureSize, &nRTSize, &nVBSize, &nIBSize, &nUnknown ) )
|
|
{
|
|
int nTotalUsed = nTextureSize + nRTSize + nVBSize + nIBSize + nUnknown;
|
|
int nOverhead = ( nGPUMemSize - nTotalUsed ) - nGPUMemFree;
|
|
m_OutputFunc( "||PS3 RSX: total used||, line 0\t%.1f\n", nTotalUsed / 1024.0f );
|
|
m_OutputFunc( "PS3 RSX: textures, line 0\t%.1f\n", nTextureSize / 1024.0f );
|
|
m_OutputFunc( "PS3 RSX: render targets, line 0\t%.1f\n", nRTSize / 1024.0f );
|
|
m_OutputFunc( "PS3 RSX: vertex buffers, line 0\t%.1f\n", nVBSize / 1024.0f );
|
|
m_OutputFunc( "PS3 RSX: index buffers, line 0\t%.1f\n", nIBSize / 1024.0f );
|
|
m_OutputFunc( "PS3 RSX: unknown, line 0\t%.1f\n", nUnknown / 1024.0f );
|
|
m_OutputFunc( "PS3 RSX: overhead, line 0\t%.1f\n", nOverhead / 1024.0f );
|
|
}
|
|
}
|
|
#endif
|
|
|
|
//m_OutputFunc("File/Line Based\n");
|
|
DumpFileStats();
|
|
}
|
|
|
|
if (m_OutputFunc == DefaultHeapReportFunc)
|
|
{
|
|
fclose(s_DbgFile);
|
|
|
|
#if defined( _X360 )
|
|
XBX_rMemDump( szFileName );
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void CDbgMemAlloc::GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory )
|
|
{
|
|
if ( !pUsedMemory || !pFreeMemory )
|
|
return;
|
|
|
|
#if defined ( _X360 )
|
|
|
|
// GlobalMemoryStatus tells us how much physical memory is free
|
|
MEMORYSTATUS stat;
|
|
::GlobalMemoryStatus( &stat );
|
|
*pFreeMemory = stat.dwAvailPhys;
|
|
|
|
// Used is total minus free (discount the 32MB system reservation)
|
|
*pUsedMemory = ( stat.dwTotalPhys - 32*1024*1024 ) - *pFreeMemory;
|
|
|
|
#elif defined( _PS3 )
|
|
|
|
// need to factor in how much empty space there is in the heap
|
|
// (since it NEVER returns pages back to the OS after hitting a high-watermark)
|
|
static malloc_managed_size mms;
|
|
(g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
|
|
int heapFree = mms.current_system_size - mms.current_inuse_size;
|
|
Assert( heapFree >= 0 );
|
|
|
|
// sys_memory_get_user_memory_size tells us how much PPU memory is used/free
|
|
static sys_memory_info stat;
|
|
sys_memory_get_user_memory_size( &stat );
|
|
*pFreeMemory = stat.available_user_memory;
|
|
*pFreeMemory += heapFree;
|
|
*pUsedMemory = stat.total_user_memory - *pFreeMemory;
|
|
// 213MB are available in retail mode, so adjust free mem to reflect that even if we're in devkit mode
|
|
const size_t RETAIL_SIZE = 213*1024*1024;
|
|
if ( stat.total_user_memory > RETAIL_SIZE )
|
|
*pFreeMemory -= stat.total_user_memory - RETAIL_SIZE;
|
|
|
|
#else
|
|
|
|
// no data
|
|
*pFreeMemory = 0;
|
|
*pUsedMemory = 0;
|
|
|
|
#endif
|
|
}
|
|
|
|
#ifdef USE_STACK_TRACES
|
|
void CDbgMemAlloc::DumpCallStackFlow( char const *pchFileBase )
|
|
{
|
|
HEAP_LOCK();
|
|
|
|
char szFileName[MAX_PATH];
|
|
static int s_FileCount = 0;
|
|
|
|
char *pPath = "";
|
|
if ( IsX360() )
|
|
{
|
|
pPath = "D:\\";
|
|
}
|
|
|
|
#if defined( _MEMTEST ) && defined( _WIN32 )
|
|
char szXboxName[32];
|
|
strcpy( szXboxName, "xbox" );
|
|
DWORD numChars = sizeof( szXboxName );
|
|
DmGetXboxName( szXboxName, &numChars );
|
|
char *pXboxName = strstr( szXboxName, "_360" );
|
|
if ( pXboxName )
|
|
{
|
|
*pXboxName = '\0';
|
|
}
|
|
|
|
SYSTEMTIME systemTime;
|
|
GetLocalTime( &systemTime );
|
|
_snprintf( szFileName, sizeof( szFileName ), "%s%s_%2.2d%2.2d_%2.2d%2.2d%2.2d_%d.csf", pPath, s_szStatsMapName, systemTime.wMonth, systemTime.wDay, systemTime.wHour, systemTime.wMinute, systemTime.wSecond, s_FileCount );
|
|
#else
|
|
_snprintf( szFileName, sizeof( szFileName ), "%s%s%d.vcsf", pPath, pchFileBase, s_FileCount );
|
|
#endif
|
|
|
|
++s_FileCount;
|
|
m_CallStackStats.DumpToFile( szFileName, false );
|
|
}
|
|
#endif
|
|
|
|
//-----------------------------------------------------------------------------
|
|
// Stat output
|
|
//-----------------------------------------------------------------------------
|
|
void CDbgMemAlloc::DumpStats()
|
|
{
|
|
DumpStatsFileBase( "memstats" );
|
|
#ifdef USE_STACK_TRACES
|
|
DumpCallStackFlow( "memflow" );
|
|
#endif
|
|
}
|
|
|
|
void CDbgMemAlloc::SetCRTAllocFailed( size_t nSize )
|
|
{
|
|
m_sMemoryAllocFailed = nSize;
|
|
DebuggerBreakIfDebugging();
|
|
char buffer[256];
|
|
_snprintf( buffer, sizeof( buffer ), "***** OUT OF MEMORY! attempted allocation size: %u ****\n", nSize );
|
|
buffer[ ARRAYSIZE(buffer) - 1] = 0;
|
|
#if defined( _PS3 ) && defined( _DEBUG )
|
|
DebuggerBreak();
|
|
#endif // _PS3
|
|
|
|
#ifdef _X360
|
|
XBX_OutputDebugString( buffer );
|
|
if ( !Plat_IsInDebugSession() )
|
|
{
|
|
XBX_CrashDump( true );
|
|
#if defined( _DEMO )
|
|
XLaunchNewImage( XLAUNCH_KEYWORD_DEFAULT_APP, 0 );
|
|
#else
|
|
XLaunchNewImage( "default.xex", 0 );
|
|
#endif
|
|
}
|
|
#elif defined(_WIN32 )
|
|
OutputDebugString( buffer );
|
|
if ( !Plat_IsInDebugSession() )
|
|
{
|
|
AssertFatalMsg( false, buffer );
|
|
abort();
|
|
}
|
|
#else
|
|
printf( "%s\n", buffer );
|
|
if ( !Plat_IsInDebugSession() )
|
|
{
|
|
AssertFatalMsg( false, buffer );
|
|
exit( 0 );
|
|
}
|
|
#endif
|
|
}
|
|
|
|
size_t CDbgMemAlloc::MemoryAllocFailed()
|
|
{
|
|
return m_sMemoryAllocFailed;
|
|
}
|
|
|
|
|
|
|
|
#ifdef LINUX
|
|
//
|
|
// Under linux we can ask GLIBC to override malloc for us
|
|
// Base on code from Ryan, http://hg.icculus.org/icculus/mallocmonitor/file/29c4b0d049f7/monitor_client/malloc_hook_glibc.c
|
|
//
|
|
//
|
|
static void *glibc_malloc_hook = NULL;
|
|
static void *glibc_realloc_hook = NULL;
|
|
static void *glibc_memalign_hook = NULL;
|
|
static void *glibc_free_hook = NULL;
|
|
|
|
/* convenience functions for setting the hooks... */
|
|
static inline void save_glibc_hooks(void);
|
|
static inline void set_glibc_hooks(void);
|
|
static inline void set_override_hooks(void);
|
|
|
|
CThreadMutex g_HookMutex;
|
|
/*
|
|
* Our overriding hooks...they call through to the original C runtime
|
|
* implementations and report to the monitoring daemon.
|
|
*/
|
|
|
|
static void *override_malloc_hook(size_t s, const void *caller)
|
|
{
|
|
void *retval;
|
|
AUTO_LOCK( g_HookMutex );
|
|
set_glibc_hooks(); /* put glibc back in control. */
|
|
retval = InternalMalloc( s, NULL, 0 );
|
|
save_glibc_hooks(); /* update in case glibc changed them. */
|
|
|
|
set_override_hooks(); /* only restore hooks if daemon is listening */
|
|
|
|
return(retval);
|
|
} /* override_malloc_hook */
|
|
|
|
|
|
static void *override_realloc_hook(void *ptr, size_t s, const void *caller)
|
|
{
|
|
void *retval;
|
|
AUTO_LOCK( g_HookMutex );
|
|
|
|
set_glibc_hooks(); /* put glibc back in control. */
|
|
retval = InternalRealloc(ptr, s, NULL, 0); /* call glibc version. */
|
|
save_glibc_hooks(); /* update in case glibc changed them. */
|
|
|
|
set_override_hooks(); /* only restore hooks if daemon is listening */
|
|
|
|
return(retval);
|
|
} /* override_realloc_hook */
|
|
|
|
|
|
static void *override_memalign_hook(size_t a, size_t s, const void *caller)
|
|
{
|
|
void *retval;
|
|
AUTO_LOCK( g_HookMutex );
|
|
|
|
set_glibc_hooks(); /* put glibc back in control. */
|
|
retval = memalign(a, s); /* call glibc version. */
|
|
save_glibc_hooks(); /* update in case glibc changed them. */
|
|
|
|
set_override_hooks(); /* only restore hooks if daemon is listening */
|
|
|
|
return(retval);
|
|
} /* override_memalign_hook */
|
|
|
|
|
|
static void override_free_hook(void *ptr, const void *caller)
|
|
{
|
|
AUTO_LOCK( g_HookMutex );
|
|
|
|
set_glibc_hooks(); /* put glibc back in control. */
|
|
InternalFree(ptr); /* call glibc version. */
|
|
save_glibc_hooks(); /* update in case glibc changed them. */
|
|
|
|
set_override_hooks(); /* only restore hooks if daemon is listening */
|
|
} /* override_free_hook */
|
|
|
|
|
|
|
|
/*
|
|
* Convenience functions for swapping the hooks around...
|
|
*/
|
|
|
|
/*
|
|
* Save a copy of the original allocation hooks, so we can call into them
|
|
* from our overriding functions. It's possible that glibc might change
|
|
* these hooks under various conditions (so the manual's examples seem
|
|
* to suggest), so we update them whenever we finish calling into the
|
|
* the originals.
|
|
*/
|
|
static inline void save_glibc_hooks(void)
|
|
{
|
|
glibc_malloc_hook = (void *)__malloc_hook;
|
|
glibc_realloc_hook = (void *)__realloc_hook;
|
|
glibc_memalign_hook = (void *)__memalign_hook;
|
|
glibc_free_hook = (void *)__free_hook;
|
|
} /* save_glibc_hooks */
|
|
|
|
/*
|
|
* Restore the hooks to the glibc versions. This is needed since, say,
|
|
* their realloc() might call malloc() or free() under the hood, etc, so
|
|
* it's safer to let them have complete control over the subsystem, which
|
|
* also makes our logging saner, too.
|
|
*/
|
|
static inline void set_glibc_hooks(void)
|
|
{
|
|
__malloc_hook = (void* (*)(size_t, const void*))glibc_malloc_hook;
|
|
__realloc_hook = (void* (*)(void*, size_t, const void*))glibc_realloc_hook;
|
|
__memalign_hook = (void* (*)(size_t, size_t, const void*))glibc_memalign_hook;
|
|
__free_hook = (void (*)(void*, const void*))glibc_free_hook;
|
|
} /* set_glibc_hooks */
|
|
|
|
|
|
/*
|
|
* Put our hooks back in place. This should be done after the original
|
|
* glibc version has been called and we've finished any logging (which
|
|
* may call glibc functions, too). This sets us up for the next calls from
|
|
* the application.
|
|
*/
|
|
static inline void set_override_hooks(void)
|
|
{
|
|
__malloc_hook = override_malloc_hook;
|
|
__realloc_hook = override_realloc_hook;
|
|
__memalign_hook = override_memalign_hook;
|
|
__free_hook = override_free_hook;
|
|
} /* set_override_hooks */
|
|
|
|
|
|
|
|
/*
|
|
* The Hook Of All Hooks...how we get in there in the first place.
|
|
*/
|
|
|
|
/*
|
|
* glibc will call this when the malloc subsystem is initializing, giving
|
|
* us a chance to install hooks that override the functions.
|
|
*/
|
|
static void override_init_hook(void)
|
|
{
|
|
AUTO_LOCK( g_HookMutex );
|
|
|
|
/* install our hooks. Will connect to daemon on first malloc, etc. */
|
|
save_glibc_hooks();
|
|
set_override_hooks();
|
|
} /* override_init_hook */
|
|
|
|
|
|
/*
|
|
* __malloc_initialize_hook is apparently a "weak variable", so you can
|
|
* define and assign it here even though it's in glibc, too. This lets
|
|
* us hook into malloc as soon as the runtime initializes, and before
|
|
* main() is called. Basically, this whole trick depends on this.
|
|
*/
|
|
void (*__malloc_initialize_hook)(void) __attribute__((visibility("default")))= override_init_hook;
|
|
|
|
#elif defined( OSX )
|
|
//
|
|
// pointers to the osx versions of these functions
|
|
static void *osx_malloc_hook = NULL;
|
|
static void *osx_realloc_hook = NULL;
|
|
static void *osx_free_hook = NULL;
|
|
|
|
// convenience functions for setting the hooks...
|
|
static inline void save_osx_hooks(void);
|
|
static inline void set_osx_hooks(void);
|
|
static inline void set_override_hooks(void);
|
|
|
|
CThreadMutex g_HookMutex;
|
|
//
|
|
// Our overriding hooks...they call through to the original C runtime
|
|
// implementations and report to the monitoring daemon.
|
|
//
|
|
|
|
static void *override_malloc_hook(struct _malloc_zone_t *zone, size_t s)
|
|
{
|
|
void *retval;
|
|
set_osx_hooks();
|
|
retval = InternalMalloc( s, NULL, 0 );
|
|
set_override_hooks();
|
|
|
|
return(retval);
|
|
}
|
|
|
|
|
|
static void *override_realloc_hook(struct _malloc_zone_t *zone, void *ptr, size_t s)
|
|
{
|
|
void *retval;
|
|
|
|
set_osx_hooks();
|
|
retval = InternalRealloc(ptr, s, NULL, 0);
|
|
set_override_hooks();
|
|
|
|
return(retval);
|
|
}
|
|
|
|
|
|
static void override_free_hook(struct _malloc_zone_t *zone, void *ptr)
|
|
{
|
|
// sometime they pass in a null pointer from higher level calls, just ignore it
|
|
if ( !ptr )
|
|
return;
|
|
|
|
set_osx_hooks();
|
|
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( ptr );
|
|
if ( *((int*)pInternalMem->m_Reserved) == 0xf00df00d )
|
|
{
|
|
InternalFree( ptr );
|
|
}
|
|
|
|
set_override_hooks();
|
|
}
|
|
|
|
|
|
/*
|
|
|
|
These are func's we could optionally override right now on OSX but don't need to
|
|
|
|
static size_t override_size_hook(struct _malloc_zone_t *zone, const void *ptr)
|
|
{
|
|
set_osx_hooks();
|
|
DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( (void *)ptr );
|
|
set_override_hooks();
|
|
if ( *((int*)pInternalMem->m_Reserved) == 0xf00df00d )
|
|
{
|
|
return pInternalMem->nLogicalSize;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
static void *override_calloc_hook(struct _malloc_zone_t *zone, size_t num_items, size_t size )
|
|
{
|
|
void *ans = override_malloc_hook( zone, num_items*size );
|
|
if ( !ans )
|
|
return 0;
|
|
memset( ans, 0x0, num_items*size );
|
|
return ans;
|
|
}
|
|
|
|
static void *override_valloc_hook(struct _malloc_zone_t *zone, size_t size )
|
|
{
|
|
return override_calloc_hook( zone, 1, size );
|
|
}
|
|
|
|
static void override_destroy_hook(struct _malloc_zone_t *zone)
|
|
{
|
|
}
|
|
*/
|
|
|
|
|
|
|
|
//
|
|
// Save a copy of the original allocation hooks, so we can call into them
|
|
// from our overriding functions. It's possible that osx might change
|
|
// these hooks under various conditions (so the manual's examples seem
|
|
// to suggest), so we update them whenever we finish calling into the
|
|
// the originals.
|
|
//
|
|
static inline void save_osx_hooks(void)
|
|
{
|
|
malloc_zone_t *malloc_zone = malloc_default_zone();
|
|
|
|
osx_malloc_hook = (void *)malloc_zone->malloc;
|
|
osx_realloc_hook = (void *)malloc_zone->realloc;
|
|
osx_free_hook = (void *)malloc_zone->free;
|
|
|
|
// These are func's we could optionally override right now on OSX but don't need to
|
|
// osx_size_hook = (void *)malloc_zone->size;
|
|
// osx_calloc_hook = (void *)malloc_zone->calloc;
|
|
// osx_valloc_hook = (void *)malloc_zone->valloc;
|
|
// osx_destroy_hook = (void *)malloc_zone->destroy;
|
|
}
|
|
|
|
//
|
|
// Restore the hooks to the osx versions. This is needed since, say,
|
|
// their realloc() might call malloc() or free() under the hood, etc, so
|
|
// it's safer to let them have complete control over the subsystem, which
|
|
// also makes our logging saner, too.
|
|
//
|
|
static inline void set_osx_hooks(void)
|
|
{
|
|
malloc_zone_t *malloc_zone = malloc_default_zone();
|
|
malloc_zone->malloc = (void* (*)(_malloc_zone_t*, size_t))osx_malloc_hook;
|
|
malloc_zone->realloc = (void* (*)(_malloc_zone_t*, void*, size_t))osx_realloc_hook;
|
|
malloc_zone->free = (void (*)(_malloc_zone_t*, void*))osx_free_hook;
|
|
|
|
// These are func's we could optionally override right now on OSX but don't need to
|
|
|
|
//malloc_zone->size = (size_t (*)(_malloc_zone_t*, const void *))osx_size_hook;
|
|
//malloc_zone->calloc = (void* (*)(_malloc_zone_t*, size_t, size_t))osx_calloc_hook;
|
|
//malloc_zone->valloc = (void* (*)(_malloc_zone_t*, size_t))osx_valloc_hook;
|
|
//malloc_zone->destroy = (void (*)(_malloc_zone_t*))osx_destroy_hook;
|
|
}
|
|
|
|
|
|
/*
|
|
* Put our hooks back in place. This should be done after the original
|
|
* osx version has been called and we've finished any logging (which
|
|
* may call osx functions, too). This sets us up for the next calls from
|
|
* the application.
|
|
*/
|
|
static inline void set_override_hooks(void)
|
|
{
|
|
malloc_zone_t *malloc_zone = malloc_default_zone();
|
|
|
|
malloc_zone->malloc = override_malloc_hook;
|
|
malloc_zone->realloc = override_realloc_hook;
|
|
malloc_zone->free = override_free_hook;
|
|
|
|
// These are func's we could optionally override right now on OSX but don't need to
|
|
//malloc_zone->size = override_size_hook;
|
|
//malloc_zone->calloc = override_calloc_hook;
|
|
// malloc_zone->valloc = override_valloc_hook;
|
|
//malloc_zone->destroy = override_destroy_hook;
|
|
}
|
|
|
|
|
|
//
|
|
// The Hook Of All Hooks...how we get in there in the first place.
|
|
//
|
|
// osx will call this when the malloc subsystem is initializing, giving
|
|
// us a chance to install hooks that override the functions.
|
|
//
|
|
|
|
void __attribute__ ((constructor)) mem_init(void)
|
|
{
|
|
AUTO_LOCK( g_HookMutex );
|
|
save_osx_hooks();
|
|
set_override_hooks();
|
|
}
|
|
|
|
void *operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
|
|
{
|
|
set_osx_hooks();
|
|
void *pMem = g_pMemAlloc->Alloc(nSize, pFileName, nLine);
|
|
set_override_hooks();
|
|
return pMem;
|
|
}
|
|
|
|
void *operator new[] ( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
|
|
{
|
|
set_osx_hooks();
|
|
void *pMem = g_pMemAlloc->Alloc(nSize, pFileName, nLine);
|
|
set_override_hooks();
|
|
return pMem;
|
|
}
|
|
|
|
#endif // OSX
|
|
|
|
int GetAllocationCallStack( void *mem, void **pCallStackOut, int iMaxEntriesOut )
|
|
{
|
|
#if defined( USE_MEM_DEBUG ) && (defined( USE_STACK_TRACES ))
|
|
return s_DbgMemAlloc.GetCallStackForIndex( GetAllocationStatIndex_Internal( mem ), pCallStackOut, iMaxEntriesOut );
|
|
#else
|
|
return 0;
|
|
#endif
|
|
}
|
|
|
|
|
|
#endif // MEM_IMPL_TYPE_DBG
|
|
|
|
#endif // !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
|