Leaked source code of windows server 2003
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.

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