Team Fortress 2 Source Code as on 22/4/2020
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.

2762 lines
79 KiB

  1. //===== Copyright � 1996-2005, Valve Corporation, All rights reserved. ======//
  2. //
  3. // Purpose: Memory allocation!
  4. //
  5. // $NoKeywords: $
  6. //===========================================================================//
  7. #include "pch_tier0.h"
  8. #if !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)
  9. //#include <malloc.h>
  10. #include <string.h>
  11. #include "tier0/dbg.h"
  12. #include "tier0/stackstats.h"
  13. #include "tier0/memalloc.h"
  14. #include "tier0/fasttimer.h"
  15. #include "mem_helpers.h"
  16. #ifdef PLATFORM_WINDOWS_PC
  17. #undef WIN32_LEAN_AND_MEAN
  18. #include <windows.h>
  19. #include <crtdbg.h>
  20. #endif
  21. #ifdef OSX
  22. #include <malloc/malloc.h>
  23. #include <stdlib.h>
  24. #endif
  25. #include <map>
  26. #include <set>
  27. #include <limits.h>
  28. #include "tier0/threadtools.h"
  29. #ifdef _X360
  30. #include "xbox/xbox_console.h"
  31. #endif
  32. #ifdef _PS3
  33. #include "sys/memory.h"
  34. #include "tls_ps3.h"
  35. #include "ps3/ps3_helpers.h"
  36. #include "memoverride_ps3.h"
  37. #endif
  38. #ifdef USE_LIGHT_MEM_DEBUG
  39. #undef USE_MEM_DEBUG
  40. #endif
  41. #if (!defined( POSIX ) && (defined(_DEBUG) || defined(USE_MEM_DEBUG)))
  42. #pragma message ("USE_MEM_DEBUG is enabled in a release build. Don't check this in!")
  43. #endif
  44. #include "mem_impl_type.h"
  45. #if MEM_IMPL_TYPE_DBG
  46. #if defined(_WIN32) && ( !defined(_X360) && !defined(_WIN64) )
  47. //be sure to disable frame pointer omission for all projects. "vpc /nofpo" when using stack traces
  48. //#define USE_STACK_TRACES
  49. // or:
  50. //#define USE_STACK_TRACES_DETAILED
  51. const size_t STACK_TRACE_LENGTH = 32;
  52. #endif
  53. //prevent stupid bugs from checking one and not the other
  54. #if defined( USE_STACK_TRACES_DETAILED ) && !defined( USE_STACK_TRACES )
  55. #define USE_STACK_TRACES //don't comment me. I'm a safety check
  56. #endif
  57. #if defined( USE_STACK_TRACES )
  58. #define SORT_STACK_TRACE_DESCRIPTION_DUMPS
  59. #endif
  60. #if (defined( USE_STACK_TRACES )) && !(defined( TIER0_FPO_DISABLED ) || defined( _DEBUG ))
  61. #error Stack traces will not work unless FPO is disabled for every function traced through. Rebuild everything with FPO disabled "vpc /nofpo"
  62. #endif
  63. //-----------------------------------------------------------------------------
  64. #ifdef _PS3
  65. MemOverrideRawCrtFunctions_t *g_pMemOverrideRawCrtFns;
  66. #define DebugAlloc (g_pMemOverrideRawCrtFns->pfn_malloc)
  67. #define DebugFree (g_pMemOverrideRawCrtFns->pfn_free)
  68. #elif defined( _X360 )
  69. #define DebugAlloc DmAllocatePool
  70. #define DebugFree DmFreePool
  71. #else
  72. #define DebugAlloc malloc
  73. #define DebugFree free
  74. #endif
  75. #ifdef _WIN32
  76. int g_DefaultHeapFlags = _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_ALLOC_MEM_DF );
  77. #else
  78. int g_DefaultHeapFlags = 0;
  79. #endif // win32
  80. #if defined( _MEMTEST )
  81. static char s_szStatsMapName[32];
  82. static char s_szStatsComment[256];
  83. #endif
  84. #pragma optimize( "", off )
  85. //-----------------------------------------------------------------------------
  86. #if defined( USE_STACK_TRACES )
  87. bool GetModuleFromAddress( void *address, char *pResult, int iLength )
  88. {
  89. return GetModuleNameFromAddress( address, pResult, iLength );
  90. }
  91. bool GetCallerModule( char *pDest, int iLength )
  92. {
  93. void *pCaller;
  94. GetCallStack_Fast( &pCaller, 1, 2 );
  95. return ( pCaller != 0 && GetModuleFromAddress( pCaller, pDest, iLength ) );
  96. }
  97. //
  98. // Note: StackDescribe function is non-reentrant:
  99. // Reason: Stack description is stored in a static buffer.
  100. // Solution: Passing caller-allocated buffers would allow the
  101. // function to become reentrant, however the current only client (FindOrCreateFilename)
  102. // is synchronized with a heap mutex, after retrieving stack description the
  103. // heap memory will be allocated to copy the text.
  104. //
  105. char * StackDescribe( void * const *ppAddresses, int nMaxAddresses )
  106. {
  107. static char s_chStackDescription[ 32 * 1024 ];
  108. char *pchBuffer = s_chStackDescription;
  109. #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.
  110. static void *LastCallStack[STACK_TRACE_LENGTH] = { NULL };
  111. static char *pEndPos[STACK_TRACE_LENGTH] = { NULL };
  112. bool bUseExistingString = true;
  113. #else
  114. s_chStackDescription[ 0 ] = 0;
  115. #endif
  116. int k;
  117. for ( k = 0; k < nMaxAddresses; ++ k )
  118. {
  119. if ( !ppAddresses[k] )
  120. break;
  121. #if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
  122. if( bUseExistingString && (k < STACK_TRACE_LENGTH) )
  123. {
  124. if( ppAddresses[k] == LastCallStack[k] )
  125. {
  126. pchBuffer = pEndPos[k];
  127. continue;
  128. }
  129. else
  130. {
  131. //everything from here on is invalidated
  132. bUseExistingString = false;
  133. for( int clearEntries = k; clearEntries < STACK_TRACE_LENGTH; ++clearEntries ) //wipe out unused entries
  134. {
  135. LastCallStack[clearEntries] = NULL;
  136. pEndPos[clearEntries] = NULL;
  137. }
  138. //fall through to existing code
  139. if( k == 0 )
  140. *pchBuffer = '\0';
  141. else
  142. sprintf( pchBuffer, "<--" );
  143. }
  144. }
  145. #endif
  146. {
  147. pchBuffer += strlen( pchBuffer );
  148. char szTemp[MAX_PATH];
  149. szTemp[0] = '\0';
  150. uint32 iLine = 0;
  151. uint32 iLineDisplacement = 0;
  152. uint64 iSymbolDisplacement = 0;
  153. if ( GetFileAndLineFromAddress( ppAddresses[k], szTemp, MAX_PATH, iLine, &iLineDisplacement ) )
  154. {
  155. char const *pchFileName = szTemp + strlen( szTemp );
  156. for ( size_t numSlashesAllowed = 2; pchFileName > szTemp; --pchFileName )
  157. {
  158. if ( *pchFileName == '\\' )
  159. {
  160. if ( numSlashesAllowed-- )
  161. continue;
  162. else
  163. break;
  164. }
  165. }
  166. sprintf( pchBuffer, iLineDisplacement ? "%s:%d+0x%I32X" : "%s:%d", pchFileName, iLine, iLineDisplacement );
  167. }
  168. else if ( GetSymbolNameFromAddress( ppAddresses[k], szTemp, MAX_PATH, &iSymbolDisplacement ) )
  169. {
  170. sprintf( pchBuffer, ( iSymbolDisplacement > 0 && !( iSymbolDisplacement >> 63 ) ) ? "%s+0x%llX" : "%s", szTemp, iSymbolDisplacement );
  171. }
  172. else
  173. {
  174. sprintf( pchBuffer, "#0x%08p", ppAddresses[k] );
  175. }
  176. pchBuffer += strlen( pchBuffer );
  177. sprintf( pchBuffer, "<--" );
  178. #if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
  179. if( k < STACK_TRACE_LENGTH )
  180. {
  181. LastCallStack[k] = ppAddresses[k];
  182. pEndPos[k] = pchBuffer;
  183. }
  184. #endif
  185. }
  186. }
  187. *pchBuffer = 0;
  188. #if defined( SORT_STACK_TRACE_DESCRIPTION_DUMPS )
  189. for( ; k < STACK_TRACE_LENGTH; ++k ) //wipe out unused entries
  190. {
  191. LastCallStack[k] = NULL;
  192. pEndPos[k] = NULL;
  193. }
  194. #endif
  195. return s_chStackDescription;
  196. }
  197. #else
  198. #define GetModuleFromAddress( address, pResult, iLength ) ( ( *pResult = 0 ), 0)
  199. #define GetCallerModule( pDest, iLength ) false
  200. #endif
  201. //-----------------------------------------------------------------------------
  202. // NOTE: This exactly mirrors the dbg header in the MSDEV crt
  203. // eventually when we write our own allocator, we can kill this
  204. struct CrtDbgMemHeader_t
  205. {
  206. unsigned char m_Reserved[8];
  207. const char *m_pFileName;
  208. int m_nLineNumber;
  209. unsigned char m_Reserved2[16];
  210. };
  211. struct Sentinal_t
  212. {
  213. DWORD value[4];
  214. };
  215. Sentinal_t g_HeadSentinelAllocated =
  216. {
  217. 0xeee1beef,
  218. 0xeee1f00d,
  219. 0xbd122969,
  220. 0xbeefbeef,
  221. };
  222. Sentinal_t g_HeadSentinelFree =
  223. {
  224. 0xdeadbeef,
  225. 0xbaadf00d,
  226. 0xbd122969,
  227. 0xdeadbeef,
  228. };
  229. Sentinal_t g_TailSentinel =
  230. {
  231. 0xbaadf00d,
  232. 0xbd122969,
  233. 0xdeadbeef,
  234. 0xbaadf00d,
  235. };
  236. const byte g_FreeFill = 0xdd;
  237. enum DbgMemHeaderBlockType_t
  238. {
  239. BLOCKTYPE_FREE,
  240. BLOCKTYPE_ALLOCATED
  241. };
  242. struct DbgMemHeader_t
  243. #if !defined( _DEBUG ) || defined( _PS3 )
  244. : CrtDbgMemHeader_t
  245. #endif
  246. {
  247. size_t nLogicalSize;
  248. #if defined( USE_STACK_TRACES )
  249. unsigned int nStatIndex;
  250. byte reserved[16 - (sizeof(unsigned int) * 2)]; // MS allocator always returns mem aligned on 16 bytes, which some of our code depends on
  251. #else
  252. byte reserved[16 - sizeof(unsigned int)]; // MS allocator always returns mem aligned on 16 bytes, which some of our code depends on
  253. #endif
  254. Sentinal_t sentinal;
  255. };
  256. const int g_nRecentFrees = ( IsPC() ) ? 8192 : 512;
  257. DbgMemHeader_t ** GetRecentFrees() { static DbgMemHeader_t **g_pRecentFrees = (DbgMemHeader_t**)
  258. #ifdef _PS3
  259. g_pMemOverrideRawCrtFns->pfn_calloc
  260. #else
  261. calloc
  262. #endif
  263. ( g_nRecentFrees, sizeof(DbgMemHeader_t *) );
  264. return g_pRecentFrees; }
  265. uint32 volatile g_iNextFreeSlot;
  266. uint32 volatile g_break_BytesFree = 0xffffffff;
  267. void LMDReportInvalidBlock( DbgMemHeader_t *pHeader, const char *pszMessage )
  268. {
  269. char szMsg[256];
  270. if ( pHeader )
  271. {
  272. sprintf( szMsg, "HEAP IS CORRUPT: %s (block 0x%x, %d bytes)\n", pszMessage, (size_t)( ((byte*) pHeader) + sizeof( DbgMemHeader_t ) ), pHeader->nLogicalSize );
  273. }
  274. else
  275. {
  276. sprintf( szMsg, "HEAP IS CORRUPT: %s\n", pszMessage );
  277. }
  278. Assert( !"HEAP IS CORRUPT!" );
  279. DebuggerBreak();
  280. }
  281. void LMDValidateBlock( DbgMemHeader_t *pHeader, bool bFreeList )
  282. {
  283. if ( memcmp( &pHeader->sentinal, bFreeList ? &g_HeadSentinelFree : &g_HeadSentinelAllocated, sizeof(Sentinal_t) ) != 0 )
  284. {
  285. LMDReportInvalidBlock( pHeader, "Head sentinel corrupt" );
  286. }
  287. if ( memcmp( ((Sentinal_t *)(( ((byte*) pHeader) + sizeof( DbgMemHeader_t ) + pHeader->nLogicalSize ))), &g_TailSentinel, sizeof(Sentinal_t) ) != 0 )
  288. {
  289. LMDReportInvalidBlock( pHeader, "Tail sentinel corrupt" );
  290. }
  291. if ( bFreeList )
  292. {
  293. byte *pCur = (byte *)pHeader + sizeof(DbgMemHeader_t);
  294. byte *pLimit = pCur + pHeader->nLogicalSize;
  295. while ( pCur != pLimit )
  296. {
  297. if ( *pCur++ != g_FreeFill )
  298. {
  299. LMDReportInvalidBlock( pHeader, "Write after free" );
  300. }
  301. }
  302. }
  303. }
  304. //-----------------------------------------------------------------------------
  305. #if defined( _DEBUG ) && !defined( POSIX )
  306. #define GetCrtDbgMemHeader( pMem ) ((CrtDbgMemHeader_t*)((DbgMemHeader_t*)pMem - 1) - 1)
  307. #elif defined( OSX )
  308. DbgMemHeader_t *GetCrtDbgMemHeader( void *pMem );
  309. #else
  310. #define GetCrtDbgMemHeader( pMem ) ((DbgMemHeader_t*)(pMem) - 1)
  311. #endif
  312. #if defined( USE_STACK_TRACES )
  313. #define GetAllocationStatIndex_Internal( pMem ) ( ((DbgMemHeader_t*)pMem - 1)->nStatIndex )
  314. #endif
  315. #ifdef OSX
  316. DbgMemHeader_t *GetCrtDbgMemHeader( void *pMem )
  317. {
  318. size_t msize = malloc_size( pMem );
  319. return (DbgMemHeader_t *)( (char *)pMem + msize - sizeof(DbgMemHeader_t) );
  320. }
  321. #endif
  322. inline void *InternalMalloc( size_t nSize, const char *pFileName, int nLine )
  323. {
  324. #if defined( POSIX ) || defined( _PS3 )
  325. void *pAllocedMem = NULL;
  326. #ifdef OSX
  327. pAllocedMem = malloc_zone_malloc( malloc_default_zone(), nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  328. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pAllocedMem );
  329. #elif defined( _PS3 )
  330. pAllocedMem = (g_pMemOverrideRawCrtFns->pfn_malloc)( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  331. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
  332. *((void**)pInternalMem->m_Reserved2) = pAllocedMem;
  333. #else
  334. pAllocedMem = malloc( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  335. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
  336. #endif
  337. pInternalMem->m_pFileName = pFileName;
  338. pInternalMem->m_nLineNumber = nLine;
  339. pInternalMem->nLogicalSize = nSize;
  340. *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
  341. pInternalMem->sentinal = g_HeadSentinelAllocated;
  342. *( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nSize ) ) = g_TailSentinel;
  343. LMDValidateBlock( pInternalMem, false );
  344. #ifdef OSX
  345. return pAllocedMem;
  346. #else
  347. return pInternalMem + 1;
  348. #endif
  349. #else // WIN32
  350. DbgMemHeader_t *pInternalMem;
  351. #if !defined( _DEBUG )
  352. pInternalMem = (DbgMemHeader_t *)malloc( nSize + sizeof(DbgMemHeader_t) );
  353. pInternalMem->m_pFileName = pFileName;
  354. pInternalMem->m_nLineNumber = nLine;
  355. #else
  356. pInternalMem = (DbgMemHeader_t *)_malloc_dbg( nSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
  357. #endif
  358. pInternalMem->nLogicalSize = nSize;
  359. return pInternalMem + 1;
  360. #endif // WIN32
  361. }
  362. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  363. inline void *InternalMallocAligned( size_t nSize, size_t align, const char *pFileName, int nLine )
  364. {
  365. #if defined( POSIX ) || defined( _PS3 )
  366. void *pAllocedMem = NULL;
  367. #ifdef OSX
  368. pAllocedMem = malloc_zone_malloc( malloc_default_zone(), nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  369. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pAllocedMem );
  370. #elif defined( _PS3 )
  371. size_t numWastedAlignPages = ( sizeof( DbgMemHeader_t ) / align );
  372. if ( align * numWastedAlignPages < sizeof( DbgMemHeader_t ) )
  373. ++ numWastedAlignPages;
  374. size_t nSizeRequired = nSize + numWastedAlignPages*align + sizeof( Sentinal_t );
  375. pAllocedMem = (g_pMemOverrideRawCrtFns->pfn_memalign)( align, nSizeRequired );
  376. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( ((char*)pAllocedMem) + numWastedAlignPages*align );
  377. *((void**)pInternalMem->m_Reserved2) = pAllocedMem;
  378. #else
  379. pAllocedMem = malloc( nSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  380. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pAllocedMem;
  381. #endif
  382. pInternalMem->m_pFileName = pFileName;
  383. pInternalMem->m_nLineNumber = nLine;
  384. pInternalMem->nLogicalSize = nSize;
  385. *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
  386. pInternalMem->sentinal = g_HeadSentinelAllocated;
  387. *( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nSize ) ) = g_TailSentinel;
  388. LMDValidateBlock( pInternalMem, false );
  389. #ifdef OSX
  390. return pAllocedMem;
  391. #else
  392. return pInternalMem + 1;
  393. #endif
  394. #else // WIN32
  395. DbgMemHeader_t *pInternalMem;
  396. #if !defined( _DEBUG )
  397. pInternalMem = (DbgMemHeader_t *)malloc( nSize + sizeof(DbgMemHeader_t) );
  398. pInternalMem->m_pFileName = pFileName;
  399. pInternalMem->m_nLineNumber = nLine;
  400. #else
  401. pInternalMem = (DbgMemHeader_t *)_malloc_dbg( nSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
  402. #endif
  403. pInternalMem->nLogicalSize = nSize;
  404. return pInternalMem + 1;
  405. #endif // WIN32
  406. }
  407. #endif
  408. inline void *InternalRealloc( void *pMem, size_t nNewSize, const char *pFileName, int nLine )
  409. {
  410. if ( !pMem )
  411. return InternalMalloc( nNewSize, pFileName, nLine );
  412. #ifdef POSIX
  413. void *pNewAllocedMem = NULL;
  414. #ifdef OSX
  415. pNewAllocedMem = (DbgMemHeader_t *)malloc_zone_realloc( malloc_default_zone(), pMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  416. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pNewAllocedMem );
  417. #elif defined( _PS3 )
  418. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  419. pNewAllocedMem = (DbgMemHeader_t *)(g_pMemOverrideRawCrtFns->pfn_realloc)( *((void**)pInternalMem->m_Reserved2), nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  420. pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
  421. *((void**)pInternalMem->m_Reserved2) = pNewAllocedMem;
  422. #else
  423. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  424. pNewAllocedMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  425. pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
  426. #endif
  427. pInternalMem->m_pFileName = pFileName;
  428. pInternalMem->m_nLineNumber = nLine;
  429. pInternalMem->nLogicalSize = static_cast<unsigned int>( nNewSize );
  430. *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
  431. pInternalMem->sentinal = g_HeadSentinelAllocated;
  432. *( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nNewSize ) ) = g_TailSentinel;
  433. LMDValidateBlock( pInternalMem, false );
  434. #ifdef OSX
  435. return pNewAllocedMem;
  436. #else
  437. return pInternalMem + 1;
  438. #endif
  439. #else // WIN32
  440. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  441. #if !defined( _DEBUG )
  442. pInternalMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) );
  443. pInternalMem->m_pFileName = pFileName;
  444. pInternalMem->m_nLineNumber = nLine;
  445. #else
  446. pInternalMem = (DbgMemHeader_t *)_realloc_dbg( pInternalMem, nNewSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
  447. #endif
  448. pInternalMem->nLogicalSize = nNewSize;
  449. return pInternalMem + 1;
  450. #endif // WIN32
  451. }
  452. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  453. inline void *InternalReallocAligned( void *pMem, size_t nNewSize, size_t align, const char *pFileName, int nLine )
  454. {
  455. if ( !pMem )
  456. return InternalMallocAligned( nNewSize, align, pFileName, nLine );
  457. #ifdef POSIX
  458. void *pNewAllocedMem = NULL;
  459. #ifdef OSX
  460. pNewAllocedMem = (DbgMemHeader_t *)malloc_zone_realloc( malloc_default_zone(), pMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  461. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pNewAllocedMem );
  462. #elif defined( _PS3 )
  463. size_t numWastedAlignPages = ( sizeof( DbgMemHeader_t ) / align );
  464. if ( align * numWastedAlignPages < sizeof( DbgMemHeader_t ) )
  465. ++ numWastedAlignPages;
  466. size_t nSizeRequired = nNewSize + numWastedAlignPages*align + sizeof( Sentinal_t );
  467. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  468. pNewAllocedMem = (DbgMemHeader_t *)(g_pMemOverrideRawCrtFns->pfn_reallocalign)( *((void**)pInternalMem->m_Reserved2), nSizeRequired, align );
  469. pInternalMem = GetCrtDbgMemHeader( ((char*)pNewAllocedMem) + numWastedAlignPages*align );
  470. *((void**)pInternalMem->m_Reserved2) = pNewAllocedMem;
  471. #else
  472. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  473. pNewAllocedMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) + sizeof( Sentinal_t ) );
  474. pInternalMem = (DbgMemHeader_t *)pNewAllocedMem;
  475. #endif
  476. pInternalMem->m_pFileName = pFileName;
  477. pInternalMem->m_nLineNumber = nLine;
  478. pInternalMem->nLogicalSize = static_cast<unsigned int>( nNewSize );
  479. *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
  480. pInternalMem->sentinal = g_HeadSentinelAllocated;
  481. *( (Sentinal_t *)( ((byte*)pInternalMem) + sizeof( DbgMemHeader_t ) + nNewSize ) ) = g_TailSentinel;
  482. LMDValidateBlock( pInternalMem, false );
  483. #ifdef OSX
  484. return pNewAllocedMem;
  485. #else
  486. return pInternalMem + 1;
  487. #endif
  488. #else // WIN32
  489. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  490. #if !defined( _DEBUG )
  491. pInternalMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) );
  492. pInternalMem->m_pFileName = pFileName;
  493. pInternalMem->m_nLineNumber = nLine;
  494. #else
  495. pInternalMem = (DbgMemHeader_t *)_realloc_dbg( pInternalMem, nNewSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
  496. #endif
  497. pInternalMem->nLogicalSize = nNewSize;
  498. return pInternalMem + 1;
  499. #endif // WIN32
  500. }
  501. #endif
  502. inline void InternalFree( void *pMem )
  503. {
  504. if ( !pMem )
  505. return;
  506. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  507. #if defined( POSIX )
  508. // Record it in recent free blocks list
  509. DbgMemHeader_t **pRecentFrees = GetRecentFrees();
  510. uint32 iNextSlot = ThreadInterlockedIncrement( &g_iNextFreeSlot );
  511. iNextSlot %= g_nRecentFrees;
  512. if ( memcmp( &pInternalMem->sentinal, &g_HeadSentinelAllocated, sizeof( Sentinal_t ) ) != 0 )
  513. {
  514. Assert( !"Double Free or Corrupt Block Header!" );
  515. DebuggerBreak();
  516. }
  517. LMDValidateBlock( pInternalMem, false );
  518. if ( g_break_BytesFree == pInternalMem->nLogicalSize )
  519. {
  520. DebuggerBreak();
  521. }
  522. pInternalMem->sentinal = g_HeadSentinelFree;
  523. memset( pMem, g_FreeFill, pInternalMem->nLogicalSize );
  524. DbgMemHeader_t *pToFree = pInternalMem;
  525. if ( pInternalMem->nLogicalSize < 16*1024 )
  526. {
  527. pToFree = pRecentFrees[iNextSlot];
  528. pRecentFrees[iNextSlot] = pInternalMem;
  529. if ( pToFree )
  530. {
  531. LMDValidateBlock( pToFree, true );
  532. }
  533. }
  534. // Validate several last frees
  535. for ( uint32 k = iNextSlot - 1, iteration = 0; iteration < 10; ++ iteration, -- k )
  536. {
  537. if ( DbgMemHeader_t *pLastFree = pRecentFrees[ k % g_nRecentFrees ] )
  538. {
  539. LMDValidateBlock( pLastFree, true );
  540. }
  541. }
  542. if ( !pToFree )
  543. return;
  544. #ifdef OSX
  545. malloc_zone_free( malloc_default_zone(), pToFree );
  546. #elif defined( _PS3 )
  547. (g_pMemOverrideRawCrtFns->pfn_free)( *((void**)pToFree->m_Reserved2) );
  548. #elif LINUX
  549. free( pToFree );
  550. #else
  551. free( pToFree );
  552. #endif
  553. #elif defined( _DEBUG )
  554. _free_dbg( pInternalMem, _NORMAL_BLOCK );
  555. #else
  556. free( pInternalMem );
  557. #endif
  558. }
  559. inline size_t InternalMSize( void *pMem )
  560. {
  561. #if defined( _PS3 )
  562. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  563. return pInternalMem->nLogicalSize;
  564. #elif defined(POSIX)
  565. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  566. return pInternalMem->nLogicalSize;
  567. #elif !defined(_DEBUG)
  568. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  569. return _msize( pInternalMem ) - sizeof(DbgMemHeader_t);
  570. #else
  571. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  572. return _msize_dbg( pInternalMem, _NORMAL_BLOCK ) - sizeof(DbgMemHeader_t);
  573. #endif
  574. }
  575. inline size_t InternalLogicalSize( void *pMem )
  576. {
  577. #if defined(POSIX)
  578. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  579. #elif !defined(_DEBUG)
  580. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  581. #else
  582. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  583. #endif
  584. return pInternalMem->nLogicalSize;
  585. }
  586. #ifndef _DEBUG
  587. #define _CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg ) 0
  588. #endif
  589. //-----------------------------------------------------------------------------
  590. // Custom allocator protects this module from recursing on operator new
  591. template <class T>
  592. class CNoRecurseAllocator
  593. {
  594. public:
  595. // type definitions
  596. typedef T value_type;
  597. typedef T* pointer;
  598. typedef const T* const_pointer;
  599. typedef T& reference;
  600. typedef const T& const_reference;
  601. typedef std::size_t size_type;
  602. typedef std::ptrdiff_t difference_type;
  603. CNoRecurseAllocator() {}
  604. CNoRecurseAllocator(const CNoRecurseAllocator&) {}
  605. template <class U> CNoRecurseAllocator(const CNoRecurseAllocator<U>&) {}
  606. ~CNoRecurseAllocator(){}
  607. // rebind allocator to type U
  608. template <class U > struct rebind { typedef CNoRecurseAllocator<U> other; };
  609. // return address of values
  610. pointer address (reference value) const { return &value; }
  611. const_pointer address (const_reference value) const { return &value;}
  612. size_type max_size() const { return INT_MAX; }
  613. pointer allocate(size_type num, const void* = 0) { return (pointer)DebugAlloc(num * sizeof(T)); }
  614. void deallocate (pointer p, size_type num) { DebugFree(p); }
  615. void construct(pointer p, const T& value) { new((void*)p)T(value); }
  616. void destroy (pointer p) { p->~T(); }
  617. };
  618. template <class T1, class T2>
  619. bool operator==(const CNoRecurseAllocator<T1>&, const CNoRecurseAllocator<T2>&)
  620. {
  621. return true;
  622. }
  623. template <class T1, class T2>
  624. bool operator!=(const CNoRecurseAllocator<T1>&, const CNoRecurseAllocator<T2>&)
  625. {
  626. return false;
  627. }
  628. class CStringLess
  629. {
  630. public:
  631. bool operator()(const char *pszLeft, const char *pszRight ) const
  632. {
  633. return ( V_tier0_stricmp( pszLeft, pszRight ) < 0 );
  634. }
  635. };
  636. //-----------------------------------------------------------------------------
  637. #pragma warning( disable:4074 ) // warning C4074: initializers put in compiler reserved initialization area
  638. #pragma init_seg( compiler )
  639. //-----------------------------------------------------------------------------
  640. // NOTE! This should never be called directly from leaf code
  641. // Just use new,delete,malloc,free etc. They will call into this eventually
  642. //-----------------------------------------------------------------------------
  643. class CDbgMemAlloc : public IMemAlloc
  644. {
  645. public:
  646. CDbgMemAlloc();
  647. virtual ~CDbgMemAlloc();
  648. // Release versions
  649. virtual void *Alloc( size_t nSize );
  650. virtual void *Realloc( void *pMem, size_t nSize );
  651. virtual void Free( void *pMem );
  652. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
  653. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  654. virtual void *AllocAlign( size_t nSize, size_t align );
  655. virtual void *AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine );
  656. virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align );
  657. virtual void *ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine );
  658. #endif
  659. // Debug versions
  660. virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
  661. virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
  662. virtual void Free( void *pMem, const char *pFileName, int nLine );
  663. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
  664. virtual void *RegionAlloc( int region, size_t nSize ) { return Alloc( nSize ); }
  665. virtual void *RegionAlloc( int region, size_t nSize, const char *pFileName, int nLine ) { return Alloc( nSize, pFileName, nLine ); }
  666. // Returns the size of a particular allocation (NOTE: may be larger than the size requested!)
  667. virtual size_t GetSize( void *pMem );
  668. // Force file + line information for an allocation
  669. virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
  670. virtual void PopAllocDbgInfo();
  671. virtual int32 CrtSetBreakAlloc( int32 lNewBreakAlloc );
  672. virtual int CrtSetReportMode( int nReportType, int nReportMode );
  673. virtual int CrtIsValidHeapPointer( const void *pMem );
  674. virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
  675. virtual int CrtCheckMemory( void );
  676. virtual int CrtSetDbgFlag( int nNewFlag );
  677. virtual void CrtMemCheckpoint( _CrtMemState *pState );
  678. // handles storing allocation info for coroutines
  679. virtual uint32 GetDebugInfoSize();
  680. virtual void SaveDebugInfo( void *pvDebugInfo );
  681. virtual void RestoreDebugInfo( const void *pvDebugInfo );
  682. virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine );
  683. // FIXME: Remove when we have our own allocator
  684. virtual void* CrtSetReportFile( int nRptType, void* hFile );
  685. virtual void* CrtSetReportHook( void* pfnNewHook );
  686. virtual int CrtDbgReport( int nRptType, const char * szFile,
  687. int nLine, const char * szModule, const char * szFormat );
  688. virtual int heapchk();
  689. virtual bool IsDebugHeap() { return true; }
  690. virtual int GetVersion() { return MEMALLOC_VERSION; }
  691. virtual void CompactHeap()
  692. {
  693. #if defined( _X360 ) && defined( _DEBUG )
  694. HeapCompact( GetProcessHeap(), 0 );
  695. #endif
  696. }
  697. virtual void CompactIncremental() {}
  698. virtual void OutOfMemory( size_t nBytesAttempted = 0 ) {}
  699. virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) { return NULL; } // debug heap doesn't attempt retries
  700. void SetStatsExtraInfo( const char *pMapName, const char *pComment )
  701. {
  702. #if defined( _MEMTEST )
  703. strncpy( s_szStatsMapName, pMapName, sizeof( s_szStatsMapName ) );
  704. s_szStatsMapName[sizeof( s_szStatsMapName ) - 1] = '\0';
  705. strncpy( s_szStatsComment, pComment, sizeof( s_szStatsComment ) );
  706. s_szStatsComment[sizeof( s_szStatsComment ) - 1] = '\0';
  707. #endif
  708. }
  709. virtual size_t MemoryAllocFailed();
  710. void SetCRTAllocFailed( size_t nMemSize );
  711. enum
  712. {
  713. BYTE_COUNT_16 = 0,
  714. BYTE_COUNT_32,
  715. BYTE_COUNT_128,
  716. BYTE_COUNT_2048,
  717. BYTE_COUNT_GREATER,
  718. NUM_BYTE_COUNT_BUCKETS
  719. };
  720. private:
  721. struct MemInfo_t
  722. {
  723. #if defined( USE_STACK_TRACES )
  724. DECLARE_CALLSTACKSTATSTRUCT();
  725. DECLARE_CALLSTACKSTATSTRUCT_FIELDDESCRIPTION();
  726. #endif
  727. MemInfo_t()
  728. {
  729. memset( this, 0, sizeof(*this) );
  730. }
  731. // Size in bytes
  732. size_t m_nCurrentSize;
  733. size_t m_nPeakSize;
  734. size_t m_nTotalSize;
  735. size_t m_nOverheadSize;
  736. size_t m_nPeakOverheadSize;
  737. // Count in terms of # of allocations
  738. int m_nCurrentCount;
  739. int m_nPeakCount;
  740. int m_nTotalCount;
  741. int m_nSumTargetRange;
  742. int m_nCurTargetRange;
  743. int m_nMaxTargetRange;
  744. // Count in terms of # of allocations of a particular size
  745. int m_pCount[NUM_BYTE_COUNT_BUCKETS];
  746. // Time spent allocating + deallocating (microseconds)
  747. int64 m_nTime;
  748. };
  749. struct MemInfoKey_FileLine_t
  750. {
  751. MemInfoKey_FileLine_t( const char *pFileName, int line ) : m_pFileName(pFileName), m_nLine(line) {}
  752. bool operator<( const MemInfoKey_FileLine_t &key ) const
  753. {
  754. int iret = V_tier0_stricmp( m_pFileName, key.m_pFileName );
  755. if ( iret < 0 )
  756. return true;
  757. if ( iret > 0 )
  758. return false;
  759. return m_nLine < key.m_nLine;
  760. }
  761. const char *m_pFileName;
  762. int m_nLine;
  763. };
  764. // NOTE: Deliberately using STL here because the UTL stuff
  765. // is a client of this library; want to avoid circular dependency
  766. // Maps file name to info
  767. 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;
  768. typedef StatMap_FileLine_t::iterator StatMapIter_FileLine_t;
  769. typedef StatMap_FileLine_t::value_type StatMapEntry_FileLine_t;
  770. typedef std::set<const char *, CStringLess, CNoRecurseAllocator<const char *> > Filenames_t;
  771. // Heap reporting method
  772. typedef void (*HeapReportFunc_t)( char const *pFormat, ... );
  773. private:
  774. // Returns the actual debug info
  775. virtual void GetActualDbgInfo( const char *&pFileName, int &nLine );
  776. // Finds the file in our map
  777. MemInfo_t &FindOrCreateEntry( const char *pFileName, int line );
  778. const char *FindOrCreateFilename( const char *pFileName );
  779. #if defined( USE_STACK_TRACES )
  780. int GetCallStackForIndex( unsigned int index, void **pCallStackOut, int iMaxEntriesOut );
  781. friend int GetAllocationCallStack( void *mem, void **pCallStackOut, int iMaxEntriesOut );
  782. #endif
  783. // Updates stats
  784. virtual void RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
  785. virtual void RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
  786. #if defined( USE_STACK_TRACES )
  787. void RegisterAllocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
  788. void RegisterDeallocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
  789. #endif
  790. void RegisterAllocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
  791. void RegisterDeallocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime );
  792. // Gets the allocation file name
  793. const char *GetAllocatonFileName( void *pMem );
  794. int GetAllocatonLineNumber( void *pMem );
  795. // FIXME: specify a spew output func for dumping stats
  796. // Stat output
  797. void DumpMemInfo( const char *pAllocationName, int line, const MemInfo_t &info );
  798. void DumpFileStats();
  799. #if defined( USE_STACK_TRACES )
  800. void DumpMemInfo( void * const CallStack[STACK_TRACE_LENGTH], const MemInfo_t &info );
  801. void DumpCallStackFlow( char const *pchFileBase );
  802. #endif
  803. virtual void DumpStats();
  804. virtual void DumpStatsFileBase( char const *pchFileBase );
  805. virtual void DumpBlockStats( void *p );
  806. virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory );
  807. virtual size_t ComputeMemoryUsedBy( char const *pchSubStr );
  808. virtual IVirtualMemorySection * AllocateVirtualMemorySection( size_t numMaxBytes )
  809. {
  810. #if defined( _GAMECONSOLE ) || defined( _WIN32 )
  811. extern IVirtualMemorySection * VirtualMemoryManager_AllocateVirtualMemorySection( size_t numMaxBytes );
  812. return VirtualMemoryManager_AllocateVirtualMemorySection( numMaxBytes );
  813. #else
  814. return NULL;
  815. #endif
  816. }
  817. virtual int GetGenericMemoryStats( GenericMemoryStat_t **ppMemoryStats )
  818. {
  819. // TODO: reuse code from GlobalMemoryStatus (though this is only really useful when using CStdMemAlloc...)
  820. return 0;
  821. }
  822. private:
  823. StatMap_FileLine_t m_StatMap_FileLine;
  824. #if defined( USE_STACK_TRACES )
  825. typedef CCallStackStatsGatherer<MemInfo_t, STACK_TRACE_LENGTH, GetCallStack_Fast, CCallStackStatsGatherer_StatMutexPool<128>, CNoRecurseAllocator> CallStackStatsType_t;
  826. CallStackStatsType_t m_CallStackStats;
  827. #endif
  828. MemInfo_t m_GlobalInfo;
  829. CFastTimer m_Timer;
  830. bool m_bInitialized;
  831. Filenames_t m_Filenames;
  832. HeapReportFunc_t m_OutputFunc;
  833. static size_t s_pCountSizes[NUM_BYTE_COUNT_BUCKETS];
  834. static const char *s_pCountHeader[NUM_BYTE_COUNT_BUCKETS];
  835. size_t m_sMemoryAllocFailed;
  836. };
  837. static char const *g_pszUnknown = "unknown";
  838. #if defined( USE_STACK_TRACES )
  839. BEGIN_STATSTRUCTDESCRIPTION( CDbgMemAlloc::MemInfo_t )
  840. WRITE_STATSTRUCT_FIELDDESCRIPTION();
  841. END_STATSTRUCTDESCRIPTION()
  842. BEGIN_STATSTRUCTFIELDDESCRIPTION( CDbgMemAlloc::MemInfo_t )
  843. DEFINE_STATSTRUCTFIELD( m_nCurrentSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
  844. DEFINE_STATSTRUCTFIELD( m_nPeakSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
  845. DEFINE_STATSTRUCTFIELD( m_nTotalSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
  846. DEFINE_STATSTRUCTFIELD( m_nOverheadSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
  847. DEFINE_STATSTRUCTFIELD( m_nPeakOverheadSize, BasicStatStructFieldDesc, ( BSSFT_SIZE_T, BSSFCM_ADD ) )
  848. DEFINE_STATSTRUCTFIELD( m_nCurrentCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  849. DEFINE_STATSTRUCTFIELD( m_nPeakCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  850. DEFINE_STATSTRUCTFIELD( m_nTotalCount, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  851. DEFINE_STATSTRUCTFIELD( m_nSumTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  852. DEFINE_STATSTRUCTFIELD( m_nCurTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  853. DEFINE_STATSTRUCTFIELD( m_nMaxTargetRange, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  854. DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_16, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  855. DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_32, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  856. DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_128, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  857. DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_2048, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  858. DEFINE_STATSTRUCTFIELD_ARRAYENTRY( m_pCount, BYTE_COUNT_GREATER, BasicStatStructFieldDesc, ( BSSFT_INT, BSSFCM_ADD ) )
  859. DEFINE_STATSTRUCTFIELD( m_nTime, BasicStatStructFieldDesc, ( BSSFT_INT64, BSSFCM_ADD ) )
  860. END_STATSTRUCTFIELDDESCRIPTION()
  861. #endif
  862. //-----------------------------------------------------------------------------
  863. const int DBG_INFO_STACK_DEPTH = 32;
  864. struct DbgInfoStack_t
  865. {
  866. const char *m_pFileName;
  867. int m_nLine;
  868. };
  869. #ifdef _PS3
  870. #ifndef _CERT
  871. extern TLSGlobals * ( *g_pfnElfGetTlsGlobals )();
  872. #define IfDbgInfoIsReady() if ( TLSGlobals *IfDbgInfoIsReady_pTlsGlobals = g_pfnElfGetTlsGlobals ? g_pfnElfGetTlsGlobals() : NULL )
  873. #else
  874. #define IfDbgInfoIsReady() if ( TLSGlobals *IfDbgInfoIsReady_pTlsGlobals = GetTLSGlobals() )
  875. #endif
  876. #define g_DbgInfoStack ( ( DbgInfoStack_t *& ) IfDbgInfoIsReady_pTlsGlobals->pMallocDbgInfoStack )
  877. #define g_nDbgInfoStackDepth ( IfDbgInfoIsReady_pTlsGlobals->nMallocDbgInfoStackDepth )
  878. #else
  879. CTHREADLOCALPTR( DbgInfoStack_t) g_DbgInfoStack CONSTRUCT_EARLY;
  880. CTHREADLOCALINT g_nDbgInfoStackDepth CONSTRUCT_EARLY;
  881. #define IfDbgInfoIsReady() if (true)
  882. #endif
  883. #ifdef _PS3
  884. struct CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early
  885. {
  886. CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early()
  887. {
  888. malloc_managed_size mms;
  889. mms.current_inuse_size = 0x12345678;
  890. mms.current_system_size = 0x09ABCDEF;
  891. mms.max_system_size = 0;
  892. int iResult = malloc_stats( &mms );
  893. g_pMemOverrideRawCrtFns = reinterpret_cast< MemOverrideRawCrtFunctions_t * >( iResult );
  894. }
  895. }
  896. g_CDbgMemAlloc_GetRawCrtMemOverrideFuncs_Early CONSTRUCT_EARLY;
  897. #endif
  898. //-----------------------------------------------------------------------------
  899. // Singleton...
  900. //-----------------------------------------------------------------------------
  901. static CDbgMemAlloc s_DbgMemAlloc CONSTRUCT_EARLY;
  902. #ifdef _PS3
  903. IMemAlloc *g_pMemAllocInternalPS3 = &s_DbgMemAlloc;
  904. PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3_IMPL
  905. #else // !_PS3
  906. #ifndef TIER0_VALIDATE_HEAP
  907. IMemAlloc *g_pMemAlloc CONSTRUCT_EARLY = &s_DbgMemAlloc;
  908. #else
  909. IMemAlloc *g_pActualAlloc = &s_DbgMemAlloc;
  910. #endif
  911. #endif // _PS3
  912. //-----------------------------------------------------------------------------
  913. CThreadMutex g_DbgMemMutex CONSTRUCT_EARLY;
  914. #define HEAP_LOCK() AUTO_LOCK( g_DbgMemMutex )
  915. //-----------------------------------------------------------------------------
  916. // Byte count buckets
  917. //-----------------------------------------------------------------------------
  918. size_t CDbgMemAlloc::s_pCountSizes[CDbgMemAlloc::NUM_BYTE_COUNT_BUCKETS] =
  919. {
  920. 16, 32, 128, 2048, INT_MAX
  921. };
  922. const char *CDbgMemAlloc::s_pCountHeader[CDbgMemAlloc::NUM_BYTE_COUNT_BUCKETS] =
  923. {
  924. "<=16 byte allocations",
  925. "17-32 byte allocations",
  926. "33-128 byte allocations",
  927. "129-2048 byte allocations",
  928. ">2048 byte allocations"
  929. };
  930. size_t g_TargetCountRangeMin = 0, g_TargetCountRangeMax = 0;
  931. //-----------------------------------------------------------------------------
  932. // Standard output
  933. //-----------------------------------------------------------------------------
  934. static FILE* s_DbgFile;
  935. static void DefaultHeapReportFunc( char const *pFormat, ... )
  936. {
  937. va_list args;
  938. va_start( args, pFormat );
  939. vfprintf( s_DbgFile, pFormat, args );
  940. va_end( args );
  941. }
  942. //-----------------------------------------------------------------------------
  943. // Constructor
  944. //-----------------------------------------------------------------------------
  945. CDbgMemAlloc::CDbgMemAlloc() : m_sMemoryAllocFailed( (size_t)0 )
  946. {
  947. CClockSpeedInit::Init();
  948. m_OutputFunc = DefaultHeapReportFunc;
  949. m_bInitialized = true;
  950. if ( !IsDebug() && !IsX360() )
  951. {
  952. Plat_DebugString( "USE_MEM_DEBUG is enabled in a release build. Don't check this in!\n" );
  953. }
  954. #ifdef _PS3
  955. g_pMemAllocInternalPS3 = &s_DbgMemAlloc;
  956. PLATFORM_OVERRIDE_MEM_ALLOC_INTERNAL_PS3.m_pMemAllocCached = &s_DbgMemAlloc;
  957. malloc_managed_size mms;
  958. mms.current_inuse_size = 0x12345678;
  959. mms.current_system_size = 0x09ABCDEF;
  960. mms.max_system_size = reinterpret_cast< size_t >( this );
  961. int iResult = malloc_stats( &mms );
  962. g_pMemOverrideRawCrtFns = reinterpret_cast< MemOverrideRawCrtFunctions_t * >( iResult );
  963. #endif
  964. }
  965. CDbgMemAlloc::~CDbgMemAlloc()
  966. {
  967. Filenames_t::const_iterator iter = m_Filenames.begin();
  968. while(iter != m_Filenames.end())
  969. {
  970. char *pFileName = (char*)(*iter);
  971. free( pFileName );
  972. iter++;
  973. }
  974. m_bInitialized = false;
  975. }
  976. //-----------------------------------------------------------------------------
  977. // Release versions
  978. //-----------------------------------------------------------------------------
  979. void *CDbgMemAlloc::Alloc( size_t nSize )
  980. {
  981. /*
  982. // NOTE: Uncomment this to find unknown allocations
  983. const char *pFileName = g_pszUnknown;
  984. int nLine;
  985. GetActualDbgInfo( pFileName, nLine );
  986. if (pFileName == g_pszUnknown)
  987. {
  988. int x = 3;
  989. }
  990. */
  991. char szModule[MAX_PATH];
  992. if ( GetCallerModule( szModule, MAX_PATH ) )
  993. {
  994. return Alloc( nSize, szModule, 0 );
  995. }
  996. else
  997. {
  998. return Alloc( nSize, g_pszUnknown, 0 );
  999. }
  1000. // return malloc( nSize );
  1001. }
  1002. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  1003. void *CDbgMemAlloc::AllocAlign( size_t nSize, size_t align )
  1004. {
  1005. /*
  1006. // NOTE: Uncomment this to find unknown allocations
  1007. const char *pFileName = g_pszUnknown;
  1008. int nLine;
  1009. GetActualDbgInfo( pFileName, nLine );
  1010. if (pFileName == g_pszUnknown)
  1011. {
  1012. int x = 3;
  1013. }
  1014. */
  1015. char szModule[MAX_PATH];
  1016. if ( GetCallerModule( szModule, MAX_PATH ) )
  1017. {
  1018. return AllocAlign( nSize, align, szModule, 0 );
  1019. }
  1020. else
  1021. {
  1022. return AllocAlign( nSize, align, g_pszUnknown, 0 );
  1023. }
  1024. // return malloc( nSize );
  1025. }
  1026. #endif
  1027. void *CDbgMemAlloc::Realloc( void *pMem, size_t nSize )
  1028. {
  1029. /*
  1030. // NOTE: Uncomment this to find unknown allocations
  1031. const char *pFileName = g_pszUnknown;
  1032. int nLine;
  1033. GetActualDbgInfo( pFileName, nLine );
  1034. if (pFileName == g_pszUnknown)
  1035. {
  1036. int x = 3;
  1037. }
  1038. */
  1039. // FIXME: Should these gather stats?
  1040. char szModule[MAX_PATH];
  1041. if ( GetCallerModule( szModule, MAX_PATH ) )
  1042. {
  1043. return Realloc( pMem, nSize, szModule, 0 );
  1044. }
  1045. else
  1046. {
  1047. return Realloc( pMem, nSize, g_pszUnknown, 0 );
  1048. }
  1049. // return realloc( pMem, nSize );
  1050. }
  1051. void CDbgMemAlloc::Free( void *pMem )
  1052. {
  1053. // FIXME: Should these gather stats?
  1054. Free( pMem, g_pszUnknown, 0 );
  1055. // free( pMem );
  1056. }
  1057. void *CDbgMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize )
  1058. {
  1059. return NULL;
  1060. }
  1061. //-----------------------------------------------------------------------------
  1062. // Force file + line information for an allocation
  1063. //-----------------------------------------------------------------------------
  1064. void CDbgMemAlloc::PushAllocDbgInfo( const char *pFileName, int nLine )
  1065. {
  1066. IfDbgInfoIsReady()
  1067. {
  1068. if ( g_DbgInfoStack == NULL )
  1069. {
  1070. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  1071. g_nDbgInfoStackDepth = -1;
  1072. }
  1073. ++g_nDbgInfoStackDepth;
  1074. Assert( g_nDbgInfoStackDepth < DBG_INFO_STACK_DEPTH );
  1075. g_DbgInfoStack[g_nDbgInfoStackDepth].m_pFileName = FindOrCreateFilename( pFileName );
  1076. g_DbgInfoStack[g_nDbgInfoStackDepth].m_nLine = nLine;
  1077. }
  1078. }
  1079. void CDbgMemAlloc::PopAllocDbgInfo()
  1080. {
  1081. IfDbgInfoIsReady()
  1082. {
  1083. if ( g_DbgInfoStack == NULL )
  1084. {
  1085. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  1086. g_nDbgInfoStackDepth = -1;
  1087. }
  1088. --g_nDbgInfoStackDepth;
  1089. Assert( g_nDbgInfoStackDepth >= -1 );
  1090. }
  1091. }
  1092. //-----------------------------------------------------------------------------
  1093. // handles storing allocation info for coroutines
  1094. //-----------------------------------------------------------------------------
  1095. uint32 CDbgMemAlloc::GetDebugInfoSize()
  1096. {
  1097. return sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH + sizeof( int32 );
  1098. }
  1099. void CDbgMemAlloc::SaveDebugInfo( void *pvDebugInfo )
  1100. {
  1101. IfDbgInfoIsReady()
  1102. {
  1103. if ( g_DbgInfoStack == NULL )
  1104. {
  1105. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  1106. g_nDbgInfoStackDepth = -1;
  1107. }
  1108. int32 *pnStackDepth = (int32*) pvDebugInfo;
  1109. *pnStackDepth = g_nDbgInfoStackDepth;
  1110. memcpy( pnStackDepth+1, &g_DbgInfoStack[0], sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH );
  1111. }
  1112. }
  1113. void CDbgMemAlloc::RestoreDebugInfo( const void *pvDebugInfo )
  1114. {
  1115. IfDbgInfoIsReady()
  1116. {
  1117. if ( g_DbgInfoStack == NULL )
  1118. {
  1119. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  1120. g_nDbgInfoStackDepth = -1;
  1121. }
  1122. const int32 *pnStackDepth = (const int32*) pvDebugInfo;
  1123. g_nDbgInfoStackDepth = *pnStackDepth;
  1124. memcpy( &g_DbgInfoStack[0], pnStackDepth+1, sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH );
  1125. }
  1126. }
  1127. void CDbgMemAlloc::InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine )
  1128. {
  1129. int32 *pnStackDepth = (int32*) pvDebugInfo;
  1130. if( pchRootFileName )
  1131. {
  1132. *pnStackDepth = 0;
  1133. DbgInfoStack_t *pStackRoot = (DbgInfoStack_t *)(pnStackDepth + 1);
  1134. pStackRoot->m_pFileName = FindOrCreateFilename( pchRootFileName );
  1135. pStackRoot->m_nLine = nLine;
  1136. }
  1137. else
  1138. {
  1139. *pnStackDepth = -1;
  1140. }
  1141. }
  1142. //-----------------------------------------------------------------------------
  1143. // Returns the actual debug info
  1144. //-----------------------------------------------------------------------------
  1145. void CDbgMemAlloc::GetActualDbgInfo( const char *&pFileName, int &nLine )
  1146. {
  1147. #if defined( USE_STACK_TRACES_DETAILED )
  1148. return;
  1149. #endif
  1150. IfDbgInfoIsReady()
  1151. {
  1152. if ( g_DbgInfoStack == NULL )
  1153. {
  1154. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  1155. g_nDbgInfoStackDepth = -1;
  1156. }
  1157. if ( g_nDbgInfoStackDepth >= 0 && g_DbgInfoStack[0].m_pFileName)
  1158. {
  1159. pFileName = g_DbgInfoStack[0].m_pFileName;
  1160. nLine = g_DbgInfoStack[0].m_nLine;
  1161. }
  1162. }
  1163. }
  1164. //-----------------------------------------------------------------------------
  1165. //
  1166. //-----------------------------------------------------------------------------
  1167. const char *CDbgMemAlloc::FindOrCreateFilename( const char *pFileName )
  1168. {
  1169. // If we created it for the first time, actually *allocate* the filename memory
  1170. HEAP_LOCK();
  1171. // This is necessary for shutdown conditions: the file name is stored
  1172. // in some piece of memory in a DLL; if that DLL becomes unloaded,
  1173. // we'll have a pointer to crap memory
  1174. if ( !pFileName )
  1175. {
  1176. pFileName = g_pszUnknown;
  1177. }
  1178. #if defined( USE_STACK_TRACES_DETAILED )
  1179. {
  1180. // Walk the stack to determine what's causing the allocation
  1181. void *arrStackAddresses[ 10 ] = { 0 };
  1182. int numStackAddrRetrieved = GetCallStack_Fast( arrStackAddresses, 10, 2 ); //Skip this function, and either CDbgMemAlloc::Alloc() or CDbgMemAlloc::Realloc()
  1183. char *szStack = StackDescribe( arrStackAddresses, numStackAddrRetrieved );
  1184. if ( szStack && *szStack )
  1185. {
  1186. pFileName = szStack; // Use the stack description for the allocation
  1187. }
  1188. }
  1189. #endif // #if defined( USE_STACK_TRACES_DETAILED )
  1190. char *pszFilenameCopy;
  1191. Filenames_t::const_iterator iter = m_Filenames.find( pFileName );
  1192. if ( iter == m_Filenames.end() )
  1193. {
  1194. size_t nLen = strlen(pFileName) + 1;
  1195. pszFilenameCopy = (char *)DebugAlloc( nLen );
  1196. memcpy( pszFilenameCopy, pFileName, nLen );
  1197. m_Filenames.insert( pszFilenameCopy );
  1198. }
  1199. else
  1200. {
  1201. pszFilenameCopy = (char *)(*iter);
  1202. }
  1203. return pszFilenameCopy;
  1204. }
  1205. //-----------------------------------------------------------------------------
  1206. // Finds the file in our map
  1207. //-----------------------------------------------------------------------------
  1208. CDbgMemAlloc::MemInfo_t &CDbgMemAlloc::FindOrCreateEntry( const char *pFileName, int line )
  1209. {
  1210. // Oh how I love crazy STL. retval.first == the StatMapIter_t in the std::pair
  1211. // retval.first->second == the MemInfo_t that's part of the StatMapIter_t
  1212. std::pair<StatMapIter_FileLine_t, bool> retval;
  1213. retval = m_StatMap_FileLine.insert( StatMapEntry_FileLine_t( MemInfoKey_FileLine_t( pFileName, line ), MemInfo_t() ) );
  1214. return retval.first->second;
  1215. }
  1216. #if defined( USE_STACK_TRACES )
  1217. int CDbgMemAlloc::GetCallStackForIndex( unsigned int index, void **pCallStackOut, int iMaxEntriesOut )
  1218. {
  1219. if( iMaxEntriesOut > STACK_TRACE_LENGTH )
  1220. iMaxEntriesOut = STACK_TRACE_LENGTH;
  1221. CallStackStatsType_t::StackReference stackRef = m_CallStackStats.GetCallStackForIndex( index );
  1222. memcpy( pCallStackOut, stackRef, iMaxEntriesOut * sizeof( void * ) );
  1223. for( int i = 0; i != iMaxEntriesOut; ++i )
  1224. {
  1225. if( pCallStackOut[i] == NULL )
  1226. return i;
  1227. }
  1228. return iMaxEntriesOut;
  1229. }
  1230. #endif
  1231. //-----------------------------------------------------------------------------
  1232. // Updates stats
  1233. //-----------------------------------------------------------------------------
  1234. void CDbgMemAlloc::RegisterAllocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
  1235. {
  1236. HEAP_LOCK();
  1237. RegisterAllocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
  1238. RegisterAllocation( FindOrCreateEntry( pFileName, nLine ), nLogicalSize, nActualSize, nTime );
  1239. }
  1240. void CDbgMemAlloc::RegisterDeallocation( const char *pFileName, int nLine, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
  1241. {
  1242. HEAP_LOCK();
  1243. RegisterDeallocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
  1244. RegisterDeallocation( FindOrCreateEntry( pFileName, nLine ), nLogicalSize, nActualSize, nTime );
  1245. }
  1246. #if defined( USE_STACK_TRACES )
  1247. void CDbgMemAlloc::RegisterAllocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
  1248. {
  1249. HEAP_LOCK();
  1250. RegisterAllocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
  1251. CCallStackStatsGatherer_StructAccessor_AutoLock<MemInfo_t> entryAccessor = m_CallStackStats.GetEntry( nStatIndex );
  1252. RegisterAllocation( *entryAccessor.GetStruct(), nLogicalSize, nActualSize, nTime );
  1253. }
  1254. void CDbgMemAlloc::RegisterDeallocation( unsigned int nStatIndex, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
  1255. {
  1256. HEAP_LOCK();
  1257. RegisterDeallocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
  1258. CCallStackStatsGatherer_StructAccessor_AutoLock<MemInfo_t> entryAccessor = m_CallStackStats.GetEntry( nStatIndex );
  1259. RegisterDeallocation( *entryAccessor.GetStruct(), nLogicalSize, nActualSize, nTime );
  1260. }
  1261. #endif
  1262. void CDbgMemAlloc::RegisterAllocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
  1263. {
  1264. ++info.m_nCurrentCount;
  1265. ++info.m_nTotalCount;
  1266. if (info.m_nCurrentCount > info.m_nPeakCount)
  1267. {
  1268. info.m_nPeakCount = info.m_nCurrentCount;
  1269. }
  1270. info.m_nCurrentSize += nLogicalSize;
  1271. info.m_nTotalSize += nLogicalSize;
  1272. if (info.m_nCurrentSize > info.m_nPeakSize)
  1273. {
  1274. info.m_nPeakSize = info.m_nCurrentSize;
  1275. }
  1276. if ( nLogicalSize > g_TargetCountRangeMin && nLogicalSize <= g_TargetCountRangeMax )
  1277. {
  1278. info.m_nSumTargetRange++;
  1279. info.m_nCurTargetRange++;
  1280. if ( info.m_nCurTargetRange > info.m_nMaxTargetRange )
  1281. {
  1282. info.m_nMaxTargetRange = info.m_nCurTargetRange;
  1283. }
  1284. }
  1285. for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
  1286. {
  1287. if (nLogicalSize <= s_pCountSizes[i])
  1288. {
  1289. ++info.m_pCount[i];
  1290. break;
  1291. }
  1292. }
  1293. Assert( info.m_nPeakCount >= info.m_nCurrentCount );
  1294. Assert( info.m_nPeakSize >= info.m_nCurrentSize );
  1295. info.m_nOverheadSize += (nActualSize - nLogicalSize);
  1296. if (info.m_nOverheadSize > info.m_nPeakOverheadSize)
  1297. {
  1298. info.m_nPeakOverheadSize = info.m_nOverheadSize;
  1299. }
  1300. info.m_nTime += nTime;
  1301. }
  1302. void CDbgMemAlloc::RegisterDeallocation( MemInfo_t &info, size_t nLogicalSize, size_t nActualSize, unsigned nTime )
  1303. {
  1304. --info.m_nCurrentCount;
  1305. info.m_nCurrentSize -= nLogicalSize;
  1306. for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
  1307. {
  1308. if (nLogicalSize <= s_pCountSizes[i])
  1309. {
  1310. --info.m_pCount[i];
  1311. break;
  1312. }
  1313. }
  1314. if ( nLogicalSize > g_TargetCountRangeMin && nLogicalSize <= g_TargetCountRangeMax )
  1315. {
  1316. info.m_nCurTargetRange--;
  1317. }
  1318. Assert( info.m_nPeakCount >= info.m_nCurrentCount );
  1319. Assert( info.m_nPeakSize >= info.m_nCurrentSize );
  1320. Assert( info.m_nCurrentCount >= 0 );
  1321. Assert( info.m_nCurrentSize >= 0 );
  1322. info.m_nOverheadSize -= (nActualSize - nLogicalSize);
  1323. info.m_nTime += nTime;
  1324. }
  1325. //-----------------------------------------------------------------------------
  1326. // Gets the allocation file name
  1327. //-----------------------------------------------------------------------------
  1328. const char *CDbgMemAlloc::GetAllocatonFileName( void *pMem )
  1329. {
  1330. if (!pMem)
  1331. return "";
  1332. CrtDbgMemHeader_t *pHeader = GetCrtDbgMemHeader( pMem );
  1333. if ( pHeader->m_pFileName )
  1334. return pHeader->m_pFileName;
  1335. else
  1336. return g_pszUnknown;
  1337. }
  1338. //-----------------------------------------------------------------------------
  1339. // Gets the allocation file name
  1340. //-----------------------------------------------------------------------------
  1341. int CDbgMemAlloc::GetAllocatonLineNumber( void *pMem )
  1342. {
  1343. if ( !pMem )
  1344. return 0;
  1345. CrtDbgMemHeader_t *pHeader = GetCrtDbgMemHeader( pMem );
  1346. return pHeader->m_nLineNumber;
  1347. }
  1348. //-----------------------------------------------------------------------------
  1349. // Debug versions of the main allocation methods
  1350. //-----------------------------------------------------------------------------
  1351. void *CDbgMemAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
  1352. {
  1353. HEAP_LOCK();
  1354. #if defined( USE_STACK_TRACES )
  1355. unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndex( CCallStackStorage( m_CallStackStats.StackFunction, 1 ) );
  1356. #endif
  1357. if ( !m_bInitialized )
  1358. {
  1359. void *pRetval = InternalMalloc( nSize, pFileName, nLine );
  1360. #if defined( USE_STACK_TRACES )
  1361. if( pRetval )
  1362. {
  1363. GetAllocationStatIndex_Internal( pRetval ) = iStatEntryIndex;
  1364. }
  1365. #endif
  1366. return pRetval;
  1367. }
  1368. if ( pFileName != g_pszUnknown )
  1369. pFileName = FindOrCreateFilename( pFileName );
  1370. GetActualDbgInfo( pFileName, nLine );
  1371. /*
  1372. if ( strcmp( pFileName, "class CUtlVector<int,class CUtlMemory<int> >" ) == 0)
  1373. {
  1374. GetActualDbgInfo( pFileName, nLine );
  1375. }
  1376. */
  1377. m_Timer.Start();
  1378. void *pMem = InternalMalloc( nSize, pFileName, nLine );
  1379. m_Timer.End();
  1380. #if defined( USE_STACK_TRACES )
  1381. if( pMem )
  1382. {
  1383. GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
  1384. }
  1385. #endif
  1386. ApplyMemoryInitializations( pMem, nSize );
  1387. #if defined( USE_STACK_TRACES )
  1388. RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1389. #else
  1390. RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1391. #endif
  1392. if ( !pMem )
  1393. {
  1394. SetCRTAllocFailed( nSize );
  1395. }
  1396. return pMem;
  1397. }
  1398. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  1399. void *CDbgMemAlloc::AllocAlign( size_t nSize, size_t align, const char *pFileName, int nLine )
  1400. {
  1401. HEAP_LOCK();
  1402. #if defined( USE_STACK_TRACES )
  1403. unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndexForCurrentCallStack( 1 );
  1404. #endif
  1405. if ( !m_bInitialized )
  1406. {
  1407. void *pRetval = InternalMalloc( nSize, pFileName, nLine );
  1408. #if defined( USE_STACK_TRACES )
  1409. if( pRetval )
  1410. {
  1411. GetAllocationStatIndex_Internal( pRetval ) = iStatEntryIndex;
  1412. }
  1413. #endif
  1414. return pRetval;
  1415. }
  1416. if ( pFileName != g_pszUnknown )
  1417. pFileName = FindOrCreateFilename( pFileName );
  1418. GetActualDbgInfo( pFileName, nLine );
  1419. /*
  1420. if ( strcmp( pFileName, "class CUtlVector<int,class CUtlMemory<int> >" ) == 0)
  1421. {
  1422. GetActualDbgInfo( pFileName, nLine );
  1423. }
  1424. */
  1425. m_Timer.Start();
  1426. void *pMem = InternalMallocAligned( nSize, align, pFileName, nLine );
  1427. m_Timer.End();
  1428. #if defined( USE_STACK_TRACES )
  1429. if( pMem )
  1430. {
  1431. GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
  1432. }
  1433. #endif
  1434. ApplyMemoryInitializations( pMem, nSize );
  1435. #if defined( USE_STACK_TRACES )
  1436. RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1437. #else
  1438. RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1439. #endif
  1440. if ( !pMem )
  1441. {
  1442. SetCRTAllocFailed( nSize );
  1443. }
  1444. return pMem;
  1445. }
  1446. #endif
  1447. void *CDbgMemAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
  1448. {
  1449. HEAP_LOCK();
  1450. pFileName = FindOrCreateFilename( pFileName );
  1451. #if defined( USE_STACK_TRACES )
  1452. unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndex( CCallStackStorage( m_CallStackStats.StackFunction, 1 ) );
  1453. #endif
  1454. if ( !m_bInitialized )
  1455. {
  1456. pMem = InternalRealloc( pMem, nSize, pFileName, nLine );
  1457. #if defined( USE_STACK_TRACES )
  1458. if( pMem )
  1459. {
  1460. GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
  1461. }
  1462. #endif
  1463. return pMem;
  1464. }
  1465. if ( pMem != 0 )
  1466. {
  1467. #if defined( USE_STACK_TRACES )
  1468. RegisterDeallocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
  1469. #else
  1470. RegisterDeallocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
  1471. #endif
  1472. }
  1473. GetActualDbgInfo( pFileName, nLine );
  1474. m_Timer.Start();
  1475. pMem = InternalRealloc( pMem, nSize, pFileName, nLine );
  1476. m_Timer.End();
  1477. #if defined( USE_STACK_TRACES )
  1478. if( pMem )
  1479. {
  1480. GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
  1481. }
  1482. #endif
  1483. #if defined( USE_STACK_TRACES )
  1484. RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1485. #else
  1486. RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1487. #endif
  1488. if ( !pMem )
  1489. {
  1490. SetCRTAllocFailed( nSize );
  1491. }
  1492. return pMem;
  1493. }
  1494. #ifdef MEMALLOC_SUPPORTS_ALIGNED_ALLOCATIONS
  1495. void *CDbgMemAlloc::ReallocAlign( void *pMem, size_t nSize, size_t align )
  1496. {
  1497. /*
  1498. // NOTE: Uncomment this to find unknown allocations
  1499. const char *pFileName = g_pszUnknown;
  1500. int nLine;
  1501. GetActualDbgInfo( pFileName, nLine );
  1502. if (pFileName == g_pszUnknown)
  1503. {
  1504. int x = 3;
  1505. }
  1506. */
  1507. char szModule[MAX_PATH];
  1508. if ( GetCallerModule( szModule, MAX_PATH ) )
  1509. {
  1510. return ReallocAlign( pMem, nSize, align, szModule, 0 );
  1511. }
  1512. else
  1513. {
  1514. return ReallocAlign( pMem, nSize, align, g_pszUnknown, 0 );
  1515. }
  1516. // return malloc( nSize );
  1517. }
  1518. void *CDbgMemAlloc::ReallocAlign( void *pMem, size_t nSize, size_t align, const char *pFileName, int nLine )
  1519. {
  1520. HEAP_LOCK();
  1521. pFileName = FindOrCreateFilename( pFileName );
  1522. #if defined( USE_STACK_TRACES )
  1523. unsigned int iStatEntryIndex = m_CallStackStats.GetEntryIndexForCurrentCallStack( 1 );
  1524. #endif
  1525. if ( !m_bInitialized )
  1526. {
  1527. pMem = InternalReallocAligned( pMem, nSize, align, pFileName, nLine );
  1528. #if defined( USE_STACK_TRACES )
  1529. if( pMem )
  1530. {
  1531. GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
  1532. }
  1533. #endif
  1534. return pMem;
  1535. }
  1536. if ( pMem != 0 )
  1537. {
  1538. #if defined( USE_STACK_TRACES )
  1539. RegisterDeallocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
  1540. #else
  1541. RegisterDeallocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), 0 );
  1542. #endif
  1543. }
  1544. GetActualDbgInfo( pFileName, nLine );
  1545. m_Timer.Start();
  1546. pMem = InternalReallocAligned( pMem, nSize, align, pFileName, nLine );
  1547. m_Timer.End();
  1548. #if defined( USE_STACK_TRACES )
  1549. if( pMem )
  1550. {
  1551. GetAllocationStatIndex_Internal( pMem ) = iStatEntryIndex;
  1552. }
  1553. #endif
  1554. #if defined( USE_STACK_TRACES )
  1555. RegisterAllocation( GetAllocationStatIndex_Internal( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1556. #else
  1557. RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1558. #endif
  1559. if ( !pMem )
  1560. {
  1561. SetCRTAllocFailed( nSize );
  1562. }
  1563. return pMem;
  1564. }
  1565. #endif
  1566. void CDbgMemAlloc::Free( void *pMem, const char * /*pFileName*/, int nLine )
  1567. {
  1568. if ( !pMem )
  1569. return;
  1570. HEAP_LOCK();
  1571. if ( !m_bInitialized )
  1572. {
  1573. InternalFree( pMem );
  1574. return;
  1575. }
  1576. size_t nOldLogicalSize = InternalLogicalSize( pMem );
  1577. size_t nOldSize = InternalMSize( pMem );
  1578. #if defined( USE_STACK_TRACES )
  1579. unsigned int oldStatIndex = GetAllocationStatIndex_Internal( pMem );
  1580. #else
  1581. const char *pOldFileName = GetAllocatonFileName( pMem );
  1582. int oldLine = GetAllocatonLineNumber( pMem );
  1583. #endif
  1584. m_Timer.Start();
  1585. InternalFree( pMem );
  1586. m_Timer.End();
  1587. #if defined( USE_STACK_TRACES )
  1588. RegisterDeallocation( oldStatIndex, nOldLogicalSize, nOldSize, m_Timer.GetDuration().GetMicroseconds() );
  1589. #else
  1590. RegisterDeallocation( pOldFileName, oldLine, nOldLogicalSize, nOldSize, m_Timer.GetDuration().GetMicroseconds() );
  1591. #endif
  1592. }
  1593. void *CDbgMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine )
  1594. {
  1595. return NULL;
  1596. }
  1597. //-----------------------------------------------------------------------------
  1598. // Returns the size of a particular allocation (NOTE: may be larger than the size requested!)
  1599. //-----------------------------------------------------------------------------
  1600. size_t CDbgMemAlloc::GetSize( void *pMem )
  1601. {
  1602. HEAP_LOCK();
  1603. if ( !pMem )
  1604. return m_GlobalInfo.m_nCurrentSize;
  1605. return InternalMSize( pMem );
  1606. }
  1607. //-----------------------------------------------------------------------------
  1608. // FIXME: Remove when we make our own heap! Crt stuff we're currently using
  1609. //-----------------------------------------------------------------------------
  1610. int32 CDbgMemAlloc::CrtSetBreakAlloc( int32 lNewBreakAlloc )
  1611. {
  1612. #ifdef POSIX
  1613. return 0;
  1614. #else
  1615. return _CrtSetBreakAlloc( lNewBreakAlloc );
  1616. #endif
  1617. }
  1618. int CDbgMemAlloc::CrtSetReportMode( int nReportType, int nReportMode )
  1619. {
  1620. #ifdef POSIX
  1621. return 0;
  1622. #else
  1623. return _CrtSetReportMode( nReportType, nReportMode );
  1624. #endif
  1625. }
  1626. int CDbgMemAlloc::CrtIsValidHeapPointer( const void *pMem )
  1627. {
  1628. #ifdef POSIX
  1629. return 0;
  1630. #else
  1631. return _CrtIsValidHeapPointer( pMem );
  1632. #endif
  1633. }
  1634. int CDbgMemAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access )
  1635. {
  1636. #ifdef POSIX
  1637. return 0;
  1638. #else
  1639. return _CrtIsValidPointer( pMem, size, access );
  1640. #endif
  1641. }
  1642. #define DBGMEM_CHECKMEMORY 1
  1643. int CDbgMemAlloc::CrtCheckMemory( void )
  1644. {
  1645. #if !defined( DBGMEM_CHECKMEMORY ) || defined( POSIX )
  1646. return 1;
  1647. #elif defined( _WIN32 )
  1648. if ( !_CrtCheckMemory())
  1649. {
  1650. Msg( "Memory check failed!\n" );
  1651. return 0;
  1652. }
  1653. return 1;
  1654. #else
  1655. return 1;
  1656. #endif
  1657. }
  1658. int CDbgMemAlloc::CrtSetDbgFlag( int nNewFlag )
  1659. {
  1660. #ifdef POSIX
  1661. return 0;
  1662. #else
  1663. return _CrtSetDbgFlag( nNewFlag );
  1664. #endif
  1665. }
  1666. void CDbgMemAlloc::CrtMemCheckpoint( _CrtMemState *pState )
  1667. {
  1668. #ifndef POSIX
  1669. _CrtMemCheckpoint( pState );
  1670. #endif
  1671. }
  1672. // FIXME: Remove when we have our own allocator
  1673. void* CDbgMemAlloc::CrtSetReportFile( int nRptType, void* hFile )
  1674. {
  1675. #ifdef POSIX
  1676. return 0;
  1677. #else
  1678. return (void*)_CrtSetReportFile( nRptType, (_HFILE)hFile );
  1679. #endif
  1680. }
  1681. void* CDbgMemAlloc::CrtSetReportHook( void* pfnNewHook )
  1682. {
  1683. #ifdef POSIX
  1684. return 0;
  1685. #else
  1686. return (void*)_CrtSetReportHook( (_CRT_REPORT_HOOK)pfnNewHook );
  1687. #endif
  1688. }
  1689. int CDbgMemAlloc::CrtDbgReport( int nRptType, const char * szFile,
  1690. int nLine, const char * szModule, const char * pMsg )
  1691. {
  1692. #ifdef POSIX
  1693. return 0;
  1694. #else
  1695. return _CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg );
  1696. #endif
  1697. }
  1698. int CDbgMemAlloc::heapchk()
  1699. {
  1700. #ifdef POSIX
  1701. return 0;
  1702. #else
  1703. if ( CrtCheckMemory() )
  1704. return _HEAPOK;
  1705. else
  1706. return _HEAPBADPTR;
  1707. #endif
  1708. }
  1709. void CDbgMemAlloc::DumpBlockStats( void *p )
  1710. {
  1711. DbgMemHeader_t *pBlock = (DbgMemHeader_t *)p - 1;
  1712. if ( !CrtIsValidHeapPointer( pBlock ) )
  1713. {
  1714. Msg( "0x%x is not valid heap pointer\n", p );
  1715. return;
  1716. }
  1717. const char *pFileName = GetAllocatonFileName( p );
  1718. int line = GetAllocatonLineNumber( p );
  1719. Msg( "0x%x allocated by %s line %d, %d bytes\n", p, pFileName, line, GetSize( p ) );
  1720. }
  1721. //-----------------------------------------------------------------------------
  1722. // Stat output
  1723. //-----------------------------------------------------------------------------
  1724. void CDbgMemAlloc::DumpMemInfo( const char *pAllocationName, int line, const MemInfo_t &info )
  1725. {
  1726. 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",
  1727. pAllocationName,
  1728. line,
  1729. info.m_nCurrentSize / 1024.0f,
  1730. info.m_nPeakSize / 1024.0f,
  1731. info.m_nTotalSize / 1024.0f,
  1732. info.m_nOverheadSize / 1024.0f,
  1733. info.m_nPeakOverheadSize / 1024.0f,
  1734. (int)(info.m_nTime / 1000),
  1735. info.m_nCurrentCount,
  1736. info.m_nPeakCount,
  1737. info.m_nTotalCount,
  1738. info.m_nSumTargetRange,
  1739. info.m_nCurTargetRange,
  1740. info.m_nMaxTargetRange
  1741. );
  1742. for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
  1743. {
  1744. m_OutputFunc( "\t%d", info.m_pCount[i] );
  1745. }
  1746. m_OutputFunc("\n");
  1747. }
  1748. //-----------------------------------------------------------------------------
  1749. // Stat output
  1750. //-----------------------------------------------------------------------------
  1751. size_t CDbgMemAlloc::ComputeMemoryUsedBy( char const *pchSubStr)
  1752. {
  1753. size_t total = 0;
  1754. StatMapIter_FileLine_t iter = m_StatMap_FileLine.begin();
  1755. while(iter != m_StatMap_FileLine.end())
  1756. {
  1757. if(!pchSubStr || strstr(iter->first.m_pFileName,pchSubStr))
  1758. {
  1759. total += iter->second.m_nCurrentSize;
  1760. }
  1761. iter++;
  1762. }
  1763. return total;
  1764. }
  1765. void CDbgMemAlloc::DumpFileStats()
  1766. {
  1767. StatMapIter_FileLine_t iter = m_StatMap_FileLine.begin();
  1768. while(iter != m_StatMap_FileLine.end())
  1769. {
  1770. DumpMemInfo( iter->first.m_pFileName, iter->first.m_nLine, iter->second );
  1771. iter++;
  1772. }
  1773. }
  1774. void CDbgMemAlloc::DumpStatsFileBase( char const *pchFileBase )
  1775. {
  1776. char szFileName[MAX_PATH];
  1777. static int s_FileCount = 0;
  1778. if (m_OutputFunc == DefaultHeapReportFunc)
  1779. {
  1780. char *pPath = "";
  1781. #ifdef _X360
  1782. pPath = "D:\\";
  1783. #elif defined( _PS3 )
  1784. pPath = "/app_home/";
  1785. #endif
  1786. #if defined( _MEMTEST ) && defined( _WIN32 )
  1787. char szXboxName[32];
  1788. strcpy( szXboxName, "xbox" );
  1789. DWORD numChars = sizeof( szXboxName );
  1790. DmGetXboxName( szXboxName, &numChars );
  1791. char *pXboxName = strstr( szXboxName, "_360" );
  1792. if ( pXboxName )
  1793. {
  1794. *pXboxName = '\0';
  1795. }
  1796. SYSTEMTIME systemTime;
  1797. GetLocalTime( &systemTime );
  1798. //_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 );
  1799. _snprintf( szFileName, sizeof( szFileName ), "%s%s_%d.txt", pPath, s_szStatsMapName, s_FileCount );
  1800. #else
  1801. _snprintf( szFileName, sizeof( szFileName ), "%s%s_%d.txt", pPath, pchFileBase, s_FileCount );
  1802. #endif
  1803. ++s_FileCount;
  1804. s_DbgFile = fopen(szFileName, "wt");
  1805. if (!s_DbgFile)
  1806. return;
  1807. }
  1808. {
  1809. HEAP_LOCK();
  1810. 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");
  1811. for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
  1812. {
  1813. m_OutputFunc( "\t%s", s_pCountHeader[i] );
  1814. }
  1815. m_OutputFunc("\n");
  1816. MemInfo_t totals = m_GlobalInfo;
  1817. #ifdef _PS3
  1818. {
  1819. // Add a line for system heap stats
  1820. static malloc_managed_size mms;
  1821. (g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
  1822. MemInfo_t info;
  1823. info.m_nCurrentSize = mms.current_inuse_size;
  1824. info.m_nPeakSize = mms.max_system_size;
  1825. info.m_nOverheadSize = mms.current_system_size - mms.current_inuse_size;
  1826. DumpMemInfo( "||PS3 malloc_stats||", 0, info );
  1827. // Add a line for PRXs
  1828. char prxFilename[256];
  1829. sys_prx_id_t prxIDs[256];
  1830. sys_prx_segment_info_t prxSegments[32];
  1831. sys_prx_get_module_list_t prxList = { sizeof( sys_prx_get_module_list_t ), ARRAYSIZE( prxIDs ), 0, prxIDs, NULL };
  1832. sys_prx_get_module_list( 0, &prxList );
  1833. Assert( prxList.count < ARRAYSIZE( prxIDs ) );
  1834. memset( &info, 0, sizeof( info ) );
  1835. for ( int i = 0; i < prxList.count; i++ )
  1836. {
  1837. sys_prx_module_info_t prxInfo;
  1838. prxInfo.size = sizeof( sys_prx_module_info_t );
  1839. prxInfo.filename = prxFilename;
  1840. prxInfo.filename_size = sizeof( prxFilename );
  1841. prxInfo.segments = prxSegments;
  1842. prxInfo.segments_num = ARRAYSIZE( prxSegments );
  1843. sys_prx_get_module_info( prxList.idlist[i], 0, &prxInfo );
  1844. Assert( prxInfo.segments_num < ARRAYSIZE( prxSegments ) );
  1845. for ( int j = 0; j < prxInfo.segments_num; j++ )
  1846. {
  1847. info.m_nCurrentSize += prxInfo.segments[j].memsz;
  1848. }
  1849. }
  1850. DumpMemInfo( "PS3 PRXs", 0, info );
  1851. // Add PRX sizes to our global tracked total:
  1852. totals.m_nCurrentSize += info.m_nCurrentSize;
  1853. }
  1854. #endif // _PS3
  1855. // The total of all memory usage we know about:
  1856. DumpMemInfo( "||Totals||", 0, totals );
  1857. if ( IsGameConsole() )
  1858. {
  1859. // Add a line showing total system memory usage from the OS (if this is more than
  1860. // "||Totals||", then there is unknown memory usage that we need to track down):
  1861. size_t usedMemory, freeMemory;
  1862. GlobalMemoryStatus( &usedMemory, &freeMemory );
  1863. MemInfo_t info;
  1864. info.m_nCurrentSize = usedMemory;
  1865. DumpMemInfo( "||Used Memory||", 0, info );
  1866. }
  1867. #ifdef _MEMTEST
  1868. {
  1869. // Add lines for GPU allocations
  1870. int nGPUMemSize, nGPUMemFree, nTextureSize, nRTSize, nVBSize, nIBSize, nUnknown;
  1871. if ( 7 == sscanf( s_szStatsComment, "%d %d %d %d %d %d %d", &nGPUMemSize, &nGPUMemFree, &nTextureSize, &nRTSize, &nVBSize, &nIBSize, &nUnknown ) )
  1872. {
  1873. int nTotalUsed = nTextureSize + nRTSize + nVBSize + nIBSize + nUnknown;
  1874. int nOverhead = ( nGPUMemSize - nTotalUsed ) - nGPUMemFree;
  1875. m_OutputFunc( "||PS3 RSX: total used||, line 0\t%.1f\n", nTotalUsed / 1024.0f );
  1876. m_OutputFunc( "PS3 RSX: textures, line 0\t%.1f\n", nTextureSize / 1024.0f );
  1877. m_OutputFunc( "PS3 RSX: render targets, line 0\t%.1f\n", nRTSize / 1024.0f );
  1878. m_OutputFunc( "PS3 RSX: vertex buffers, line 0\t%.1f\n", nVBSize / 1024.0f );
  1879. m_OutputFunc( "PS3 RSX: index buffers, line 0\t%.1f\n", nIBSize / 1024.0f );
  1880. m_OutputFunc( "PS3 RSX: unknown, line 0\t%.1f\n", nUnknown / 1024.0f );
  1881. m_OutputFunc( "PS3 RSX: overhead, line 0\t%.1f\n", nOverhead / 1024.0f );
  1882. }
  1883. }
  1884. #endif
  1885. //m_OutputFunc("File/Line Based\n");
  1886. DumpFileStats();
  1887. }
  1888. if (m_OutputFunc == DefaultHeapReportFunc)
  1889. {
  1890. fclose(s_DbgFile);
  1891. #if defined( _X360 )
  1892. XBX_rMemDump( szFileName );
  1893. #endif
  1894. }
  1895. }
  1896. void CDbgMemAlloc::GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory )
  1897. {
  1898. if ( !pUsedMemory || !pFreeMemory )
  1899. return;
  1900. #if defined ( _X360 )
  1901. // GlobalMemoryStatus tells us how much physical memory is free
  1902. MEMORYSTATUS stat;
  1903. ::GlobalMemoryStatus( &stat );
  1904. *pFreeMemory = stat.dwAvailPhys;
  1905. // Used is total minus free (discount the 32MB system reservation)
  1906. *pUsedMemory = ( stat.dwTotalPhys - 32*1024*1024 ) - *pFreeMemory;
  1907. #elif defined( _PS3 )
  1908. // need to factor in how much empty space there is in the heap
  1909. // (since it NEVER returns pages back to the OS after hitting a high-watermark)
  1910. static malloc_managed_size mms;
  1911. (g_pMemOverrideRawCrtFns->pfn_malloc_stats)( &mms );
  1912. int heapFree = mms.current_system_size - mms.current_inuse_size;
  1913. Assert( heapFree >= 0 );
  1914. // sys_memory_get_user_memory_size tells us how much PPU memory is used/free
  1915. static sys_memory_info stat;
  1916. sys_memory_get_user_memory_size( &stat );
  1917. *pFreeMemory = stat.available_user_memory;
  1918. *pFreeMemory += heapFree;
  1919. *pUsedMemory = stat.total_user_memory - *pFreeMemory;
  1920. // 213MB are available in retail mode, so adjust free mem to reflect that even if we're in devkit mode
  1921. const size_t RETAIL_SIZE = 213*1024*1024;
  1922. if ( stat.total_user_memory > RETAIL_SIZE )
  1923. *pFreeMemory -= stat.total_user_memory - RETAIL_SIZE;
  1924. #else
  1925. // no data
  1926. *pFreeMemory = 0;
  1927. *pUsedMemory = 0;
  1928. #endif
  1929. }
  1930. #ifdef USE_STACK_TRACES
  1931. void CDbgMemAlloc::DumpCallStackFlow( char const *pchFileBase )
  1932. {
  1933. HEAP_LOCK();
  1934. char szFileName[MAX_PATH];
  1935. static int s_FileCount = 0;
  1936. char *pPath = "";
  1937. if ( IsX360() )
  1938. {
  1939. pPath = "D:\\";
  1940. }
  1941. #if defined( _MEMTEST ) && defined( _WIN32 )
  1942. char szXboxName[32];
  1943. strcpy( szXboxName, "xbox" );
  1944. DWORD numChars = sizeof( szXboxName );
  1945. DmGetXboxName( szXboxName, &numChars );
  1946. char *pXboxName = strstr( szXboxName, "_360" );
  1947. if ( pXboxName )
  1948. {
  1949. *pXboxName = '\0';
  1950. }
  1951. SYSTEMTIME systemTime;
  1952. GetLocalTime( &systemTime );
  1953. _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 );
  1954. #else
  1955. _snprintf( szFileName, sizeof( szFileName ), "%s%s%d.vcsf", pPath, pchFileBase, s_FileCount );
  1956. #endif
  1957. ++s_FileCount;
  1958. m_CallStackStats.DumpToFile( szFileName, false );
  1959. }
  1960. #endif
  1961. //-----------------------------------------------------------------------------
  1962. // Stat output
  1963. //-----------------------------------------------------------------------------
  1964. void CDbgMemAlloc::DumpStats()
  1965. {
  1966. DumpStatsFileBase( "memstats" );
  1967. #ifdef USE_STACK_TRACES
  1968. DumpCallStackFlow( "memflow" );
  1969. #endif
  1970. }
  1971. void CDbgMemAlloc::SetCRTAllocFailed( size_t nSize )
  1972. {
  1973. m_sMemoryAllocFailed = nSize;
  1974. DebuggerBreakIfDebugging();
  1975. char buffer[256];
  1976. _snprintf( buffer, sizeof( buffer ), "***** OUT OF MEMORY! attempted allocation size: %u ****\n", nSize );
  1977. #if defined( _PS3 ) && defined( _DEBUG )
  1978. DebuggerBreak();
  1979. #endif // _PS3
  1980. #ifdef _X360
  1981. XBX_OutputDebugString( buffer );
  1982. if ( !Plat_IsInDebugSession() )
  1983. {
  1984. XBX_CrashDump( true );
  1985. #if defined( _DEMO )
  1986. XLaunchNewImage( XLAUNCH_KEYWORD_DEFAULT_APP, 0 );
  1987. #else
  1988. XLaunchNewImage( "default.xex", 0 );
  1989. #endif
  1990. }
  1991. #elif defined(_WIN32 )
  1992. OutputDebugString( buffer );
  1993. if ( !Plat_IsInDebugSession() )
  1994. {
  1995. AssertFatalMsg( false, buffer );
  1996. abort();
  1997. }
  1998. #else
  1999. printf( "%s\n", buffer );
  2000. if ( !Plat_IsInDebugSession() )
  2001. {
  2002. AssertFatalMsg( false, buffer );
  2003. exit( 0 );
  2004. }
  2005. #endif
  2006. }
  2007. size_t CDbgMemAlloc::MemoryAllocFailed()
  2008. {
  2009. return m_sMemoryAllocFailed;
  2010. }
  2011. #ifdef LINUX
  2012. //
  2013. // Under linux we can ask GLIBC to override malloc for us
  2014. // Base on code from Ryan, http://hg.icculus.org/icculus/mallocmonitor/file/29c4b0d049f7/monitor_client/malloc_hook_glibc.c
  2015. //
  2016. //
  2017. static void *glibc_malloc_hook = NULL;
  2018. static void *glibc_realloc_hook = NULL;
  2019. static void *glibc_memalign_hook = NULL;
  2020. static void *glibc_free_hook = NULL;
  2021. /* convenience functions for setting the hooks... */
  2022. static inline void save_glibc_hooks(void);
  2023. static inline void set_glibc_hooks(void);
  2024. static inline void set_override_hooks(void);
  2025. CThreadMutex g_HookMutex;
  2026. /*
  2027. * Our overriding hooks...they call through to the original C runtime
  2028. * implementations and report to the monitoring daemon.
  2029. */
  2030. static void *override_malloc_hook(size_t s, const void *caller)
  2031. {
  2032. void *retval;
  2033. AUTO_LOCK( g_HookMutex );
  2034. set_glibc_hooks(); /* put glibc back in control. */
  2035. retval = InternalMalloc( s, NULL, 0 );
  2036. save_glibc_hooks(); /* update in case glibc changed them. */
  2037. set_override_hooks(); /* only restore hooks if daemon is listening */
  2038. return(retval);
  2039. } /* override_malloc_hook */
  2040. static void *override_realloc_hook(void *ptr, size_t s, const void *caller)
  2041. {
  2042. void *retval;
  2043. AUTO_LOCK( g_HookMutex );
  2044. set_glibc_hooks(); /* put glibc back in control. */
  2045. retval = InternalRealloc(ptr, s, NULL, 0); /* call glibc version. */
  2046. save_glibc_hooks(); /* update in case glibc changed them. */
  2047. set_override_hooks(); /* only restore hooks if daemon is listening */
  2048. return(retval);
  2049. } /* override_realloc_hook */
  2050. static void *override_memalign_hook(size_t a, size_t s, const void *caller)
  2051. {
  2052. void *retval;
  2053. AUTO_LOCK( g_HookMutex );
  2054. set_glibc_hooks(); /* put glibc back in control. */
  2055. retval = memalign(a, s); /* call glibc version. */
  2056. save_glibc_hooks(); /* update in case glibc changed them. */
  2057. set_override_hooks(); /* only restore hooks if daemon is listening */
  2058. return(retval);
  2059. } /* override_memalign_hook */
  2060. static void override_free_hook(void *ptr, const void *caller)
  2061. {
  2062. AUTO_LOCK( g_HookMutex );
  2063. set_glibc_hooks(); /* put glibc back in control. */
  2064. InternalFree(ptr); /* call glibc version. */
  2065. save_glibc_hooks(); /* update in case glibc changed them. */
  2066. set_override_hooks(); /* only restore hooks if daemon is listening */
  2067. } /* override_free_hook */
  2068. /*
  2069. * Convenience functions for swapping the hooks around...
  2070. */
  2071. /*
  2072. * Save a copy of the original allocation hooks, so we can call into them
  2073. * from our overriding functions. It's possible that glibc might change
  2074. * these hooks under various conditions (so the manual's examples seem
  2075. * to suggest), so we update them whenever we finish calling into the
  2076. * the originals.
  2077. */
  2078. static inline void save_glibc_hooks(void)
  2079. {
  2080. glibc_malloc_hook = (void *)__malloc_hook;
  2081. glibc_realloc_hook = (void *)__realloc_hook;
  2082. glibc_memalign_hook = (void *)__memalign_hook;
  2083. glibc_free_hook = (void *)__free_hook;
  2084. } /* save_glibc_hooks */
  2085. /*
  2086. * Restore the hooks to the glibc versions. This is needed since, say,
  2087. * their realloc() might call malloc() or free() under the hood, etc, so
  2088. * it's safer to let them have complete control over the subsystem, which
  2089. * also makes our logging saner, too.
  2090. */
  2091. static inline void set_glibc_hooks(void)
  2092. {
  2093. __malloc_hook = (void* (*)(size_t, const void*))glibc_malloc_hook;
  2094. __realloc_hook = (void* (*)(void*, size_t, const void*))glibc_realloc_hook;
  2095. __memalign_hook = (void* (*)(size_t, size_t, const void*))glibc_memalign_hook;
  2096. __free_hook = (void (*)(void*, const void*))glibc_free_hook;
  2097. } /* set_glibc_hooks */
  2098. /*
  2099. * Put our hooks back in place. This should be done after the original
  2100. * glibc version has been called and we've finished any logging (which
  2101. * may call glibc functions, too). This sets us up for the next calls from
  2102. * the application.
  2103. */
  2104. static inline void set_override_hooks(void)
  2105. {
  2106. __malloc_hook = override_malloc_hook;
  2107. __realloc_hook = override_realloc_hook;
  2108. __memalign_hook = override_memalign_hook;
  2109. __free_hook = override_free_hook;
  2110. } /* set_override_hooks */
  2111. /*
  2112. * The Hook Of All Hooks...how we get in there in the first place.
  2113. */
  2114. /*
  2115. * glibc will call this when the malloc subsystem is initializing, giving
  2116. * us a chance to install hooks that override the functions.
  2117. */
  2118. static void override_init_hook(void)
  2119. {
  2120. AUTO_LOCK( g_HookMutex );
  2121. /* install our hooks. Will connect to daemon on first malloc, etc. */
  2122. save_glibc_hooks();
  2123. set_override_hooks();
  2124. } /* override_init_hook */
  2125. /*
  2126. * __malloc_initialize_hook is apparently a "weak variable", so you can
  2127. * define and assign it here even though it's in glibc, too. This lets
  2128. * us hook into malloc as soon as the runtime initializes, and before
  2129. * main() is called. Basically, this whole trick depends on this.
  2130. */
  2131. void (*__malloc_initialize_hook)(void) __attribute__((visibility("default")))= override_init_hook;
  2132. #elif defined( OSX )
  2133. //
  2134. // pointers to the osx versions of these functions
  2135. static void *osx_malloc_hook = NULL;
  2136. static void *osx_realloc_hook = NULL;
  2137. static void *osx_free_hook = NULL;
  2138. // convenience functions for setting the hooks...
  2139. static inline void save_osx_hooks(void);
  2140. static inline void set_osx_hooks(void);
  2141. static inline void set_override_hooks(void);
  2142. CThreadMutex g_HookMutex;
  2143. //
  2144. // Our overriding hooks...they call through to the original C runtime
  2145. // implementations and report to the monitoring daemon.
  2146. //
  2147. static void *override_malloc_hook(struct _malloc_zone_t *zone, size_t s)
  2148. {
  2149. void *retval;
  2150. set_osx_hooks();
  2151. retval = InternalMalloc( s, NULL, 0 );
  2152. set_override_hooks();
  2153. return(retval);
  2154. }
  2155. static void *override_realloc_hook(struct _malloc_zone_t *zone, void *ptr, size_t s)
  2156. {
  2157. void *retval;
  2158. set_osx_hooks();
  2159. retval = InternalRealloc(ptr, s, NULL, 0);
  2160. set_override_hooks();
  2161. return(retval);
  2162. }
  2163. static void override_free_hook(struct _malloc_zone_t *zone, void *ptr)
  2164. {
  2165. // sometime they pass in a null pointer from higher level calls, just ignore it
  2166. if ( !ptr )
  2167. return;
  2168. set_osx_hooks();
  2169. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( ptr );
  2170. if ( *((int*)pInternalMem->m_Reserved) == 0xf00df00d )
  2171. {
  2172. InternalFree( ptr );
  2173. }
  2174. set_override_hooks();
  2175. }
  2176. /*
  2177. These are func's we could optionally override right now on OSX but don't need to
  2178. static size_t override_size_hook(struct _malloc_zone_t *zone, const void *ptr)
  2179. {
  2180. set_osx_hooks();
  2181. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( (void *)ptr );
  2182. set_override_hooks();
  2183. if ( *((int*)pInternalMem->m_Reserved) == 0xf00df00d )
  2184. {
  2185. return pInternalMem->nLogicalSize;
  2186. }
  2187. return 0;
  2188. }
  2189. static void *override_calloc_hook(struct _malloc_zone_t *zone, size_t num_items, size_t size )
  2190. {
  2191. void *ans = override_malloc_hook( zone, num_items*size );
  2192. if ( !ans )
  2193. return 0;
  2194. memset( ans, 0x0, num_items*size );
  2195. return ans;
  2196. }
  2197. static void *override_valloc_hook(struct _malloc_zone_t *zone, size_t size )
  2198. {
  2199. return override_calloc_hook( zone, 1, size );
  2200. }
  2201. static void override_destroy_hook(struct _malloc_zone_t *zone)
  2202. {
  2203. }
  2204. */
  2205. //
  2206. // Save a copy of the original allocation hooks, so we can call into them
  2207. // from our overriding functions. It's possible that osx might change
  2208. // these hooks under various conditions (so the manual's examples seem
  2209. // to suggest), so we update them whenever we finish calling into the
  2210. // the originals.
  2211. //
  2212. static inline void save_osx_hooks(void)
  2213. {
  2214. malloc_zone_t *malloc_zone = malloc_default_zone();
  2215. osx_malloc_hook = (void *)malloc_zone->malloc;
  2216. osx_realloc_hook = (void *)malloc_zone->realloc;
  2217. osx_free_hook = (void *)malloc_zone->free;
  2218. // These are func's we could optionally override right now on OSX but don't need to
  2219. // osx_size_hook = (void *)malloc_zone->size;
  2220. // osx_calloc_hook = (void *)malloc_zone->calloc;
  2221. // osx_valloc_hook = (void *)malloc_zone->valloc;
  2222. // osx_destroy_hook = (void *)malloc_zone->destroy;
  2223. }
  2224. //
  2225. // Restore the hooks to the osx versions. This is needed since, say,
  2226. // their realloc() might call malloc() or free() under the hood, etc, so
  2227. // it's safer to let them have complete control over the subsystem, which
  2228. // also makes our logging saner, too.
  2229. //
  2230. static inline void set_osx_hooks(void)
  2231. {
  2232. malloc_zone_t *malloc_zone = malloc_default_zone();
  2233. malloc_zone->malloc = (void* (*)(_malloc_zone_t*, size_t))osx_malloc_hook;
  2234. malloc_zone->realloc = (void* (*)(_malloc_zone_t*, void*, size_t))osx_realloc_hook;
  2235. malloc_zone->free = (void (*)(_malloc_zone_t*, void*))osx_free_hook;
  2236. // These are func's we could optionally override right now on OSX but don't need to
  2237. //malloc_zone->size = (size_t (*)(_malloc_zone_t*, const void *))osx_size_hook;
  2238. //malloc_zone->calloc = (void* (*)(_malloc_zone_t*, size_t, size_t))osx_calloc_hook;
  2239. //malloc_zone->valloc = (void* (*)(_malloc_zone_t*, size_t))osx_valloc_hook;
  2240. //malloc_zone->destroy = (void (*)(_malloc_zone_t*))osx_destroy_hook;
  2241. }
  2242. /*
  2243. * Put our hooks back in place. This should be done after the original
  2244. * osx version has been called and we've finished any logging (which
  2245. * may call osx functions, too). This sets us up for the next calls from
  2246. * the application.
  2247. */
  2248. static inline void set_override_hooks(void)
  2249. {
  2250. malloc_zone_t *malloc_zone = malloc_default_zone();
  2251. malloc_zone->malloc = override_malloc_hook;
  2252. malloc_zone->realloc = override_realloc_hook;
  2253. malloc_zone->free = override_free_hook;
  2254. // These are func's we could optionally override right now on OSX but don't need to
  2255. //malloc_zone->size = override_size_hook;
  2256. //malloc_zone->calloc = override_calloc_hook;
  2257. // malloc_zone->valloc = override_valloc_hook;
  2258. //malloc_zone->destroy = override_destroy_hook;
  2259. }
  2260. //
  2261. // The Hook Of All Hooks...how we get in there in the first place.
  2262. //
  2263. // osx will call this when the malloc subsystem is initializing, giving
  2264. // us a chance to install hooks that override the functions.
  2265. //
  2266. void __attribute__ ((constructor)) mem_init(void)
  2267. {
  2268. AUTO_LOCK( g_HookMutex );
  2269. save_osx_hooks();
  2270. set_override_hooks();
  2271. }
  2272. void *operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  2273. {
  2274. set_osx_hooks();
  2275. void *pMem = g_pMemAlloc->Alloc(nSize, pFileName, nLine);
  2276. set_override_hooks();
  2277. return pMem;
  2278. }
  2279. void *operator new[] ( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  2280. {
  2281. set_osx_hooks();
  2282. void *pMem = g_pMemAlloc->Alloc(nSize, pFileName, nLine);
  2283. set_override_hooks();
  2284. return pMem;
  2285. }
  2286. #endif // OSX
  2287. int GetAllocationCallStack( void *mem, void **pCallStackOut, int iMaxEntriesOut )
  2288. {
  2289. #if defined( USE_MEM_DEBUG ) && (defined( USE_STACK_TRACES ))
  2290. return s_DbgMemAlloc.GetCallStackForIndex( GetAllocationStatIndex_Internal( mem ), pCallStackOut, iMaxEntriesOut );
  2291. #else
  2292. return 0;
  2293. #endif
  2294. }
  2295. #endif // MEM_IMPL_TYPE_DBG
  2296. #endif // !defined(STEAM) && !defined(NO_MALLOC_OVERRIDE)