Source code of Windows XP (NT5)
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.

565 lines
14 KiB

  1. //---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright (C) Microsoft Corporation, 1992 - 2000.
  5. //
  6. // File: MEMORY.CXX
  7. //
  8. // Contents: Memory allocators
  9. //
  10. // History: 9-27-93 IsaacHe Heavy modifications throughout:
  11. // Elimination of Buddy Heap
  12. // Rework of stack backtrace & leak checks
  13. // Use VirtualAlloc on large chunks
  14. // Elimination of KERNEL and FLAT stuff
  15. // Cosmetics
  16. // 10-12-93 IsaacHe Made a separate info level for heap
  17. // 12-3-93 IsaacHe Removed the VirtualAlloc stuff. Moved
  18. // debug support code to another file.
  19. // 7/27/94 Cruft removal
  20. // 4/30/96 dlee Cruft removal
  21. // 9/26/96 dlee Cruft removal
  22. //
  23. //----------------------------------------------------------------------------
  24. #include <pch.cxx>
  25. #pragma hdrstop
  26. #include <falloc.hxx>
  27. #include <cidllsem.hxx>
  28. // Uncomment this to include OLE heap tracking. This isn't normally done
  29. // because there are some issues with exiting cleanly.
  30. //#define USE_IMALLOCSPY
  31. typedef void * (* PAllocFun)( UINT cb );
  32. typedef void (* PFreeFun)( void * pv );
  33. typedef UINT (* PSizeFun)( const void * pv );
  34. typedef BOOL (* PValidateFun)( const void * pv );
  35. typedef void (* PUtilizationFun)( void );
  36. PAllocFun realAlloc = 0;
  37. PFreeFun realFree = 0;
  38. PSizeFun realSize = 0;
  39. PValidateFun realValidate = 0;
  40. PUtilizationFun realUtilization = 0;
  41. HANDLE gmem_hHeap = 0;
  42. inline void * heapAlloc( UINT cbAlloc )
  43. {
  44. Win4Assert( 0 != gmem_hHeap );
  45. void * p = (void *) HeapAlloc( gmem_hHeap, 0, cbAlloc );
  46. #if CIDBG == 1 || DBG == 1
  47. if ( 0 != p )
  48. RtlFillMemory( p, cbAlloc, 0xda );
  49. #endif
  50. return p;
  51. } //heapAlloc
  52. inline void heapFree( void * p )
  53. {
  54. Win4Assert( 0 != gmem_hHeap );
  55. #if CIDBG == 1 || DBG == 1
  56. UINT cb = (UINT)HeapSize( gmem_hHeap, 0, p );
  57. if ( ~0 != cb )
  58. RtlFillMemory( p, cb, 0xdc );
  59. #endif
  60. if ( !HeapFree( gmem_hHeap, 0, p ) )
  61. Win4Assert(!"Bad ptr for operator delete");
  62. } //heapFree
  63. inline UINT heapSize( void const * p )
  64. {
  65. return (UINT) HeapSize( gmem_hHeap, 0, p );
  66. }
  67. inline BOOL heapValidate( const void * p )
  68. {
  69. if ( 0 == p )
  70. return TRUE;
  71. Win4Assert( 0 != gmem_hHeap );
  72. if ( HeapSize( gmem_hHeap, 0, p ) <= 0 )
  73. {
  74. Win4Assert( !"Invalid pointer detected" );
  75. return FALSE;
  76. }
  77. return TRUE;
  78. } //heapValidate
  79. inline void heapUtilization()
  80. {
  81. // No stats here when you use the system heap. Use !heap or dh.exe
  82. } //heapUtilization
  83. #if CIDBG == 1 || DBG == 1
  84. #include <tracheap.h>
  85. #include <dbgpoint.hxx>
  86. #include <propapi.h>
  87. #include "spy.hxx"
  88. CMallocSpy * g_pMallocSpy = 0;
  89. void __cdecl HeapExit()
  90. {
  91. #ifdef USE_IMALLOCSPY
  92. if ( g_pMallocSpy )
  93. {
  94. MallocSpyUnRegister( g_pMallocSpy );
  95. g_pMallocSpy->Release();
  96. }
  97. #endif // USE_IMALLOCSPY
  98. AllocArenaDump( 0 );
  99. } //HeapExit
  100. //
  101. // Debugging dialog support...
  102. //
  103. extern ULONG Win4InfoLevel;
  104. extern ULONG Win4InfoMask;
  105. BOOL g_fDumpArena = FALSE;
  106. DWORD g_FailTestRatio = FALSE;
  107. void EnableCiFailTest( unsigned Ratio )
  108. {
  109. g_FailTestRatio = Ratio;
  110. }
  111. DECLARE_DEBUG( heap );
  112. #define heapDebugOut(x) heapInlineDebugOut x
  113. CRITICAL_SECTION g_csDbgMemExclusive; // ensures single creator of arena
  114. UINT ciAddAllocRecord( UINT cb )
  115. {
  116. return cb + sizeof 1 + sizeof AHeader;
  117. }
  118. void * ciRecordAlloc( void * p, UINT size )
  119. {
  120. if ( 0 == p )
  121. return p;
  122. static AllocArena * pAllocArena = (AllocArena *) -1;
  123. if ( pAllocArena == (AllocArena *) -1 )
  124. {
  125. // Note: If the cs is invalid at this point, it's because
  126. // you're calling operator new too early. Construct
  127. // your global object later.
  128. EnterCriticalSection( &g_csDbgMemExclusive );
  129. if ( pAllocArena == (AllocArena *) -1 )
  130. {
  131. pAllocArena = AllocArenaCreate( MEMCTX_TASK,
  132. "Operator new");
  133. #ifdef USE_IMALLOCSPY
  134. MallocSpyRegister( &g_pMallocSpy );
  135. #endif // USE_IMALLOCSPY
  136. atexit( HeapExit );
  137. }
  138. LeaveCriticalSection( &g_csDbgMemExclusive );
  139. }
  140. ((AHeader *)p)->size = size;
  141. ((AHeader *)p)->p = AllocArenaRecordAlloc( pAllocArena, size );
  142. p = (AHeader *)p + 1;
  143. *((char *)p + size ) = ALLOC_SIGNATURE;
  144. return p;
  145. } //ciRecordAlloc
  146. UINT ciAllocSize( void * p )
  147. {
  148. if ( 0 == p )
  149. return 0;
  150. AHeader *ap = (AHeader *)p - 1;
  151. return ap->size;
  152. } //ciAllocSize
  153. void * ciRecordFree( void * p )
  154. {
  155. if ( 0 == p )
  156. return 0;
  157. AHeader *ap = (AHeader *)p - 1;
  158. switch( *((char *)p + ap->size) )
  159. {
  160. case ALLOC_SIGNATURE:
  161. break;
  162. case FREE_SIGNATURE:
  163. heapDebugOut(( DEB_WARN, "Invalid freed pointer: 0x%x\n", p ));
  164. Win4Assert( !"Invalid freed pointer" );
  165. return 0;
  166. break;
  167. default:
  168. heapDebugOut((DEB_WARN, "Invalid overrun pointer: 0x%x\n", p ));
  169. Win4Assert( !"Invalid overrun pointer" );
  170. return 0;
  171. break;
  172. }
  173. *((char *)p + ap->size) = FREE_SIGNATURE;
  174. if ( 0 != ap->p )
  175. AllocArenaRecordFree( ap->p, ap->size );
  176. return (void *) ap;
  177. } //CiRecordFree
  178. //+---------------------------------------------------------------------------
  179. //
  180. // Function: ciNewDebug
  181. //
  182. // Synopsis: Debugging allocator
  183. //
  184. // Arguments: [size] -- Size of the memory to allocate.
  185. //
  186. // Returns: A pointer to the allocated memory.
  187. //
  188. //----------------------------------------------------------------------------
  189. void * ciNewDebug( size_t size )
  190. {
  191. // just a convenient way to dump the allocation arena
  192. if ( g_fDumpArena )
  193. {
  194. // Note: If the cs is invalid at this point, it's because
  195. // you're calling operator new too early. Construct
  196. // your global object later.
  197. EnterCriticalSection( &g_csDbgMemExclusive );
  198. if ( g_fDumpArena )
  199. {
  200. AllocArenaDump( 0 );
  201. realUtilization();
  202. g_fDumpArena = FALSE;
  203. heapDebugOut(( DEB_FORCE, "done dumping the heap\n" ));
  204. DebugBreak();
  205. }
  206. LeaveCriticalSection( &g_csDbgMemExclusive );
  207. }
  208. // fail test?
  209. if ( ( 0 != g_FailTestRatio ) &&
  210. ( ( rand() % g_FailTestRatio ) == 1 ) )
  211. return 0;
  212. UINT cb = ciAddAllocRecord( size );
  213. void *p = realAlloc( cb );
  214. if ( 0 != p )
  215. p = ciRecordAlloc( p, size );
  216. return p;
  217. } //CiNewDebug
  218. //+---------------------------------------------------------------------------
  219. //
  220. // Function: ciNewDebugNoRecord
  221. //
  222. // Synopsis: Compatible with ciNewDebug, but doesn't log allocation.
  223. //
  224. // Arguments: [size] -- Size of the memory to allocate.
  225. //
  226. // Returns: A pointer to the allocated memory.
  227. //
  228. //----------------------------------------------------------------------------
  229. void * ciNewDebugNoRecord( size_t size )
  230. {
  231. void *p = realAlloc( 1 + size + sizeof AHeader );
  232. if ( 0 != p )
  233. {
  234. ((AHeader *)p)->size = size;
  235. ((AHeader *)p)->p = 0;
  236. p = (AHeader *)p + 1;
  237. *((char *)p + size ) = ALLOC_SIGNATURE;
  238. }
  239. return p;
  240. } //CiNewDebugNoRecord
  241. #ifdef UseCICoTaskMem
  242. #undef CoTaskMemAlloc
  243. WINOLEAPI_(LPVOID) CoTaskMemAlloc(IN ULONG cb);
  244. void * CICoTaskMemAlloc( ULONG cb )
  245. {
  246. // fail test?
  247. if ( ( 0 != g_FailTestRatio ) &&
  248. ( ( rand() % g_FailTestRatio ) == 1 ) )
  249. return 0;
  250. ULONG cbAlloc = ciAddAllocRecord( cb );
  251. void *p = CoTaskMemAlloc( cbAlloc );
  252. return ciRecordAlloc( p, cb );
  253. }
  254. #undef CoTaskMemFree
  255. WINOLEAPI_(void) CoTaskMemFree(IN LPVOID p);
  256. void CICoTaskMemFree( LPVOID p )
  257. {
  258. void *pv = ciRecordFree( p );
  259. CoTaskMemFree( pv );
  260. }
  261. #endif // UseCICoTaskMem
  262. #else // CIDBG == 1 || DBG == 1
  263. #define heapDebugOut(x)
  264. #endif // CIDBG == 1 || DBG == 1
  265. //+---------------------------------------------------------------------------
  266. //
  267. // Function: ExceptDllMain
  268. //
  269. // Synopsis: Entry point on DLL initialization for exception-specific stuff.
  270. //
  271. // History: 10-12-93 kevinro Created
  272. // 02-28-96 KyleP Cleanup
  273. //
  274. //----------------------------------------------------------------------------
  275. #if CIDBG == 1 || DBG == 1
  276. extern CDLLStaticMutexSem g_mxsAssert;
  277. #endif // CIDBG == 1 || DBG == 1
  278. extern CStaticMutexSem g_mtxGetStackTrace;
  279. extern CMemMutex gmem_mutex;
  280. BOOL ExceptDllMain( HANDLE hDll, DWORD dwReason, LPVOID lpReserved )
  281. {
  282. switch( dwReason )
  283. {
  284. case DLL_PROCESS_ATTACH:
  285. #if CIDBG == 1 || DBG == 1
  286. {
  287. // These two objects are initialized from win.ini
  288. static CInfoLevel level( L"Win4InfoLevel",Win4InfoLevel);
  289. static CInfoLevel mask( L"Win4InfoMask", Win4InfoMask, (ULONG)-1 );
  290. }
  291. InitializeCriticalSection( &g_csDbgMemExclusive );
  292. g_mxsAssert.Init();
  293. #endif // CIDBG == 1 || DBG == 1
  294. g_mtxGetStackTrace.Init();
  295. gmem_mutex.Init();
  296. gmem_hHeap = HeapCreate( 0, 0, 0 );
  297. //
  298. // HeapAlloc is faster on MP machines, and falloc is faster and
  299. // uses less working set on UP machines.
  300. //
  301. SYSTEM_INFO si;
  302. GetSystemInfo( &si );
  303. if ( si.dwNumberOfProcessors > 1 )
  304. {
  305. realAlloc = heapAlloc;
  306. realFree = heapFree;
  307. realSize = heapSize;
  308. realValidate = heapValidate;
  309. realUtilization = heapUtilization;
  310. }
  311. else
  312. {
  313. realAlloc = memAlloc;
  314. realFree = memFree;
  315. realSize = memSize;
  316. realValidate = memIsValidPointer;
  317. realUtilization = memUtilization;
  318. }
  319. break;
  320. case DLL_PROCESS_DETACH:
  321. if ( 0 != gmem_hHeap )
  322. {
  323. HeapDestroy( gmem_hHeap );
  324. gmem_hHeap = 0;
  325. }
  326. #if CIDBG == 1 || DBG == 1
  327. DeleteCriticalSection( &g_csDbgMemExclusive );
  328. g_mxsAssert.Delete();
  329. #endif // CIDBG == 1 || DBG == 1
  330. break;
  331. case DLL_THREAD_ATTACH:
  332. #if 0
  333. //
  334. // can't do the exception translation here for several reasons:
  335. //
  336. // 1) iis creates threads before we are called, so they don't have
  337. // their exceptions translated (on dll load, those threads don't
  338. // send thread_attach's, per the sdk)
  339. // 2) for some reason, we aren't getting thread attaches anyway.
  340. // 3) the first thread to call us only is guaranteed to send
  341. // a process_attach, not a thread_attach (per sdk)
  342. // 4) we don't want to hose other ISAPI apps by changing
  343. // their exception handler.
  344. //
  345. //DbgPrint("thread attach!\n");
  346. #if defined(NATIVE_EH)
  347. _set_se_translator( SystemExceptionTranslator );
  348. //DbgPrint("set the handler!\n");
  349. #endif // NATIVE_EH
  350. #endif
  351. break;
  352. }
  353. return TRUE;
  354. } //ExceptDllMain
  355. //+---------------------------------------------------------------------------
  356. //
  357. // Function: function ciNew, public
  358. //
  359. // Synopsis: Global operator new which throws exceptions.
  360. //
  361. // Effects: Keeps track of the most recent heap allocation in each
  362. // thread. This information is used to determine when to
  363. // unlink CUnwindable objects.
  364. //
  365. // Arguments: [size] -- Size of the memory to allocate.
  366. //
  367. // Returns: A pointer to the allocated memory.
  368. // Is *NOT* initialized to 0!
  369. // It is 8-byte aligned.
  370. //
  371. // Modifies: _pLastNew in _exceptioncontext.
  372. //
  373. //----------------------------------------------------------------------------
  374. void * ciNew( size_t size )
  375. {
  376. Win4Assert( 0 != realAlloc );
  377. #if CIDBG == 1 || DBG == 1
  378. void* p = ciNewDebug( size );
  379. #else // CIDBG == 1 || DBG == 1
  380. void * p = realAlloc( size );
  381. #endif // CIDBG == 1 || DBG == 1
  382. if ( 0 == p )
  383. THROW( CException( E_OUTOFMEMORY ) );
  384. return p;
  385. } //ciNew
  386. //+---------------------------------------------------------------------------
  387. //
  388. // Function: ciDelete, public
  389. //
  390. // Synopsis: Matches the operator new above.
  391. //
  392. // Arguments: [p] -- The pointer to delete.
  393. //
  394. // Requires: [p] was allocated with ciNew
  395. //
  396. //----------------------------------------------------------------------------
  397. void ciDelete( void * p )
  398. {
  399. #if CIDBG == 1 || DBG == 1
  400. p = ciRecordFree( p );
  401. #endif // CIDBG == 1 || DBG == 1
  402. if ( 0 == p )
  403. return;
  404. realFree( p );
  405. } //ciDelete
  406. //+---------------------------------------------------------------------------
  407. //
  408. // Function: ciIsValidPointer, public
  409. //
  410. // Synopsis: Determines if a pointer is valid, was allocated with ciNew,
  411. // and can be freed with ciDelete.
  412. //
  413. // Arguments: [p] -- The pointer to check
  414. //
  415. // Returns: TRUE if the pointer appears valid, FALSE otherwise
  416. //
  417. //----------------------------------------------------------------------------
  418. BOOL ciIsValidPointer( const void * p )
  419. {
  420. if ( 0 == p )
  421. return TRUE;
  422. Win4Assert( 0 != realValidate );
  423. //
  424. // Allocations are rounded up to at least 8 bytes, so at least that
  425. // much must be writable.
  426. //
  427. if ( IsBadWritePtr( (void *) p, 8 ) )
  428. {
  429. heapDebugOut(( DEB_WARN, "Invalid non-writable pointer: 0x%x\n", p ));
  430. Win4Assert( !"Invalid non-writable pointer" );
  431. return FALSE;
  432. }
  433. #if CIDBG == 1 || DBG == 1
  434. AHeader *ap = (AHeader *)p - 1;
  435. switch( *((char *)p + ap->size) )
  436. {
  437. case ALLOC_SIGNATURE:
  438. break;
  439. case FREE_SIGNATURE:
  440. heapDebugOut(( DEB_WARN, "Invalid freed pointer: 0x%x\n", p ));
  441. Win4Assert( !"Invalid freed pointer" );
  442. return FALSE;
  443. break;
  444. default:
  445. heapDebugOut((DEB_WARN, "Invalid overrun pointer: 0x%x\n", p ));
  446. Win4Assert( !"Invalid overrun pointer" );
  447. return FALSE;
  448. break;
  449. }
  450. p = (void *) ap;
  451. #endif // CIDBG == 1 || DBG == 1
  452. return realValidate( p );
  453. } //ciIsValidPointer