Team Fortress 2 Source Code as on 22/4/2020
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1955 lines
52 KiB

  1. //========= Copyright 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/memalloc.h"
  13. #include "mem_helpers.h"
  14. #ifdef _WIN32
  15. #include <crtdbg.h>
  16. #endif
  17. #ifdef OSX
  18. #include <malloc/malloc.h>
  19. #include <mach/mach.h>
  20. #include <stdlib.h>
  21. #endif
  22. #include <map>
  23. #include <set>
  24. #include <limits.h>
  25. #include "tier0/threadtools.h"
  26. #ifdef _X360
  27. #include "xbox/xbox_console.h"
  28. #endif
  29. #if ( !defined(_DEBUG) && defined(USE_MEM_DEBUG) )
  30. #pragma message ("USE_MEM_DEBUG is enabled in a release build. Don't check this in!")
  31. #endif
  32. #if (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  33. #if defined(_WIN32) && ( !defined(_X360) && !defined(_WIN64) )
  34. // #define USE_STACK_WALK
  35. // or:
  36. // #define USE_STACK_WALK_DETAILED
  37. #endif
  38. //-----------------------------------------------------------------------------
  39. #ifndef _X360
  40. #define DebugAlloc malloc
  41. #define DebugFree free
  42. #else
  43. #define DebugAlloc DmAllocatePool
  44. #define DebugFree DmFreePool
  45. #endif
  46. #ifdef WIN32
  47. int g_DefaultHeapFlags = _CrtSetDbgFlag( _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG) | _CRTDBG_ALLOC_MEM_DF );
  48. #endif
  49. #if defined( _MEMTEST )
  50. static char s_szStatsMapName[32];
  51. static char s_szStatsComment[256];
  52. #endif
  53. //-----------------------------------------------------------------------------
  54. #if defined( USE_STACK_WALK ) || defined( USE_STACK_WALK_DETAILED )
  55. #include <dbghelp.h>
  56. #pragma comment(lib, "Dbghelp.lib" )
  57. #pragma auto_inline(off)
  58. __declspec(naked) DWORD GetEIP()
  59. {
  60. __asm
  61. {
  62. mov eax, [ebp + 4]
  63. ret
  64. }
  65. }
  66. int WalkStack( void **ppAddresses, int nMaxAddresses, int nSkip = 0 )
  67. {
  68. HANDLE hProcess = GetCurrentProcess();
  69. HANDLE hThread = GetCurrentThread();
  70. STACKFRAME64 frame;
  71. memset(&frame, 0, sizeof(frame));
  72. DWORD valEsp, valEbp;
  73. __asm
  74. {
  75. mov [valEsp], esp;
  76. mov [valEbp], ebp
  77. }
  78. frame.AddrPC.Offset = GetEIP();
  79. frame.AddrStack.Offset = valEsp;
  80. frame.AddrFrame.Offset = valEbp;
  81. frame.AddrPC.Mode = AddrModeFlat;
  82. frame.AddrStack.Mode = AddrModeFlat;
  83. frame.AddrFrame.Mode = AddrModeFlat;
  84. // Walk the stack.
  85. int nWalked = 0;
  86. nSkip++;
  87. while ( nMaxAddresses - nWalked > 0 )
  88. {
  89. if ( !StackWalk64(IMAGE_FILE_MACHINE_I386, hProcess, hThread, &frame, NULL, NULL, SymFunctionTableAccess64, SymGetModuleBase64, NULL ) )
  90. {
  91. break;
  92. }
  93. if ( nSkip == 0 )
  94. {
  95. if (frame.AddrFrame.Offset == 0)
  96. {
  97. // End of stack.
  98. break;
  99. }
  100. *ppAddresses++ = (void *)frame.AddrPC.Offset;
  101. nWalked++;
  102. if (frame.AddrPC.Offset == frame.AddrReturn.Offset)
  103. {
  104. // Catching a stack loop
  105. break;
  106. }
  107. }
  108. else
  109. {
  110. nSkip--;
  111. }
  112. }
  113. if ( nMaxAddresses )
  114. {
  115. memset( ppAddresses, 0, ( nMaxAddresses - nWalked ) * sizeof(*ppAddresses) );
  116. }
  117. return nWalked;
  118. }
  119. bool GetModuleFromAddress( void *address, char *pResult )
  120. {
  121. IMAGEHLP_MODULE moduleInfo;
  122. moduleInfo.SizeOfStruct = sizeof(moduleInfo);
  123. if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) )
  124. {
  125. strcpy( pResult, moduleInfo.ModuleName );
  126. return true;
  127. }
  128. return false;
  129. }
  130. bool GetCallerModule( char *pDest )
  131. {
  132. static bool bInit;
  133. if ( !bInit )
  134. {
  135. PSTR psUserSearchPath = NULL;
  136. psUserSearchPath = "u:\\data\\game\\bin\\;u:\\data\\game\\episodic\\bin\\;u:\\data\\game\\hl2\\bin\\;\\\\perforce\\symbols";
  137. SymInitialize( GetCurrentProcess(), psUserSearchPath, true );
  138. bInit = true;
  139. }
  140. void *pCaller;
  141. WalkStack( &pCaller, 1, 2 );
  142. return ( pCaller != 0 && GetModuleFromAddress( pCaller, pDest ) );
  143. }
  144. #if defined( USE_STACK_WALK_DETAILED )
  145. //
  146. // Note: StackDescribe function is non-reentrant:
  147. // Reason: Stack description is stored in a static buffer.
  148. // Solution: Passing caller-allocated buffers would allow the
  149. // function to become reentrant, however the current only client (FindOrCreateFilename)
  150. // is synchronized with a heap mutex, after retrieving stack description the
  151. // heap memory will be allocated to copy the text.
  152. //
  153. char * StackDescribe( void **ppAddresses, int nMaxAddresses )
  154. {
  155. static char s_chStackDescription[ 32 * 1024 ];
  156. static char s_chSymbolBuffer[ sizeof( IMAGEHLP_SYMBOL64 ) + 1024 ];
  157. IMAGEHLP_SYMBOL64 &hlpSymbol = * ( IMAGEHLP_SYMBOL64 * ) s_chSymbolBuffer;
  158. hlpSymbol.SizeOfStruct = sizeof( IMAGEHLP_SYMBOL64 );
  159. hlpSymbol.MaxNameLength = 1024;
  160. DWORD64 hlpSymbolOffset = 0;
  161. IMAGEHLP_LINE64 hlpLine;
  162. hlpLine.SizeOfStruct = sizeof( IMAGEHLP_LINE64 );
  163. DWORD hlpLineOffset = 0;
  164. s_chStackDescription[ 0 ] = 0;
  165. char *pchBuffer = s_chStackDescription;
  166. for ( int k = 0; k < nMaxAddresses; ++ k )
  167. {
  168. if ( !ppAddresses[k] )
  169. break;
  170. pchBuffer += strlen( pchBuffer );
  171. if ( SymGetLineFromAddr64( GetCurrentProcess(), ( DWORD64 ) ppAddresses[k], &hlpLineOffset, &hlpLine ) )
  172. {
  173. char const *pchFileName = hlpLine.FileName ? hlpLine.FileName + strlen( hlpLine.FileName ) : NULL;
  174. for ( size_t numSlashesAllowed = 2; pchFileName > hlpLine.FileName; -- pchFileName )
  175. {
  176. if ( *pchFileName == '\\' )
  177. {
  178. if ( numSlashesAllowed -- )
  179. continue;
  180. else
  181. break;
  182. }
  183. }
  184. sprintf( pchBuffer, hlpLineOffset ? "%s:%d+0x%I32X" : "%s:%d", pchFileName, hlpLine.LineNumber, hlpLineOffset );
  185. }
  186. else if ( SymGetSymFromAddr64( GetCurrentProcess(), ( DWORD64 ) ppAddresses[k], &hlpSymbolOffset, &hlpSymbol ) )
  187. {
  188. sprintf( pchBuffer, ( hlpSymbolOffset > 0 && !( hlpSymbolOffset >> 63 ) ) ? "%s+0x%I64X" : "%s", hlpSymbol.Name, hlpSymbolOffset );
  189. }
  190. else
  191. {
  192. sprintf( pchBuffer, "#0x%08p", ppAddresses[k] );
  193. }
  194. pchBuffer += strlen( pchBuffer );
  195. sprintf( pchBuffer, "<--" );
  196. }
  197. *pchBuffer = 0;
  198. return s_chStackDescription;
  199. }
  200. #endif // #if defined( USE_STACK_WALK_DETAILED )
  201. #else
  202. inline int WalkStack( void **ppAddresses, int nMaxAddresses, int nSkip = 0 )
  203. {
  204. memset( ppAddresses, 0, nMaxAddresses * sizeof(*ppAddresses) );
  205. return 0;
  206. }
  207. #define GetModuleFromAddress( address, pResult ) ( ( *pResult = 0 ), 0)
  208. #define GetCallerModule( pDest ) false
  209. #endif
  210. //-----------------------------------------------------------------------------
  211. // NOTE: This exactly mirrors the dbg header in the MSDEV crt
  212. // eventually when we write our own allocator, we can kill this
  213. struct CrtDbgMemHeader_t
  214. {
  215. unsigned char m_Reserved[8];
  216. const char *m_pFileName;
  217. int m_nLineNumber;
  218. unsigned char m_Reserved2[16];
  219. };
  220. struct DbgMemHeader_t
  221. #if !defined( _DEBUG ) || defined( POSIX )
  222. : CrtDbgMemHeader_t
  223. #endif
  224. {
  225. unsigned nLogicalSize;
  226. byte reserved[12]; // MS allocator always returns mem aligned on 16 bytes, which some of our code depends on
  227. };
  228. //-----------------------------------------------------------------------------
  229. #if defined( _DEBUG ) && !defined( POSIX )
  230. #define GetCrtDbgMemHeader( pMem ) ((CrtDbgMemHeader_t*)((DbgMemHeader_t*)pMem - 1) - 1)
  231. #elif defined( OSX )
  232. DbgMemHeader_t *GetCrtDbgMemHeader( void *pMem );
  233. #else
  234. #define GetCrtDbgMemHeader( pMem ) ((DbgMemHeader_t*)pMem - 1)
  235. #endif
  236. #ifdef OSX
  237. DbgMemHeader_t *GetCrtDbgMemHeader( void *pMem )
  238. {
  239. size_t msize = malloc_size( pMem );
  240. return (DbgMemHeader_t *)( (char *)pMem + msize - sizeof(DbgMemHeader_t) );
  241. }
  242. #endif
  243. inline void *InternalMalloc( size_t nSize, const char *pFileName, int nLine )
  244. {
  245. #ifdef OSX
  246. void *pAllocedMem = malloc_zone_malloc( malloc_default_zone(), nSize + sizeof(DbgMemHeader_t) );
  247. if (!pAllocedMem)
  248. {
  249. return NULL;
  250. }
  251. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pAllocedMem );
  252. pInternalMem->m_pFileName = pFileName;
  253. pInternalMem->m_nLineNumber = nLine;
  254. pInternalMem->nLogicalSize = nSize;
  255. *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
  256. return pAllocedMem;
  257. #else // LINUX || WIN32
  258. DbgMemHeader_t *pInternalMem;
  259. #if defined( POSIX ) || !defined( _DEBUG )
  260. pInternalMem = (DbgMemHeader_t *)malloc( nSize + sizeof(DbgMemHeader_t) );
  261. if (!pInternalMem)
  262. {
  263. return NULL;
  264. }
  265. pInternalMem->m_pFileName = pFileName;
  266. pInternalMem->m_nLineNumber = nLine;
  267. *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
  268. #else
  269. pInternalMem = (DbgMemHeader_t *)_malloc_dbg( nSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
  270. #endif // defined( POSIX ) || !defined( _DEBUG )
  271. pInternalMem->nLogicalSize = nSize;
  272. return pInternalMem + 1;
  273. #endif // LINUX || WIN32
  274. }
  275. inline void *InternalRealloc( void *pMem, size_t nNewSize, const char *pFileName, int nLine )
  276. {
  277. if ( !pMem )
  278. return InternalMalloc( nNewSize, pFileName, nLine );
  279. #ifdef OSX
  280. void *pNewAllocedMem = NULL;
  281. pNewAllocedMem = (void *)malloc_zone_realloc( malloc_default_zone(), pMem, nNewSize + sizeof(DbgMemHeader_t) );
  282. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pNewAllocedMem );
  283. pInternalMem->m_pFileName = pFileName;
  284. pInternalMem->m_nLineNumber = nLine;
  285. pInternalMem->nLogicalSize = static_cast<unsigned int>( nNewSize );
  286. *((int*)pInternalMem->m_Reserved) = 0xf00df00d;
  287. return pNewAllocedMem;
  288. #else // LINUX || WIN32
  289. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  290. #if defined( POSIX ) || !defined( _DEBUG )
  291. pInternalMem = (DbgMemHeader_t *)realloc( pInternalMem, nNewSize + sizeof(DbgMemHeader_t) );
  292. pInternalMem->m_pFileName = pFileName;
  293. pInternalMem->m_nLineNumber = nLine;
  294. #else
  295. pInternalMem = (DbgMemHeader_t *)_realloc_dbg( pInternalMem, nNewSize + sizeof(DbgMemHeader_t), _NORMAL_BLOCK, pFileName, nLine );
  296. #endif
  297. pInternalMem->nLogicalSize = nNewSize;
  298. return pInternalMem + 1;
  299. #endif // LINUX || WIN32
  300. }
  301. inline void InternalFree( void *pMem )
  302. {
  303. if ( !pMem )
  304. return;
  305. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  306. #if !defined( _DEBUG ) || defined( POSIX )
  307. #ifdef OSX
  308. malloc_zone_free( malloc_default_zone(), pMem );
  309. #elif LINUX
  310. free( pInternalMem );
  311. #else
  312. free( pInternalMem );
  313. #endif
  314. #else
  315. _free_dbg( pInternalMem, _NORMAL_BLOCK );
  316. #endif
  317. }
  318. inline size_t InternalMSize( void *pMem )
  319. {
  320. //$ TODO. For Linux, we could use 'int size = malloc_usable_size( pMem )'...
  321. #if defined(POSIX)
  322. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  323. return pInternalMem->nLogicalSize;
  324. #elif !defined(_DEBUG)
  325. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  326. return _msize( pInternalMem ) - sizeof(DbgMemHeader_t);
  327. #else
  328. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  329. return _msize_dbg( pInternalMem, _NORMAL_BLOCK ) - sizeof(DbgMemHeader_t);
  330. #endif
  331. }
  332. inline size_t InternalLogicalSize( void *pMem )
  333. {
  334. #if defined(POSIX)
  335. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( pMem );
  336. #elif !defined(_DEBUG)
  337. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  338. #else
  339. DbgMemHeader_t *pInternalMem = (DbgMemHeader_t *)pMem - 1;
  340. #endif
  341. return pInternalMem->nLogicalSize;
  342. }
  343. #ifndef _DEBUG
  344. #define _CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg ) 0
  345. #endif
  346. //-----------------------------------------------------------------------------
  347. // Custom allocator protects this module from recursing on operator new
  348. template <class T>
  349. class CNoRecurseAllocator
  350. {
  351. public:
  352. // type definitions
  353. typedef T value_type;
  354. typedef T* pointer;
  355. typedef const T* const_pointer;
  356. typedef T& reference;
  357. typedef const T& const_reference;
  358. typedef std::size_t size_type;
  359. typedef std::ptrdiff_t difference_type;
  360. CNoRecurseAllocator() {}
  361. CNoRecurseAllocator(const CNoRecurseAllocator&) {}
  362. template <class U> CNoRecurseAllocator(const CNoRecurseAllocator<U>&) {}
  363. ~CNoRecurseAllocator(){}
  364. // rebind allocator to type U
  365. template <class U > struct rebind { typedef CNoRecurseAllocator<U> other; };
  366. // return address of values
  367. pointer address (reference value) const { return &value; }
  368. const_pointer address (const_reference value) const { return &value;}
  369. size_type max_size() const { return INT_MAX; }
  370. pointer allocate(size_type num, const void* = 0) { return (pointer)DebugAlloc(num * sizeof(T)); }
  371. void deallocate (pointer p, size_type num) { DebugFree(p); }
  372. void construct(pointer p, const T& value) { new((void*)p)T(value); }
  373. void destroy (pointer p) { p->~T(); }
  374. };
  375. template <class T1, class T2>
  376. bool operator==(const CNoRecurseAllocator<T1>&, const CNoRecurseAllocator<T2>&)
  377. {
  378. return true;
  379. }
  380. template <class T1, class T2>
  381. bool operator!=(const CNoRecurseAllocator<T1>&, const CNoRecurseAllocator<T2>&)
  382. {
  383. return false;
  384. }
  385. class CStringLess
  386. {
  387. public:
  388. bool operator()(const char *pszLeft, const char *pszRight ) const
  389. {
  390. return ( stricmp( pszLeft, pszRight ) < 0 );
  391. }
  392. };
  393. //-----------------------------------------------------------------------------
  394. #pragma warning( disable:4074 ) // warning C4074: initializers put in compiler reserved initialization area
  395. #pragma init_seg( compiler )
  396. //-----------------------------------------------------------------------------
  397. // NOTE! This should never be called directly from leaf code
  398. // Just use new,delete,malloc,free etc. They will call into this eventually
  399. //-----------------------------------------------------------------------------
  400. class CDbgMemAlloc : public IMemAlloc
  401. {
  402. public:
  403. CDbgMemAlloc();
  404. virtual ~CDbgMemAlloc();
  405. // Release versions
  406. virtual void *Alloc( size_t nSize );
  407. virtual void *Realloc( void *pMem, size_t nSize );
  408. virtual void Free( void *pMem );
  409. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize );
  410. // Debug versions
  411. virtual void *Alloc( size_t nSize, const char *pFileName, int nLine );
  412. virtual void *Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine );
  413. virtual void Free( void *pMem, const char *pFileName, int nLine );
  414. virtual void *Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine );
  415. // Returns size of a particular allocation
  416. virtual size_t GetSize( void *pMem );
  417. // Force file + line information for an allocation
  418. virtual void PushAllocDbgInfo( const char *pFileName, int nLine );
  419. virtual void PopAllocDbgInfo();
  420. virtual long CrtSetBreakAlloc( long lNewBreakAlloc );
  421. virtual int CrtSetReportMode( int nReportType, int nReportMode );
  422. virtual int CrtIsValidHeapPointer( const void *pMem );
  423. virtual int CrtIsValidPointer( const void *pMem, unsigned int size, int access );
  424. virtual int CrtCheckMemory( void );
  425. virtual int CrtSetDbgFlag( int nNewFlag );
  426. virtual void CrtMemCheckpoint( _CrtMemState *pState );
  427. // handles storing allocation info for coroutines
  428. virtual uint32 GetDebugInfoSize();
  429. virtual void SaveDebugInfo( void *pvDebugInfo );
  430. virtual void RestoreDebugInfo( const void *pvDebugInfo );
  431. virtual void InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine );
  432. // FIXME: Remove when we have our own allocator
  433. virtual void* CrtSetReportFile( int nRptType, void* hFile );
  434. virtual void* CrtSetReportHook( void* pfnNewHook );
  435. virtual int CrtDbgReport( int nRptType, const char * szFile,
  436. int nLine, const char * szModule, const char * szFormat );
  437. virtual int heapchk();
  438. virtual bool IsDebugHeap() { return true; }
  439. virtual int GetVersion() { return MEMALLOC_VERSION; }
  440. virtual void CompactHeap()
  441. {
  442. #if defined( _X360 ) && defined( _DEBUG )
  443. HeapCompact( GetProcessHeap(), 0 );
  444. #endif
  445. }
  446. virtual MemAllocFailHandler_t SetAllocFailHandler( MemAllocFailHandler_t pfnMemAllocFailHandler ) { return NULL; } // debug heap doesn't attempt retries
  447. #if defined( _MEMTEST )
  448. void SetStatsExtraInfo( const char *pMapName, const char *pComment )
  449. {
  450. strncpy( s_szStatsMapName, pMapName, sizeof( s_szStatsMapName ) );
  451. s_szStatsMapName[sizeof( s_szStatsMapName ) - 1] = '\0';
  452. strncpy( s_szStatsComment, pComment, sizeof( s_szStatsComment ) );
  453. s_szStatsComment[sizeof( s_szStatsComment ) - 1] = '\0';
  454. }
  455. #endif
  456. virtual size_t MemoryAllocFailed();
  457. void SetCRTAllocFailed( size_t nMemSize );
  458. enum
  459. {
  460. BYTE_COUNT_16 = 0,
  461. BYTE_COUNT_32,
  462. BYTE_COUNT_128,
  463. BYTE_COUNT_1024,
  464. BYTE_COUNT_GREATER,
  465. NUM_BYTE_COUNT_BUCKETS
  466. };
  467. void Shutdown();
  468. private:
  469. struct MemInfo_t
  470. {
  471. MemInfo_t()
  472. {
  473. memset( this, 0, sizeof(*this) );
  474. }
  475. // Size in bytes
  476. size_t m_nCurrentSize;
  477. size_t m_nPeakSize;
  478. size_t m_nTotalSize;
  479. size_t m_nOverheadSize;
  480. size_t m_nPeakOverheadSize;
  481. // Count in terms of # of allocations
  482. size_t m_nCurrentCount;
  483. size_t m_nPeakCount;
  484. size_t m_nTotalCount;
  485. // Count in terms of # of allocations of a particular size
  486. size_t m_pCount[NUM_BYTE_COUNT_BUCKETS];
  487. // Time spent allocating + deallocating (microseconds)
  488. int64 m_nTime;
  489. };
  490. struct MemInfoKey_t
  491. {
  492. MemInfoKey_t( const char *pFileName, int line ) : m_pFileName(pFileName), m_nLine(line) {}
  493. bool operator<( const MemInfoKey_t &key ) const
  494. {
  495. int iret = stricmp( m_pFileName, key.m_pFileName );
  496. if ( iret < 0 )
  497. return true;
  498. if ( iret > 0 )
  499. return false;
  500. return m_nLine < key.m_nLine;
  501. }
  502. const char *m_pFileName;
  503. int m_nLine;
  504. };
  505. // NOTE: Deliberately using STL here because the UTL stuff
  506. // is a client of this library; want to avoid circular dependency
  507. // Maps file name to info
  508. typedef std::map< MemInfoKey_t, MemInfo_t, std::less<MemInfoKey_t>, CNoRecurseAllocator<std::pair<const MemInfoKey_t, MemInfo_t> > > StatMap_t;
  509. typedef StatMap_t::iterator StatMapIter_t;
  510. typedef StatMap_t::value_type StatMapEntry_t;
  511. typedef std::set<const char *, CStringLess, CNoRecurseAllocator<const char *> > Filenames_t;
  512. // Heap reporting method
  513. typedef void (*HeapReportFunc_t)( char const *pFormat, ... );
  514. private:
  515. // Returns the actual debug info
  516. void GetActualDbgInfo( const char *&pFileName, int &nLine );
  517. void Initialize();
  518. // Finds the file in our map
  519. MemInfo_t &FindOrCreateEntry( const char *pFileName, int line );
  520. const char *FindOrCreateFilename( const char *pFileName );
  521. // Updates stats
  522. void RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime );
  523. void RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime );
  524. void RegisterAllocation( MemInfo_t &info, int nLogicalSize, int nActualSize, unsigned nTime );
  525. void RegisterDeallocation( MemInfo_t &info, int nLogicalSize, int nActualSize, unsigned nTime );
  526. // Gets the allocation file name
  527. const char *GetAllocatonFileName( void *pMem );
  528. int GetAllocatonLineNumber( void *pMem );
  529. // FIXME: specify a spew output func for dumping stats
  530. // Stat output
  531. void DumpMemInfo( const char *pAllocationName, int line, const MemInfo_t &info );
  532. void DumpFileStats();
  533. void DumpStats();
  534. void DumpStatsFileBase( char const *pchFileBase );
  535. void DumpBlockStats( void *p );
  536. virtual void GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory );
  537. private:
  538. StatMap_t *m_pStatMap;
  539. MemInfo_t m_GlobalInfo;
  540. CFastTimer m_Timer;
  541. bool m_bInitialized;
  542. Filenames_t *m_pFilenames;
  543. HeapReportFunc_t m_OutputFunc;
  544. static int s_pCountSizes[NUM_BYTE_COUNT_BUCKETS];
  545. static const char *s_pCountHeader[NUM_BYTE_COUNT_BUCKETS];
  546. size_t m_sMemoryAllocFailed;
  547. };
  548. static char const *g_pszUnknown = "unknown";
  549. //-----------------------------------------------------------------------------
  550. const int DBG_INFO_STACK_DEPTH = 32;
  551. struct DbgInfoStack_t
  552. {
  553. const char *m_pFileName;
  554. int m_nLine;
  555. };
  556. CThreadLocalPtr<DbgInfoStack_t> g_DbgInfoStack CONSTRUCT_EARLY;
  557. CThreadLocalInt<> g_nDbgInfoStackDepth CONSTRUCT_EARLY;
  558. //-----------------------------------------------------------------------------
  559. // Singleton...
  560. //-----------------------------------------------------------------------------
  561. static CDbgMemAlloc s_DbgMemAlloc CONSTRUCT_EARLY;
  562. #ifndef TIER0_VALIDATE_HEAP
  563. IMemAlloc *g_pMemAlloc = &s_DbgMemAlloc;
  564. #else
  565. IMemAlloc *g_pActualAlloc = &s_DbgMemAlloc;
  566. #endif
  567. //-----------------------------------------------------------------------------
  568. CThreadMutex g_DbgMemMutex CONSTRUCT_EARLY;
  569. #define HEAP_LOCK() AUTO_LOCK( g_DbgMemMutex )
  570. //-----------------------------------------------------------------------------
  571. // Byte count buckets
  572. //-----------------------------------------------------------------------------
  573. int CDbgMemAlloc::s_pCountSizes[CDbgMemAlloc::NUM_BYTE_COUNT_BUCKETS] =
  574. {
  575. 16, 32, 128, 1024, INT_MAX
  576. };
  577. const char *CDbgMemAlloc::s_pCountHeader[CDbgMemAlloc::NUM_BYTE_COUNT_BUCKETS] =
  578. {
  579. "<=16 byte allocations",
  580. "17-32 byte allocations",
  581. "33-128 byte allocations",
  582. "129-1024 byte allocations",
  583. ">1024 byte allocations"
  584. };
  585. //-----------------------------------------------------------------------------
  586. // Standard output
  587. //-----------------------------------------------------------------------------
  588. static FILE* s_DbgFile;
  589. static void DefaultHeapReportFunc( char const *pFormat, ... )
  590. {
  591. va_list args;
  592. va_start( args, pFormat );
  593. vfprintf( s_DbgFile, pFormat, args );
  594. va_end( args );
  595. }
  596. //-----------------------------------------------------------------------------
  597. // Constructor
  598. //-----------------------------------------------------------------------------
  599. CDbgMemAlloc::CDbgMemAlloc() : m_sMemoryAllocFailed( (size_t)0 )
  600. {
  601. // Make sure that we return 64-bit addresses in 64-bit builds.
  602. ReserveBottomMemory();
  603. m_OutputFunc = DefaultHeapReportFunc;
  604. m_bInitialized = false;
  605. if ( !IsDebug() && !IsX360() )
  606. {
  607. Plat_DebugString( "USE_MEM_DEBUG is enabled in a release build. Don't check this in!\n" );
  608. }
  609. }
  610. CDbgMemAlloc::~CDbgMemAlloc()
  611. {
  612. Shutdown();
  613. }
  614. void CDbgMemAlloc::Initialize()
  615. {
  616. if ( !m_bInitialized )
  617. {
  618. m_pFilenames = new Filenames_t;
  619. m_pStatMap= new StatMap_t;
  620. m_bInitialized = true;
  621. }
  622. }
  623. //-----------------------------------------------------------------------------
  624. // Release versions
  625. //-----------------------------------------------------------------------------
  626. void CDbgMemAlloc::Shutdown()
  627. {
  628. if ( m_bInitialized )
  629. {
  630. Filenames_t::const_iterator iter = m_pFilenames->begin();
  631. while ( iter != m_pFilenames->end() )
  632. {
  633. char *pFileName = (char*)(*iter);
  634. free( pFileName );
  635. iter++;
  636. }
  637. m_pFilenames->clear();
  638. m_bInitialized = false;
  639. delete m_pFilenames;
  640. m_pFilenames = nullptr;
  641. delete m_pStatMap;
  642. m_pStatMap = nullptr;
  643. }
  644. m_bInitialized = false;
  645. }
  646. #ifdef WIN32
  647. extern "C" BOOL APIENTRY MemDbgDllMain( HMODULE hDll, DWORD dwReason, PVOID pvReserved )
  648. {
  649. UNREFERENCED_PARAMETER( pvReserved );
  650. // Check if we are shutting down
  651. if ( dwReason == DLL_PROCESS_DETACH )
  652. {
  653. // CDbgMemAlloc is a global object and destructs after the _Lockit object in the CRT runtime,
  654. // so we can't actually operate on the STL object in a normal destructor here as its support libraries have been turned off already
  655. s_DbgMemAlloc.Shutdown();
  656. }
  657. return TRUE;
  658. }
  659. #endif
  660. //-----------------------------------------------------------------------------
  661. // Release versions
  662. //-----------------------------------------------------------------------------
  663. void *CDbgMemAlloc::Alloc( size_t nSize )
  664. {
  665. /*
  666. // NOTE: Uncomment this to find unknown allocations
  667. const char *pFileName = g_pszUnknown;
  668. int nLine;
  669. GetActualDbgInfo( pFileName, nLine );
  670. if (pFileName == g_pszUnknown)
  671. {
  672. int x = 3;
  673. }
  674. */
  675. char szModule[MAX_PATH];
  676. if ( GetCallerModule( szModule ) )
  677. {
  678. return Alloc( nSize, szModule, 0 );
  679. }
  680. else
  681. {
  682. return Alloc( nSize, g_pszUnknown, 0 );
  683. }
  684. // return malloc( nSize );
  685. }
  686. void *CDbgMemAlloc::Realloc( void *pMem, size_t nSize )
  687. {
  688. /*
  689. // NOTE: Uncomment this to find unknown allocations
  690. const char *pFileName = g_pszUnknown;
  691. int nLine;
  692. GetActualDbgInfo( pFileName, nLine );
  693. if (pFileName == g_pszUnknown)
  694. {
  695. int x = 3;
  696. }
  697. */
  698. // FIXME: Should these gather stats?
  699. char szModule[MAX_PATH];
  700. if ( GetCallerModule( szModule ) )
  701. {
  702. return Realloc( pMem, nSize, szModule, 0 );
  703. }
  704. else
  705. {
  706. return Realloc( pMem, nSize, g_pszUnknown, 0 );
  707. }
  708. // return realloc( pMem, nSize );
  709. }
  710. void CDbgMemAlloc::Free( void *pMem )
  711. {
  712. // FIXME: Should these gather stats?
  713. Free( pMem, g_pszUnknown, 0 );
  714. // free( pMem );
  715. }
  716. void *CDbgMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize )
  717. {
  718. return NULL;
  719. }
  720. //-----------------------------------------------------------------------------
  721. // Force file + line information for an allocation
  722. //-----------------------------------------------------------------------------
  723. void CDbgMemAlloc::PushAllocDbgInfo( const char *pFileName, int nLine )
  724. {
  725. if ( g_DbgInfoStack == NULL )
  726. {
  727. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  728. g_nDbgInfoStackDepth = -1;
  729. }
  730. ++g_nDbgInfoStackDepth;
  731. Assert( g_nDbgInfoStackDepth < DBG_INFO_STACK_DEPTH );
  732. g_DbgInfoStack[g_nDbgInfoStackDepth].m_pFileName = FindOrCreateFilename( pFileName );
  733. g_DbgInfoStack[g_nDbgInfoStackDepth].m_nLine = nLine;
  734. }
  735. void CDbgMemAlloc::PopAllocDbgInfo()
  736. {
  737. if ( g_DbgInfoStack == NULL )
  738. {
  739. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  740. g_nDbgInfoStackDepth = -1;
  741. }
  742. --g_nDbgInfoStackDepth;
  743. Assert( g_nDbgInfoStackDepth >= -1 );
  744. }
  745. //-----------------------------------------------------------------------------
  746. // handles storing allocation info for coroutines
  747. //-----------------------------------------------------------------------------
  748. uint32 CDbgMemAlloc::GetDebugInfoSize()
  749. {
  750. return sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH + sizeof( int32 );
  751. }
  752. void CDbgMemAlloc::SaveDebugInfo( void *pvDebugInfo )
  753. {
  754. if ( g_DbgInfoStack == NULL )
  755. {
  756. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  757. g_nDbgInfoStackDepth = -1;
  758. }
  759. int32 *pnStackDepth = (int32*) pvDebugInfo;
  760. *pnStackDepth = g_nDbgInfoStackDepth;
  761. memcpy( pnStackDepth+1, &g_DbgInfoStack[0], sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH );
  762. }
  763. void CDbgMemAlloc::RestoreDebugInfo( const void *pvDebugInfo )
  764. {
  765. if ( g_DbgInfoStack == NULL )
  766. {
  767. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  768. g_nDbgInfoStackDepth = -1;
  769. }
  770. const int32 *pnStackDepth = (const int32*) pvDebugInfo;
  771. g_nDbgInfoStackDepth = *pnStackDepth;
  772. memcpy( &g_DbgInfoStack[0], pnStackDepth+1, sizeof( DbgInfoStack_t ) * DBG_INFO_STACK_DEPTH );
  773. }
  774. void CDbgMemAlloc::InitDebugInfo( void *pvDebugInfo, const char *pchRootFileName, int nLine )
  775. {
  776. int32 *pnStackDepth = (int32*) pvDebugInfo;
  777. if( pchRootFileName )
  778. {
  779. *pnStackDepth = 0;
  780. DbgInfoStack_t *pStackRoot = (DbgInfoStack_t *)(pnStackDepth + 1);
  781. pStackRoot->m_pFileName = FindOrCreateFilename( pchRootFileName );
  782. pStackRoot->m_nLine = nLine;
  783. }
  784. else
  785. {
  786. *pnStackDepth = -1;
  787. }
  788. }
  789. //-----------------------------------------------------------------------------
  790. // Returns the actual debug info
  791. //-----------------------------------------------------------------------------
  792. void CDbgMemAlloc::GetActualDbgInfo( const char *&pFileName, int &nLine )
  793. {
  794. #if defined( USE_STACK_WALK_DETAILED )
  795. return;
  796. #endif
  797. if ( g_DbgInfoStack == NULL )
  798. {
  799. g_DbgInfoStack = (DbgInfoStack_t *)DebugAlloc( sizeof(DbgInfoStack_t) * DBG_INFO_STACK_DEPTH );
  800. g_nDbgInfoStackDepth = -1;
  801. }
  802. if ( g_nDbgInfoStackDepth >= 0 && g_DbgInfoStack[0].m_pFileName)
  803. {
  804. pFileName = g_DbgInfoStack[0].m_pFileName;
  805. nLine = g_DbgInfoStack[0].m_nLine;
  806. }
  807. }
  808. //-----------------------------------------------------------------------------
  809. //
  810. //-----------------------------------------------------------------------------
  811. const char *CDbgMemAlloc::FindOrCreateFilename( const char *pFileName )
  812. {
  813. Initialize();
  814. // If we created it for the first time, actually *allocate* the filename memory
  815. HEAP_LOCK();
  816. // This is necessary for shutdown conditions: the file name is stored
  817. // in some piece of memory in a DLL; if that DLL becomes unloaded,
  818. // we'll have a pointer to crap memory
  819. if ( !pFileName )
  820. {
  821. pFileName = g_pszUnknown;
  822. }
  823. #if defined( USE_STACK_WALK_DETAILED )
  824. {
  825. // Walk the stack to determine what's causing the allocation
  826. void *arrStackAddresses[ 10 ] = { 0 };
  827. int numStackAddrRetrieved = WalkStack( arrStackAddresses, 10, 0 );
  828. char *szStack = StackDescribe( arrStackAddresses, numStackAddrRetrieved );
  829. if ( szStack && *szStack )
  830. {
  831. pFileName = szStack; // Use the stack description for the allocation
  832. }
  833. }
  834. #endif // #if defined( USE_STACK_WALK_DETAILED )
  835. char *pszFilenameCopy;
  836. Filenames_t::const_iterator iter = m_pFilenames->find( pFileName );
  837. if ( iter == m_pFilenames->end() )
  838. {
  839. int nLen = strlen(pFileName) + 1;
  840. pszFilenameCopy = (char *)DebugAlloc( nLen );
  841. memcpy( pszFilenameCopy, pFileName, nLen );
  842. m_pFilenames->insert( pszFilenameCopy );
  843. }
  844. else
  845. {
  846. pszFilenameCopy = (char *)(*iter);
  847. }
  848. return pszFilenameCopy;
  849. }
  850. //-----------------------------------------------------------------------------
  851. // Finds the file in our map
  852. //-----------------------------------------------------------------------------
  853. CDbgMemAlloc::MemInfo_t &CDbgMemAlloc::FindOrCreateEntry( const char *pFileName, int line )
  854. {
  855. Initialize();
  856. // Oh how I love crazy STL. retval.first == the StatMapIter_t in the std::pair
  857. // retval.first->second == the MemInfo_t that's part of the StatMapIter_t
  858. std::pair<StatMapIter_t, bool> retval;
  859. if ( m_pStatMap )
  860. {
  861. retval = m_pStatMap->insert( StatMapEntry_t( MemInfoKey_t( pFileName, line ), MemInfo_t() ) );
  862. }
  863. return retval.first->second;
  864. }
  865. //-----------------------------------------------------------------------------
  866. // Updates stats
  867. //-----------------------------------------------------------------------------
  868. void CDbgMemAlloc::RegisterAllocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime )
  869. {
  870. HEAP_LOCK();
  871. RegisterAllocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
  872. RegisterAllocation( FindOrCreateEntry( pFileName, nLine ), nLogicalSize, nActualSize, nTime );
  873. }
  874. void CDbgMemAlloc::RegisterDeallocation( const char *pFileName, int nLine, int nLogicalSize, int nActualSize, unsigned nTime )
  875. {
  876. HEAP_LOCK();
  877. RegisterDeallocation( m_GlobalInfo, nLogicalSize, nActualSize, nTime );
  878. RegisterDeallocation( FindOrCreateEntry( pFileName, nLine ), nLogicalSize, nActualSize, nTime );
  879. }
  880. void CDbgMemAlloc::RegisterAllocation( MemInfo_t &info, int nLogicalSize, int nActualSize, unsigned nTime )
  881. {
  882. ++info.m_nCurrentCount;
  883. ++info.m_nTotalCount;
  884. if (info.m_nCurrentCount > info.m_nPeakCount)
  885. {
  886. info.m_nPeakCount = info.m_nCurrentCount;
  887. }
  888. info.m_nCurrentSize += nLogicalSize;
  889. info.m_nTotalSize += nLogicalSize;
  890. if (info.m_nCurrentSize > info.m_nPeakSize)
  891. {
  892. info.m_nPeakSize = info.m_nCurrentSize;
  893. }
  894. for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
  895. {
  896. if (nLogicalSize <= s_pCountSizes[i])
  897. {
  898. ++info.m_pCount[i];
  899. break;
  900. }
  901. }
  902. Assert( info.m_nPeakCount >= info.m_nCurrentCount );
  903. Assert( info.m_nPeakSize >= info.m_nCurrentSize );
  904. info.m_nOverheadSize += (nActualSize - nLogicalSize);
  905. if (info.m_nOverheadSize > info.m_nPeakOverheadSize)
  906. {
  907. info.m_nPeakOverheadSize = info.m_nOverheadSize;
  908. }
  909. info.m_nTime += nTime;
  910. }
  911. void CDbgMemAlloc::RegisterDeallocation( MemInfo_t &info, int nLogicalSize, int nActualSize, unsigned nTime )
  912. {
  913. // Check for decrementing these counters below zero. The checks
  914. // must be done here because these unsigned counters will wrap-around and
  915. // still be positive.
  916. Assert( info.m_nCurrentCount != 0 );
  917. // It is technically legal for code to request allocations of zero bytes, and there are a number of places in our code
  918. // that do. So only assert that nLogicalSize >= 0. http://stackoverflow.com/questions/1087042/c-new-int0-will-it-allocate-memory
  919. Assert( nLogicalSize >= 0 );
  920. Assert( info.m_nCurrentSize >= (size_t)nLogicalSize );
  921. --info.m_nCurrentCount;
  922. info.m_nCurrentSize -= nLogicalSize;
  923. for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
  924. {
  925. if (nLogicalSize <= s_pCountSizes[i])
  926. {
  927. --info.m_pCount[i];
  928. break;
  929. }
  930. }
  931. Assert( info.m_nPeakCount >= info.m_nCurrentCount );
  932. Assert( info.m_nPeakSize >= info.m_nCurrentSize );
  933. info.m_nOverheadSize -= (nActualSize - nLogicalSize);
  934. info.m_nTime += nTime;
  935. }
  936. //-----------------------------------------------------------------------------
  937. // Gets the allocation file name
  938. //-----------------------------------------------------------------------------
  939. const char *CDbgMemAlloc::GetAllocatonFileName( void *pMem )
  940. {
  941. if (!pMem)
  942. return "";
  943. CrtDbgMemHeader_t *pHeader = GetCrtDbgMemHeader( pMem );
  944. if ( pHeader->m_pFileName )
  945. return pHeader->m_pFileName;
  946. else
  947. return g_pszUnknown;
  948. }
  949. //-----------------------------------------------------------------------------
  950. // Gets the allocation file name
  951. //-----------------------------------------------------------------------------
  952. int CDbgMemAlloc::GetAllocatonLineNumber( void *pMem )
  953. {
  954. if ( !pMem )
  955. return 0;
  956. CrtDbgMemHeader_t *pHeader = GetCrtDbgMemHeader( pMem );
  957. return pHeader->m_nLineNumber;
  958. }
  959. //-----------------------------------------------------------------------------
  960. // Debug versions of the main allocation methods
  961. //-----------------------------------------------------------------------------
  962. void *CDbgMemAlloc::Alloc( size_t nSize, const char *pFileName, int nLine )
  963. {
  964. HEAP_LOCK();
  965. if ( !m_bInitialized )
  966. return InternalMalloc( nSize, pFileName, nLine );
  967. if ( pFileName != g_pszUnknown )
  968. pFileName = FindOrCreateFilename( pFileName );
  969. GetActualDbgInfo( pFileName, nLine );
  970. /*
  971. if ( strcmp( pFileName, "class CUtlVector<int,class CUtlMemory<int> >" ) == 0)
  972. {
  973. GetActualDbgInfo( pFileName, nLine );
  974. }
  975. */
  976. m_Timer.Start();
  977. void *pMem = InternalMalloc( nSize, pFileName, nLine );
  978. m_Timer.End();
  979. ApplyMemoryInitializations( pMem, nSize );
  980. if ( pMem )
  981. {
  982. RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem ), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  983. }
  984. else
  985. {
  986. SetCRTAllocFailed( nSize );
  987. }
  988. return pMem;
  989. }
  990. void *CDbgMemAlloc::Realloc( void *pMem, size_t nSize, const char *pFileName, int nLine )
  991. {
  992. HEAP_LOCK();
  993. pFileName = FindOrCreateFilename( pFileName );
  994. if ( !m_bInitialized )
  995. return InternalRealloc( pMem, nSize, pFileName, nLine );
  996. if ( pMem != 0 )
  997. {
  998. RegisterDeallocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem), InternalMSize( pMem ), 0 );
  999. }
  1000. GetActualDbgInfo( pFileName, nLine );
  1001. m_Timer.Start();
  1002. pMem = InternalRealloc( pMem, nSize, pFileName, nLine );
  1003. m_Timer.End();
  1004. if ( pMem )
  1005. {
  1006. RegisterAllocation( GetAllocatonFileName( pMem ), GetAllocatonLineNumber( pMem ), InternalLogicalSize( pMem), InternalMSize( pMem ), m_Timer.GetDuration().GetMicroseconds() );
  1007. }
  1008. else
  1009. {
  1010. SetCRTAllocFailed( nSize );
  1011. }
  1012. return pMem;
  1013. }
  1014. void CDbgMemAlloc::Free( void *pMem, const char * /*pFileName*/, int nLine )
  1015. {
  1016. if ( !pMem )
  1017. return;
  1018. HEAP_LOCK();
  1019. if ( !m_bInitialized )
  1020. {
  1021. InternalFree( pMem );
  1022. return;
  1023. }
  1024. int nOldLogicalSize = InternalLogicalSize( pMem );
  1025. int nOldSize = InternalMSize( pMem );
  1026. const char *pOldFileName = GetAllocatonFileName( pMem );
  1027. int oldLine = GetAllocatonLineNumber( pMem );
  1028. m_Timer.Start();
  1029. InternalFree( pMem );
  1030. m_Timer.End();
  1031. RegisterDeallocation( pOldFileName, oldLine, nOldLogicalSize, nOldSize, m_Timer.GetDuration().GetMicroseconds() );
  1032. }
  1033. void *CDbgMemAlloc::Expand_NoLongerSupported( void *pMem, size_t nSize, const char *pFileName, int nLine )
  1034. {
  1035. return NULL;
  1036. }
  1037. //-----------------------------------------------------------------------------
  1038. // Returns size of a particular allocation
  1039. //-----------------------------------------------------------------------------
  1040. size_t CDbgMemAlloc::GetSize( void *pMem )
  1041. {
  1042. HEAP_LOCK();
  1043. if ( !pMem )
  1044. return CalcHeapUsed();
  1045. return InternalMSize( pMem );
  1046. }
  1047. //-----------------------------------------------------------------------------
  1048. // FIXME: Remove when we make our own heap! Crt stuff we're currently using
  1049. //-----------------------------------------------------------------------------
  1050. long CDbgMemAlloc::CrtSetBreakAlloc( long lNewBreakAlloc )
  1051. {
  1052. #ifdef POSIX
  1053. return 0;
  1054. #else
  1055. return _CrtSetBreakAlloc( lNewBreakAlloc );
  1056. #endif
  1057. }
  1058. int CDbgMemAlloc::CrtSetReportMode( int nReportType, int nReportMode )
  1059. {
  1060. #ifdef POSIX
  1061. return 0;
  1062. #else
  1063. return _CrtSetReportMode( nReportType, nReportMode );
  1064. #endif
  1065. }
  1066. int CDbgMemAlloc::CrtIsValidHeapPointer( const void *pMem )
  1067. {
  1068. #ifdef POSIX
  1069. return 0;
  1070. #else
  1071. return _CrtIsValidHeapPointer( pMem );
  1072. #endif
  1073. }
  1074. int CDbgMemAlloc::CrtIsValidPointer( const void *pMem, unsigned int size, int access )
  1075. {
  1076. #ifdef POSIX
  1077. return 0;
  1078. #else
  1079. return _CrtIsValidPointer( pMem, size, access );
  1080. #endif
  1081. }
  1082. #define DBGMEM_CHECKMEMORY 1
  1083. int CDbgMemAlloc::CrtCheckMemory( void )
  1084. {
  1085. #if !defined( DBGMEM_CHECKMEMORY ) || defined( POSIX )
  1086. return 1;
  1087. #else
  1088. if ( !_CrtCheckMemory())
  1089. {
  1090. Msg( "Memory check failed!\n" );
  1091. return 0;
  1092. }
  1093. return 1;
  1094. #endif
  1095. }
  1096. int CDbgMemAlloc::CrtSetDbgFlag( int nNewFlag )
  1097. {
  1098. #ifdef POSIX
  1099. return 0;
  1100. #else
  1101. return _CrtSetDbgFlag( nNewFlag );
  1102. #endif
  1103. }
  1104. void CDbgMemAlloc::CrtMemCheckpoint( _CrtMemState *pState )
  1105. {
  1106. #ifndef POSIX
  1107. _CrtMemCheckpoint( pState );
  1108. #endif
  1109. }
  1110. // FIXME: Remove when we have our own allocator
  1111. void* CDbgMemAlloc::CrtSetReportFile( int nRptType, void* hFile )
  1112. {
  1113. #ifdef POSIX
  1114. return 0;
  1115. #else
  1116. return (void*)_CrtSetReportFile( nRptType, (_HFILE)hFile );
  1117. #endif
  1118. }
  1119. void* CDbgMemAlloc::CrtSetReportHook( void* pfnNewHook )
  1120. {
  1121. #ifdef POSIX
  1122. return 0;
  1123. #else
  1124. return (void*)_CrtSetReportHook( (_CRT_REPORT_HOOK)pfnNewHook );
  1125. #endif
  1126. }
  1127. int CDbgMemAlloc::CrtDbgReport( int nRptType, const char * szFile,
  1128. int nLine, const char * szModule, const char * pMsg )
  1129. {
  1130. #ifdef POSIX
  1131. return 0;
  1132. #else
  1133. return _CrtDbgReport( nRptType, szFile, nLine, szModule, pMsg );
  1134. #endif
  1135. }
  1136. int CDbgMemAlloc::heapchk()
  1137. {
  1138. #ifdef POSIX
  1139. return 0;
  1140. #else
  1141. return _HEAPOK;
  1142. #endif
  1143. }
  1144. void CDbgMemAlloc::DumpBlockStats( void *p )
  1145. {
  1146. DbgMemHeader_t *pBlock = (DbgMemHeader_t *)p - 1;
  1147. if ( !CrtIsValidHeapPointer( pBlock ) )
  1148. {
  1149. Msg( "0x%p is not valid heap pointer\n", p );
  1150. return;
  1151. }
  1152. const char *pFileName = GetAllocatonFileName( p );
  1153. int line = GetAllocatonLineNumber( p );
  1154. Msg( "0x%p allocated by %s line %d, %llu bytes\n", p, pFileName, line, (uint64)GetSize( p ) );
  1155. }
  1156. //-----------------------------------------------------------------------------
  1157. // Stat output
  1158. //-----------------------------------------------------------------------------
  1159. void CDbgMemAlloc::DumpMemInfo( const char *pAllocationName, int line, const MemInfo_t &info )
  1160. {
  1161. m_OutputFunc("%s, line %i\t%.1f\t%.1f\t%.1f\t%.1f\t%.1f\t%d\t%d\t%d\t%d",
  1162. pAllocationName,
  1163. line,
  1164. info.m_nCurrentSize / 1024.0f,
  1165. info.m_nPeakSize / 1024.0f,
  1166. info.m_nTotalSize / 1024.0f,
  1167. info.m_nOverheadSize / 1024.0f,
  1168. info.m_nPeakOverheadSize / 1024.0f,
  1169. (int)(info.m_nTime / 1000),
  1170. info.m_nCurrentCount,
  1171. info.m_nPeakCount,
  1172. info.m_nTotalCount
  1173. );
  1174. for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
  1175. {
  1176. m_OutputFunc( "\t%d", info.m_pCount[i] );
  1177. }
  1178. m_OutputFunc("\n");
  1179. }
  1180. //-----------------------------------------------------------------------------
  1181. // Stat output
  1182. //-----------------------------------------------------------------------------
  1183. void CDbgMemAlloc::DumpFileStats()
  1184. {
  1185. if ( !m_pStatMap )
  1186. return;
  1187. StatMapIter_t iter = m_pStatMap->begin();
  1188. while ( iter != m_pStatMap->end() )
  1189. {
  1190. DumpMemInfo( iter->first.m_pFileName, iter->first.m_nLine, iter->second );
  1191. iter++;
  1192. }
  1193. }
  1194. void CDbgMemAlloc::DumpStatsFileBase( char const *pchFileBase )
  1195. {
  1196. HEAP_LOCK();
  1197. char szFileName[MAX_PATH];
  1198. static int s_FileCount = 0;
  1199. if (m_OutputFunc == DefaultHeapReportFunc)
  1200. {
  1201. char *pPath = "";
  1202. if ( IsX360() )
  1203. {
  1204. pPath = "D:\\";
  1205. }
  1206. #if defined( _MEMTEST ) && defined( _X360 )
  1207. char szXboxName[32];
  1208. strcpy( szXboxName, "xbox" );
  1209. DWORD numChars = sizeof( szXboxName );
  1210. DmGetXboxName( szXboxName, &numChars );
  1211. char *pXboxName = strstr( szXboxName, "_360" );
  1212. if ( pXboxName )
  1213. {
  1214. *pXboxName = '\0';
  1215. }
  1216. SYSTEMTIME systemTime;
  1217. GetLocalTime( &systemTime );
  1218. _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 );
  1219. #else
  1220. _snprintf( szFileName, sizeof( szFileName ), "%s%s%d.txt", pPath, pchFileBase, s_FileCount );
  1221. #endif
  1222. szFileName[ ARRAYSIZE(szFileName) - 1 ] = 0;
  1223. ++s_FileCount;
  1224. s_DbgFile = fopen(szFileName, "wt");
  1225. if (!s_DbgFile)
  1226. return;
  1227. }
  1228. 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");
  1229. for (int i = 0; i < NUM_BYTE_COUNT_BUCKETS; ++i)
  1230. {
  1231. m_OutputFunc( "\t%s", s_pCountHeader[i] );
  1232. }
  1233. m_OutputFunc("\n");
  1234. DumpMemInfo( "Totals", 0, m_GlobalInfo );
  1235. #ifdef WIN32
  1236. if ( IsX360() )
  1237. {
  1238. // add a line that has free memory
  1239. size_t usedMemory, freeMemory;
  1240. GlobalMemoryStatus( &usedMemory, &freeMemory );
  1241. MemInfo_t info;
  1242. // OS takes 32 MB, report our internal allocations only
  1243. info.m_nCurrentSize = usedMemory;
  1244. DumpMemInfo( "Used Memory", 0, info );
  1245. }
  1246. #endif
  1247. DumpFileStats();
  1248. if (m_OutputFunc == DefaultHeapReportFunc)
  1249. {
  1250. fclose(s_DbgFile);
  1251. #if defined( _X360 ) && !defined( _RETAIL )
  1252. XBX_rMemDump( szFileName );
  1253. #endif
  1254. }
  1255. }
  1256. void CDbgMemAlloc::GlobalMemoryStatus( size_t *pUsedMemory, size_t *pFreeMemory )
  1257. {
  1258. if ( !pUsedMemory || !pFreeMemory )
  1259. return;
  1260. #if defined ( _X360 )
  1261. // GlobalMemoryStatus tells us how much physical memory is free
  1262. MEMORYSTATUS stat;
  1263. ::GlobalMemoryStatus( &stat );
  1264. *pFreeMemory = stat.dwAvailPhys;
  1265. // Used is total minus free (discount the 32MB system reservation)
  1266. *pUsedMemory = ( stat.dwTotalPhys - 32*1024*1024 ) - *pFreeMemory;
  1267. #else
  1268. // no data
  1269. *pFreeMemory = 0;
  1270. *pUsedMemory = 0;
  1271. #endif
  1272. }
  1273. //-----------------------------------------------------------------------------
  1274. // Stat output
  1275. //-----------------------------------------------------------------------------
  1276. void CDbgMemAlloc::DumpStats()
  1277. {
  1278. DumpStatsFileBase( "memstats" );
  1279. }
  1280. void CDbgMemAlloc::SetCRTAllocFailed( size_t nSize )
  1281. {
  1282. m_sMemoryAllocFailed = nSize;
  1283. MemAllocOOMError( nSize );
  1284. }
  1285. size_t CDbgMemAlloc::MemoryAllocFailed()
  1286. {
  1287. return m_sMemoryAllocFailed;
  1288. }
  1289. #if defined( LINUX ) && !defined( NO_HOOK_MALLOC )
  1290. //
  1291. // Under linux we can ask GLIBC to override malloc for us
  1292. // Base on code from Ryan, http://hg.icculus.org/icculus/mallocmonitor/file/29c4b0d049f7/monitor_client/malloc_hook_glibc.c
  1293. //
  1294. //
  1295. static void *glibc_malloc_hook = NULL;
  1296. static void *glibc_realloc_hook = NULL;
  1297. static void *glibc_memalign_hook = NULL;
  1298. static void *glibc_free_hook = NULL;
  1299. /* convenience functions for setting the hooks... */
  1300. static inline void save_glibc_hooks(void);
  1301. static inline void set_glibc_hooks(void);
  1302. static inline void set_override_hooks(void);
  1303. CThreadMutex g_HookMutex;
  1304. /*
  1305. * Our overriding hooks...they call through to the original C runtime
  1306. * implementations and report to the monitoring daemon.
  1307. */
  1308. static void *override_malloc_hook(size_t s, const void *caller)
  1309. {
  1310. void *retval;
  1311. AUTO_LOCK( g_HookMutex );
  1312. set_glibc_hooks(); /* put glibc back in control. */
  1313. retval = InternalMalloc( s, NULL, 0 );
  1314. save_glibc_hooks(); /* update in case glibc changed them. */
  1315. set_override_hooks(); /* only restore hooks if daemon is listening */
  1316. return(retval);
  1317. } /* override_malloc_hook */
  1318. static void *override_realloc_hook(void *ptr, size_t s, const void *caller)
  1319. {
  1320. void *retval;
  1321. AUTO_LOCK( g_HookMutex );
  1322. set_glibc_hooks(); /* put glibc back in control. */
  1323. retval = InternalRealloc(ptr, s, NULL, 0); /* call glibc version. */
  1324. save_glibc_hooks(); /* update in case glibc changed them. */
  1325. set_override_hooks(); /* only restore hooks if daemon is listening */
  1326. return(retval);
  1327. } /* override_realloc_hook */
  1328. static void *override_memalign_hook(size_t a, size_t s, const void *caller)
  1329. {
  1330. void *retval;
  1331. AUTO_LOCK( g_HookMutex );
  1332. set_glibc_hooks(); /* put glibc back in control. */
  1333. retval = memalign(a, s); /* call glibc version. */
  1334. save_glibc_hooks(); /* update in case glibc changed them. */
  1335. set_override_hooks(); /* only restore hooks if daemon is listening */
  1336. return(retval);
  1337. } /* override_memalign_hook */
  1338. static void override_free_hook(void *ptr, const void *caller)
  1339. {
  1340. AUTO_LOCK( g_HookMutex );
  1341. set_glibc_hooks(); /* put glibc back in control. */
  1342. InternalFree(ptr); /* call glibc version. */
  1343. save_glibc_hooks(); /* update in case glibc changed them. */
  1344. set_override_hooks(); /* only restore hooks if daemon is listening */
  1345. } /* override_free_hook */
  1346. /*
  1347. * Convenience functions for swapping the hooks around...
  1348. */
  1349. /*
  1350. * Save a copy of the original allocation hooks, so we can call into them
  1351. * from our overriding functions. It's possible that glibc might change
  1352. * these hooks under various conditions (so the manual's examples seem
  1353. * to suggest), so we update them whenever we finish calling into the
  1354. * the originals.
  1355. */
  1356. static inline void save_glibc_hooks(void)
  1357. {
  1358. glibc_malloc_hook = (void *)__malloc_hook;
  1359. glibc_realloc_hook = (void *)__realloc_hook;
  1360. glibc_memalign_hook = (void *)__memalign_hook;
  1361. glibc_free_hook = (void *)__free_hook;
  1362. } /* save_glibc_hooks */
  1363. /*
  1364. * Restore the hooks to the glibc versions. This is needed since, say,
  1365. * their realloc() might call malloc() or free() under the hood, etc, so
  1366. * it's safer to let them have complete control over the subsystem, which
  1367. * also makes our logging saner, too.
  1368. */
  1369. static inline void set_glibc_hooks(void)
  1370. {
  1371. __malloc_hook = (void* (*)(size_t, const void*))glibc_malloc_hook;
  1372. __realloc_hook = (void* (*)(void*, size_t, const void*))glibc_realloc_hook;
  1373. __memalign_hook = (void* (*)(size_t, size_t, const void*))glibc_memalign_hook;
  1374. __free_hook = (void (*)(void*, const void*))glibc_free_hook;
  1375. } /* set_glibc_hooks */
  1376. /*
  1377. * Put our hooks back in place. This should be done after the original
  1378. * glibc version has been called and we've finished any logging (which
  1379. * may call glibc functions, too). This sets us up for the next calls from
  1380. * the application.
  1381. */
  1382. static inline void set_override_hooks(void)
  1383. {
  1384. __malloc_hook = override_malloc_hook;
  1385. __realloc_hook = override_realloc_hook;
  1386. __memalign_hook = override_memalign_hook;
  1387. __free_hook = override_free_hook;
  1388. } /* set_override_hooks */
  1389. /*
  1390. * The Hook Of All Hooks...how we get in there in the first place.
  1391. */
  1392. /*
  1393. * glibc will call this when the malloc subsystem is initializing, giving
  1394. * us a chance to install hooks that override the functions.
  1395. */
  1396. static void __attribute__((constructor)) override_init_hook(void)
  1397. {
  1398. AUTO_LOCK( g_HookMutex );
  1399. /* install our hooks. Will connect to daemon on first malloc, etc. */
  1400. save_glibc_hooks();
  1401. set_override_hooks();
  1402. } /* override_init_hook */
  1403. /*
  1404. * __malloc_initialize_hook is apparently a "weak variable", so you can
  1405. * define and assign it here even though it's in glibc, too. This lets
  1406. * us hook into malloc as soon as the runtime initializes, and before
  1407. * main() is called. Basically, this whole trick depends on this.
  1408. */
  1409. void (*__MALLOC_HOOK_VOLATILE __malloc_initialize_hook)(void) __attribute__((visibility("default")))= override_init_hook;
  1410. #endif // LINUX
  1411. #if defined( OSX ) && !defined( NO_HOOK_MALLOC )
  1412. //
  1413. // pointers to the osx versions of these functions
  1414. static void *osx_malloc_hook = NULL;
  1415. static void *osx_realloc_hook = NULL;
  1416. static void *osx_free_hook = NULL;
  1417. // convenience functions for setting the hooks...
  1418. static inline void save_osx_hooks(void);
  1419. static inline void set_osx_hooks(void);
  1420. static inline void set_override_hooks(void);
  1421. CThreadMutex g_HookMutex;
  1422. //
  1423. // Our overriding hooks...they call through to the original C runtime
  1424. // implementations and report to the monitoring daemon.
  1425. //
  1426. static void *override_malloc_hook(struct _malloc_zone_t *zone, size_t s)
  1427. {
  1428. void *retval;
  1429. set_osx_hooks();
  1430. retval = InternalMalloc( s, NULL, 0 );
  1431. set_override_hooks();
  1432. return(retval);
  1433. }
  1434. static void *override_realloc_hook(struct _malloc_zone_t *zone, void *ptr, size_t s)
  1435. {
  1436. void *retval;
  1437. set_osx_hooks();
  1438. retval = InternalRealloc(ptr, s, NULL, 0);
  1439. set_override_hooks();
  1440. return(retval);
  1441. }
  1442. static void override_free_hook(struct _malloc_zone_t *zone, void *ptr)
  1443. {
  1444. // sometime they pass in a null pointer from higher level calls, just ignore it
  1445. if ( !ptr )
  1446. return;
  1447. set_osx_hooks();
  1448. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( ptr );
  1449. if ( *((int*)pInternalMem->m_Reserved) == 0xf00df00d )
  1450. {
  1451. InternalFree( ptr );
  1452. }
  1453. set_override_hooks();
  1454. }
  1455. /*
  1456. These are func's we could optionally override right now on OSX but don't need to
  1457. static size_t override_size_hook(struct _malloc_zone_t *zone, const void *ptr)
  1458. {
  1459. set_osx_hooks();
  1460. DbgMemHeader_t *pInternalMem = GetCrtDbgMemHeader( (void *)ptr );
  1461. set_override_hooks();
  1462. if ( *((int*)pInternalMem->m_Reserved) == 0xf00df00d )
  1463. {
  1464. return pInternalMem->nLogicalSize;
  1465. }
  1466. return 0;
  1467. }
  1468. static void *override_calloc_hook(struct _malloc_zone_t *zone, size_t num_items, size_t size )
  1469. {
  1470. void *ans = override_malloc_hook( zone, num_items*size );
  1471. if ( !ans )
  1472. return 0;
  1473. memset( ans, 0x0, num_items*size );
  1474. return ans;
  1475. }
  1476. static void *override_valloc_hook(struct _malloc_zone_t *zone, size_t size )
  1477. {
  1478. return override_calloc_hook( zone, 1, size );
  1479. }
  1480. static void override_destroy_hook(struct _malloc_zone_t *zone)
  1481. {
  1482. }
  1483. */
  1484. static inline void unprotect_malloc_zone( malloc_zone_t *malloc_zone )
  1485. {
  1486. // Starting in OS X 10.7 the default zone defaults to read-only, version 8.
  1487. // The version check may not be necessary, but we know it was RW before that.
  1488. if ( malloc_zone->version >= 8 )
  1489. {
  1490. vm_protect( mach_task_self(), (uintptr_t)malloc_zone, sizeof( malloc_zone_t ), 0, VM_PROT_READ | VM_PROT_WRITE );
  1491. }
  1492. }
  1493. static inline void protect_malloc_zone( malloc_zone_t *malloc_zone )
  1494. {
  1495. if ( malloc_zone->version >= 8 )
  1496. {
  1497. vm_protect( mach_task_self(), (uintptr_t)malloc_zone, sizeof( malloc_zone_t ), 0, VM_PROT_READ );
  1498. }
  1499. }
  1500. //
  1501. // Save a copy of the original allocation hooks, so we can call into them
  1502. // from our overriding functions. It's possible that osx might change
  1503. // these hooks under various conditions (so the manual's examples seem
  1504. // to suggest), so we update them whenever we finish calling into the
  1505. // the originals.
  1506. //
  1507. static inline void save_osx_hooks(void)
  1508. {
  1509. malloc_zone_t *malloc_zone = malloc_default_zone();
  1510. osx_malloc_hook = (void *)malloc_zone->malloc;
  1511. osx_realloc_hook = (void *)malloc_zone->realloc;
  1512. osx_free_hook = (void *)malloc_zone->free;
  1513. // These are func's we could optionally override right now on OSX but don't need to
  1514. // osx_size_hook = (void *)malloc_zone->size;
  1515. // osx_calloc_hook = (void *)malloc_zone->calloc;
  1516. // osx_valloc_hook = (void *)malloc_zone->valloc;
  1517. // osx_destroy_hook = (void *)malloc_zone->destroy;
  1518. }
  1519. //
  1520. // Restore the hooks to the osx versions. This is needed since, say,
  1521. // their realloc() might call malloc() or free() under the hood, etc, so
  1522. // it's safer to let them have complete control over the subsystem, which
  1523. // also makes our logging saner, too.
  1524. //
  1525. static inline void set_osx_hooks(void)
  1526. {
  1527. malloc_zone_t *malloc_zone = malloc_default_zone();
  1528. unprotect_malloc_zone( malloc_zone );
  1529. malloc_zone->malloc = (void* (*)(_malloc_zone_t*, size_t))osx_malloc_hook;
  1530. malloc_zone->realloc = (void* (*)(_malloc_zone_t*, void*, size_t))osx_realloc_hook;
  1531. malloc_zone->free = (void (*)(_malloc_zone_t*, void*))osx_free_hook;
  1532. protect_malloc_zone( malloc_zone );
  1533. // These are func's we could optionally override right now on OSX but don't need to
  1534. //malloc_zone->size = (size_t (*)(_malloc_zone_t*, const void *))osx_size_hook;
  1535. //malloc_zone->calloc = (void* (*)(_malloc_zone_t*, size_t, size_t))osx_calloc_hook;
  1536. //malloc_zone->valloc = (void* (*)(_malloc_zone_t*, size_t))osx_valloc_hook;
  1537. //malloc_zone->destroy = (void (*)(_malloc_zone_t*))osx_destroy_hook;
  1538. }
  1539. /*
  1540. * Put our hooks back in place. This should be done after the original
  1541. * osx version has been called and we've finished any logging (which
  1542. * may call osx functions, too). This sets us up for the next calls from
  1543. * the application.
  1544. */
  1545. static inline void set_override_hooks(void)
  1546. {
  1547. malloc_zone_t *malloc_zone = malloc_default_zone();
  1548. AssertMsg( malloc_zone, "No malloc zone returned by malloc_default_zone" );
  1549. unprotect_malloc_zone( malloc_zone );
  1550. malloc_zone->malloc = override_malloc_hook;
  1551. malloc_zone->realloc = override_realloc_hook;
  1552. malloc_zone->free = override_free_hook;
  1553. protect_malloc_zone( malloc_zone );
  1554. // These are func's we could optionally override right now on OSX but don't need to
  1555. //malloc_zone->size = override_size_hook;
  1556. //malloc_zone->calloc = override_calloc_hook;
  1557. // malloc_zone->valloc = override_valloc_hook;
  1558. //malloc_zone->destroy = override_destroy_hook;
  1559. }
  1560. //
  1561. // The Hook Of All Hooks...how we get in there in the first place.
  1562. //
  1563. // osx will call this when the malloc subsystem is initializing, giving
  1564. // us a chance to install hooks that override the functions.
  1565. //
  1566. void __attribute__ ((constructor)) mem_init(void)
  1567. {
  1568. AUTO_LOCK( g_HookMutex );
  1569. save_osx_hooks();
  1570. set_override_hooks();
  1571. }
  1572. void *operator new( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  1573. {
  1574. set_osx_hooks();
  1575. void *pMem = g_pMemAlloc->Alloc(nSize, pFileName, nLine);
  1576. set_override_hooks();
  1577. return pMem;
  1578. }
  1579. void *operator new[] ( size_t nSize, int nBlockUse, const char *pFileName, int nLine )
  1580. {
  1581. set_osx_hooks();
  1582. void *pMem = g_pMemAlloc->Alloc(nSize, pFileName, nLine);
  1583. set_override_hooks();
  1584. return pMem;
  1585. }
  1586. #endif // defined( OSX ) && !defined( NO_HOOK_MALLOC )
  1587. #endif // (defined(_DEBUG) || defined(USE_MEM_DEBUG))
  1588. #endif // !STEAM && !NO_MALLOC_OVERRIDE