Counter Strike : Global Offensive Source Code
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2812 lines
80 KiB

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