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.

1753 lines
36 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. heapleak.c
  5. Abstract:
  6. Garbage collection leak detection
  7. Author:
  8. Adrian Marinescu (adrmarin) 04-24-2000
  9. Revision History:
  10. --*/
  11. #include "ntrtlp.h"
  12. #include "heap.h"
  13. #include "heappriv.h"
  14. //
  15. // heap walking contexts.
  16. //
  17. #define CONTEXT_START_GLOBALS 11
  18. #define CONTEXT_START_HEAP 1
  19. #define CONTEXT_END_HEAP 2
  20. #define CONTEXT_START_SEGMENT 3
  21. #define CONTEXT_END_SEGMENT 4
  22. #define CONTEXT_FREE_BLOCK 5
  23. #define CONTEXT_BUSY_BLOCK 6
  24. #define CONTEXT_LOOKASIDE_BLOCK 7
  25. #define CONTEXT_VIRTUAL_BLOCK 8
  26. #define CONTEXT_END_BLOCKS 9
  27. #define CONTEXT_ERROR 10
  28. typedef BOOLEAN (*HEAP_ITERATOR_CALLBACK)(
  29. IN ULONG Context,
  30. IN PHEAP HeapAddress,
  31. IN PHEAP_SEGMENT SegmentAddress,
  32. IN PHEAP_ENTRY EntryAddress,
  33. IN ULONG_PTR Data
  34. );
  35. //
  36. // Garbage collector structures
  37. //
  38. typedef enum _USAGE_TYPE {
  39. UsageUnknown,
  40. UsageModule,
  41. UsageHeap,
  42. UsageOther
  43. } USAGE_TYPE;
  44. typedef struct _HEAP_BLOCK {
  45. LIST_ENTRY Entry;
  46. ULONG_PTR BlockAddress;
  47. ULONG_PTR Size;
  48. LONG Count;
  49. } HEAP_BLOCK, *PHEAP_BLOCK;
  50. typedef struct _BLOCK_DESCR {
  51. USAGE_TYPE Type;
  52. ULONG_PTR Heap;
  53. LONG Count;
  54. HEAP_BLOCK Blocks[1];
  55. }BLOCK_DESCR, *PBLOCK_DESCR;
  56. typedef struct _MEMORY_MAP {
  57. ULONG_PTR Granularity;
  58. ULONG_PTR Offset;
  59. ULONG_PTR MaxAddress;
  60. CHAR FlagsBitmap[256 / 8];
  61. union{
  62. struct _MEMORY_MAP * Details[ 256 ];
  63. PBLOCK_DESCR Usage[ 256 ];
  64. };
  65. struct _MEMORY_MAP * Parent;
  66. } MEMORY_MAP, *PMEMORY_MAP;
  67. //
  68. // Process leak detection flags
  69. //
  70. #define INSPECT_LEAKS 1
  71. #define BREAK_ON_LEAKS 2
  72. ULONG RtlpShutdownProcessFlags = 0;
  73. //
  74. // Allocation routines. It creates a temporary heap for the temporary
  75. // leak detection structures
  76. //
  77. HANDLE RtlpLeakHeap;
  78. #define RtlpLeakAllocateBlock(Size) RtlAllocateHeap(RtlpLeakHeap, 0, Size)
  79. //
  80. // Local data declarations
  81. //
  82. MEMORY_MAP RtlpProcessMemoryMap;
  83. LIST_ENTRY RtlpBusyList;
  84. LIST_ENTRY RtlpLeakList;
  85. ULONG RtlpLeaksCount = 0;
  86. ULONG_PTR RtlpLDPreviousPage = 0;
  87. ULONG_PTR RtlpLDCrtPage = 0;
  88. LONG RtlpLDNumBlocks = 0;
  89. PHEAP_BLOCK RtlpTempBlocks = NULL;
  90. ULONG_PTR RtlpCrtHeapAddress = 0;
  91. ULONG_PTR RtlpLeakHeapAddress = 0;
  92. ULONG_PTR RtlpPreviousStartAddress = 0;
  93. //
  94. // Debugging facility
  95. //
  96. ULONG_PTR RtlpBreakAtAddress = MAXULONG_PTR;
  97. //
  98. // Walking heap routines. These are general purposes routines that
  99. // receive a callback function to handle a specific operation
  100. //
  101. BOOLEAN
  102. RtlpReadHeapSegment(
  103. IN PHEAP Heap,
  104. IN ULONG SegmentIndex,
  105. IN PHEAP_SEGMENT Segment,
  106. IN HEAP_ITERATOR_CALLBACK HeapCallback
  107. )
  108. /*++
  109. Routine Description:
  110. This routine is called to walk a heap segment. For each block
  111. from the segment is invoked the HeapCallback function.
  112. Arguments:
  113. Heap - The heap being walked
  114. SegmentIndex - The index of this segment
  115. Segment - The segment to be walked
  116. HeapCallback - a HEAP_ITERATOR_CALLBACK function passed down from the heap walk
  117. Return Value:
  118. TRUE if succeeds.
  119. --*/
  120. {
  121. PHEAP_ENTRY PrevEntry, Entry, NextEntry;
  122. PHEAP_UNCOMMMTTED_RANGE UnCommittedRange;
  123. ULONG_PTR UnCommittedRangeAddress = 0;
  124. SIZE_T UnCommittedRangeSize = 0;
  125. //
  126. // Ask the callback if we're required to walk this segment. Return otherwise.
  127. //
  128. if (!(*HeapCallback)( CONTEXT_START_SEGMENT,
  129. Heap,
  130. Segment,
  131. 0,
  132. 0
  133. )) {
  134. return FALSE;
  135. }
  136. //
  137. // Prepare to read the uncommitted ranges. we need to jump
  138. // to the next uncommitted range for each last block
  139. //
  140. UnCommittedRange = Segment->UnCommittedRanges;
  141. if (UnCommittedRange) {
  142. UnCommittedRangeAddress = (ULONG_PTR)UnCommittedRange->Address;
  143. UnCommittedRangeSize = UnCommittedRange->Size;
  144. }
  145. //
  146. // Walk the segment, block by block
  147. //
  148. Entry = (PHEAP_ENTRY)Segment->BaseAddress;
  149. PrevEntry = 0;
  150. while (Entry < Segment->LastValidEntry) {
  151. ULONG EntryFlags = Entry->Flags;
  152. //
  153. // Determine the next block entry. Size is in heap granularity and
  154. // sizeof(HEAP_ENTRY) == HEAP_GRANULARITY.
  155. //
  156. NextEntry = Entry + Entry->Size;
  157. (*HeapCallback)( (Entry->Flags & HEAP_ENTRY_BUSY ?
  158. CONTEXT_BUSY_BLOCK :
  159. CONTEXT_FREE_BLOCK),
  160. Heap,
  161. Segment,
  162. Entry,
  163. Entry->Size
  164. );
  165. PrevEntry = Entry;
  166. Entry = NextEntry;
  167. //
  168. // Check whether this is the last entry
  169. //
  170. if (EntryFlags & HEAP_ENTRY_LAST_ENTRY) {
  171. if ((ULONG_PTR)Entry == UnCommittedRangeAddress) {
  172. //
  173. // Here we need to skip the uncommited range and jump
  174. // to the next valid block
  175. //
  176. PrevEntry = 0;
  177. Entry = (PHEAP_ENTRY)(UnCommittedRangeAddress + UnCommittedRangeSize);
  178. UnCommittedRange = UnCommittedRange->Next;
  179. if (UnCommittedRange) {
  180. UnCommittedRangeAddress = UnCommittedRange->Address;
  181. UnCommittedRangeSize = UnCommittedRange->Size;
  182. }
  183. } else {
  184. //
  185. // We finished the search because we exausted the uncommitted
  186. // ranges
  187. //
  188. break;
  189. }
  190. }
  191. }
  192. //
  193. // Return to our caller.
  194. //
  195. return TRUE;
  196. }
  197. BOOLEAN
  198. RtlpReadHeapData(
  199. IN PHEAP Heap,
  200. IN HEAP_ITERATOR_CALLBACK HeapCallback
  201. )
  202. /*++
  203. Routine Description:
  204. This routine is called to walk a heap. This means:
  205. - walking all segments
  206. - walking the virtual blocks
  207. - walking the lookaside
  208. Arguments:
  209. Heap - The heap being walked
  210. HeapCallback - a HEAP_ITERATOR_CALLBACK function passed down from the heap walk
  211. Return Value:
  212. TRUE if succeeds.
  213. --*/
  214. {
  215. ULONG SegmentCount;
  216. PLIST_ENTRY Head, Next;
  217. PHEAP_LOOKASIDE Lookaside = (PHEAP_LOOKASIDE)RtlpGetLookasideHeap(Heap);
  218. //
  219. // Flush the lookaside first
  220. //
  221. if (Lookaside != NULL) {
  222. ULONG i;
  223. PVOID Block;
  224. Heap->FrontEndHeap = NULL;
  225. Heap->FrontEndHeapType = 0;
  226. for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i += 1) {
  227. while ((Block = RtlpAllocateFromHeapLookaside(&(Lookaside[i]))) != NULL) {
  228. RtlFreeHeap( Heap, 0, Block );
  229. }
  230. }
  231. }
  232. //
  233. // Check whether we're required to walk this heap
  234. //
  235. if (!(*HeapCallback)( CONTEXT_START_HEAP,
  236. Heap,
  237. 0,
  238. 0,
  239. 0
  240. )) {
  241. return FALSE;
  242. }
  243. //
  244. // Start walking through the segments
  245. //
  246. for (SegmentCount = 0; SegmentCount < HEAP_MAXIMUM_SEGMENTS; SegmentCount++) {
  247. PHEAP_SEGMENT Segment = Heap->Segments[SegmentCount];
  248. if (Segment) {
  249. //
  250. // Call the appropriate routine to walk a valid segment
  251. //
  252. RtlpReadHeapSegment( Heap,
  253. SegmentCount,
  254. Segment,
  255. HeapCallback
  256. );
  257. }
  258. }
  259. //
  260. // Start walking the virtual block list
  261. //
  262. Head = &Heap->VirtualAllocdBlocks;
  263. Next = Head->Flink;
  264. while (Next != Head) {
  265. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock;
  266. VirtualAllocBlock = CONTAINING_RECORD(Next, HEAP_VIRTUAL_ALLOC_ENTRY, Entry);
  267. (*HeapCallback)( CONTEXT_VIRTUAL_BLOCK,
  268. Heap,
  269. 0,
  270. NULL,
  271. (ULONG_PTR)VirtualAllocBlock
  272. );
  273. Next = Next->Flink;
  274. }
  275. if (!(*HeapCallback)( CONTEXT_END_BLOCKS,
  276. Heap,
  277. 0,
  278. 0,
  279. 0
  280. )) {
  281. return FALSE;
  282. }
  283. return TRUE;
  284. }
  285. VOID
  286. RtlpReadProcessHeaps(
  287. IN HEAP_ITERATOR_CALLBACK HeapCallback
  288. )
  289. /*++
  290. Routine Description:
  291. This routine is called to walk the existing heaps in the current process
  292. Arguments:
  293. HeapCallback - a HEAP_ITERATOR_CALLBACK function passed down from the heap walk
  294. Return Value:
  295. TRUE if succeeds.
  296. --*/
  297. {
  298. ULONG i;
  299. PPEB ProcessPeb = NtCurrentPeb();
  300. if (!(*HeapCallback)( CONTEXT_START_GLOBALS,
  301. 0,
  302. 0,
  303. 0,
  304. (ULONG_PTR)ProcessPeb
  305. )) {
  306. return;
  307. }
  308. //
  309. // Walk the heaps from the process PEB
  310. //
  311. for (i = 0; i < ProcessPeb->NumberOfHeaps; i++) {
  312. RtlpReadHeapData ( (PHEAP)(ProcessPeb->ProcessHeaps[ i ]),
  313. HeapCallback
  314. );
  315. }
  316. }
  317. VOID
  318. RtlpInitializeMap (
  319. IN PMEMORY_MAP MemMap,
  320. IN PMEMORY_MAP Parent
  321. )
  322. /*++
  323. Routine Description:
  324. This routine initialize a memory map structure
  325. Arguments:
  326. MemMap - Map being initializated
  327. Parent - The upper level map
  328. Return Value:
  329. None
  330. --*/
  331. {
  332. //
  333. // Clear the memory map data
  334. //
  335. RtlZeroMemory(MemMap, sizeof(*MemMap));
  336. //
  337. // Save the upper level map
  338. //
  339. MemMap->Parent = Parent;
  340. //
  341. // Determine the granularity from the parent's granularity
  342. //
  343. if (Parent) {
  344. MemMap->Granularity = Parent->Granularity / 256;
  345. }
  346. }
  347. VOID
  348. RtlpSetBlockInfo (
  349. IN PMEMORY_MAP MemMap,
  350. IN ULONG_PTR Base,
  351. IN ULONG_PTR Size,
  352. IN PBLOCK_DESCR BlockDescr
  353. )
  354. /*++
  355. Routine Description:
  356. The routine will set a given block descriptor for a range
  357. in the memory map.
  358. Arguments:
  359. MemMap - The memory map
  360. Base - base address for the range to be set.
  361. Size - size in bytes of the zone
  362. BlockDescr - The pointer to the BLOCK_DESCR structure to be set
  363. Return Value:
  364. None
  365. --*/
  366. {
  367. ULONG_PTR Start, End;
  368. ULONG_PTR i;
  369. //
  370. // Check wheter we got a valid range
  371. //
  372. if (((Base + Size - 1) < MemMap->Offset) ||
  373. (Base > MemMap->MaxAddress)
  374. ) {
  375. return;
  376. }
  377. //
  378. // Determine the starting index to be set
  379. //
  380. if (Base > MemMap->Offset) {
  381. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  382. } else {
  383. Start = 0;
  384. }
  385. //
  386. // Determine the ending index to be set
  387. //
  388. End = (Base - MemMap->Offset + Size - 1) / MemMap->Granularity;
  389. if (End > 255) {
  390. End = 255;
  391. }
  392. for (i = Start; i <= End; i++) {
  393. //
  394. // Check whether this is the lowes memory map level
  395. //
  396. if (MemMap->Granularity == PAGE_SIZE) {
  397. //
  398. // This is the last level in the memory map, so we can apply
  399. // the block descriptor here
  400. //
  401. if (BlockDescr) {
  402. //
  403. // Check if we already have a block descriptor here
  404. //
  405. if (MemMap->Usage[i] != NULL) {
  406. if (MemMap->Usage[i] != BlockDescr) {
  407. DbgPrint("Error\n");
  408. }
  409. }
  410. //
  411. // Assign the given descriptor
  412. //
  413. MemMap->Usage[i] = BlockDescr;
  414. } else {
  415. //
  416. // We didn't recedive a block descriptor. We set
  417. // then the given flag
  418. //
  419. MemMap->FlagsBitmap[i / 8] |= 1 << (i % 8);
  420. }
  421. } else {
  422. //
  423. // This isn't the lowest map level. We recursively call
  424. // this function for the next detail range
  425. //
  426. if (!MemMap->Details[i]) {
  427. //
  428. // Allocate a new map
  429. //
  430. MemMap->Details[i] = RtlpLeakAllocateBlock( sizeof(*MemMap) );
  431. if (!MemMap->Details[i]) {
  432. DbgPrint("Error allocate\n");
  433. }
  434. //
  435. // Initialize the map and link it with the current one
  436. //
  437. RtlpInitializeMap(MemMap->Details[i], MemMap);
  438. MemMap->Details[i]->Offset = MemMap->Offset + MemMap->Granularity * i;
  439. MemMap->Details[i]->MaxAddress = MemMap->Offset + MemMap->Granularity * (i+1) - 1;
  440. }
  441. RtlpSetBlockInfo(MemMap->Details[i], Base, Size, BlockDescr);
  442. }
  443. }
  444. }
  445. PBLOCK_DESCR
  446. RtlpGetBlockInfo (
  447. IN PMEMORY_MAP MemMap,
  448. IN ULONG_PTR Base
  449. )
  450. /*++
  451. Routine Description:
  452. This function will return the appropriate Block descriptor
  453. for a given base address
  454. Arguments:
  455. MemMap - The memory map
  456. Base - The base address for the descriptor we are looking for
  457. Return Value:
  458. None
  459. --*/
  460. {
  461. ULONG_PTR Start;
  462. PBLOCK_DESCR BlockDescr = NULL;
  463. //
  464. // Validate the range
  465. //
  466. if ((Base < MemMap->Offset) ||
  467. (Base > MemMap->MaxAddress)
  468. ) {
  469. return NULL;
  470. }
  471. //
  472. // Determine the appropriate index for lookup
  473. //
  474. if (Base > MemMap->Offset) {
  475. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  476. } else {
  477. Start = 0;
  478. }
  479. //
  480. // If this is the lowest map level we'll return that entry
  481. //
  482. if (MemMap->Granularity == PAGE_SIZE) {
  483. return MemMap->Usage[ Start ];
  484. } else {
  485. //
  486. // We need a lower detail level call
  487. //
  488. if (MemMap->Details[ Start ]) {
  489. return RtlpGetBlockInfo( MemMap->Details[Start], Base );
  490. }
  491. }
  492. //
  493. // We didn't find something for this address, we'll return NULL then
  494. //
  495. return NULL;
  496. }
  497. BOOLEAN
  498. RtlpGetMemoryFlag (
  499. IN PMEMORY_MAP MemMap,
  500. IN ULONG_PTR Base
  501. )
  502. /*++
  503. Routine Description:
  504. This function returns the flag for a given base address
  505. Arguments:
  506. MemMap - The memory map
  507. Base - The base address we want to know the flag
  508. Return Value:
  509. None
  510. --*/
  511. {
  512. ULONG_PTR Start;
  513. PBLOCK_DESCR BlockDescr = NULL;
  514. //
  515. // Validate the base address
  516. //
  517. if ((Base < MemMap->Offset) ||
  518. (Base > MemMap->MaxAddress)
  519. ) {
  520. return FALSE;
  521. }
  522. //
  523. // Determine the appropriate index for the given base address
  524. //
  525. if (Base > MemMap->Offset) {
  526. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  527. } else {
  528. Start = 0;
  529. }
  530. if (MemMap->Granularity == PAGE_SIZE) {
  531. //
  532. // Return the bit value if are in the case of
  533. // the lowest detail level
  534. //
  535. return (MemMap->FlagsBitmap[Start / 8] & (1 << (Start % 8))) != 0;
  536. } else {
  537. //
  538. // Lookup in the detailed map
  539. //
  540. if (MemMap->Details[Start]) {
  541. return RtlpGetMemoryFlag(MemMap->Details[Start], Base);
  542. }
  543. }
  544. return FALSE;
  545. }
  546. VOID
  547. RtlpInitializeLeakDetection ()
  548. /*++
  549. Routine Description:
  550. This function initialize the leak detection structures
  551. Arguments:
  552. Return Value:
  553. None
  554. --*/
  555. {
  556. ULONG_PTR AddressRange = PAGE_SIZE;
  557. ULONG_PTR PreviousAddressRange = PAGE_SIZE;
  558. //
  559. // Initialize the global memory map
  560. //
  561. RtlpInitializeMap(&RtlpProcessMemoryMap, NULL);
  562. //
  563. // Initialize the lists
  564. //
  565. InitializeListHead( &RtlpBusyList );
  566. InitializeListHead( &RtlpLeakList );
  567. //
  568. // Determine the granularity for the highest memory map level
  569. //
  570. while (TRUE) {
  571. AddressRange = AddressRange * 256;
  572. if (AddressRange < PreviousAddressRange) {
  573. RtlpProcessMemoryMap.MaxAddress = MAXULONG_PTR;
  574. RtlpProcessMemoryMap.Granularity = PreviousAddressRange;
  575. break;
  576. }
  577. PreviousAddressRange = AddressRange;
  578. }
  579. RtlpTempBlocks = RtlpLeakAllocateBlock(PAGE_SIZE);
  580. }
  581. BOOLEAN
  582. RtlpPushPageDescriptor(
  583. IN ULONG_PTR Page,
  584. IN ULONG_PTR NumPages
  585. )
  586. /*++
  587. Routine Description:
  588. This routine binds the temporary block data into a block descriptor
  589. structure and push it to the memory map
  590. Arguments:
  591. Page - The start page that wil contain this data
  592. NumPages - The number of pages to be set
  593. Return Value:
  594. TRUE if succeeds.
  595. --*/
  596. {
  597. PBLOCK_DESCR PBlockDescr;
  598. PBLOCK_DESCR PreviousDescr;
  599. //
  600. // Check whether we already have a block descriptor there
  601. //
  602. PreviousDescr = RtlpGetBlockInfo( &RtlpProcessMemoryMap, Page * PAGE_SIZE );
  603. if (PreviousDescr) {
  604. DbgPrint("Conflicting descriptors %08lx\n", PreviousDescr);
  605. return FALSE;
  606. }
  607. //
  608. // We need to allocate a block descriptor structure and initializate it
  609. // with the acquired data.
  610. //
  611. PBlockDescr = (PBLOCK_DESCR)RtlpLeakAllocateBlock(sizeof(BLOCK_DESCR) + (RtlpLDNumBlocks - 1) * sizeof(HEAP_BLOCK));
  612. if (!PBlockDescr) {
  613. DbgPrint("Unable to allocate page descriptor\n");
  614. return FALSE;
  615. }
  616. PBlockDescr->Type = UsageHeap;
  617. PBlockDescr->Count = RtlpLDNumBlocks;
  618. PBlockDescr->Heap = RtlpCrtHeapAddress;
  619. //
  620. // Copy the temporary block buffer
  621. //
  622. RtlCopyMemory(PBlockDescr->Blocks, RtlpTempBlocks, RtlpLDNumBlocks * sizeof(HEAP_BLOCK));
  623. //
  624. // If this page doesn't bnelong to the temporary heap, we insert all these blocks
  625. // in the busy list
  626. //
  627. if (RtlpCrtHeapAddress != RtlpLeakHeapAddress) {
  628. LONG i;
  629. for (i = 0; i < RtlpLDNumBlocks; i++) {
  630. InitializeListHead( &PBlockDescr->Blocks[i].Entry );
  631. //
  632. // We might have a blockin more different pages. but We'll
  633. // insert only ones in the list
  634. //
  635. if (PBlockDescr->Blocks[i].BlockAddress != RtlpPreviousStartAddress) {
  636. InsertTailList(&RtlpLeakList, &PBlockDescr->Blocks[i].Entry);
  637. PBlockDescr->Blocks[i].Count = 0;
  638. //
  639. // Save the last block address
  640. //
  641. RtlpPreviousStartAddress = PBlockDescr->Blocks[i].BlockAddress;
  642. }
  643. }
  644. }
  645. //
  646. // Set the memory map with this block descriptor
  647. //
  648. RtlpSetBlockInfo(&RtlpProcessMemoryMap, Page * PAGE_SIZE, NumPages * PAGE_SIZE, PBlockDescr);
  649. return TRUE;
  650. }
  651. BOOLEAN
  652. RtlpRegisterHeapBlocks (
  653. IN ULONG Context,
  654. IN PHEAP Heap OPTIONAL,
  655. IN PHEAP_SEGMENT Segment OPTIONAL,
  656. IN PHEAP_ENTRY Entry OPTIONAL,
  657. IN ULONG_PTR Data OPTIONAL
  658. )
  659. /*++
  660. Routine Description:
  661. This is the callback routine invoked while parsing the
  662. process heaps. Depending on the context it is invoked
  663. it performs different tasks.
  664. Arguments:
  665. Context - The context this callback is being invoked
  666. Heap - The Heap structure
  667. Segment - The current Segment (if any)
  668. Entry - The current block entry (if any)
  669. Data - Additional data
  670. Return Value:
  671. TRUE if succeeds.
  672. --*/
  673. {
  674. //
  675. // Check whether we need to break at this address
  676. //
  677. if ((ULONG_PTR)Entry == RtlpBreakAtAddress) {
  678. DbgBreakPoint();
  679. }
  680. if (Context == CONTEXT_START_HEAP) {
  681. //
  682. // The only thing we need to do in this case
  683. // is to set the global current heap address
  684. //
  685. RtlpCrtHeapAddress = (ULONG_PTR)Heap;
  686. return TRUE;
  687. }
  688. //
  689. // For a new segment, we mark the flag for the whole
  690. // reserved space for the segment the flag to TRUE
  691. //
  692. if (Context == CONTEXT_START_SEGMENT) {
  693. RtlpSetBlockInfo(&RtlpProcessMemoryMap, (ULONG_PTR)Segment->BaseAddress, Segment->NumberOfPages * PAGE_SIZE, NULL);
  694. return TRUE;
  695. }
  696. if (Context == CONTEXT_ERROR) {
  697. DbgPrint("HEAP %p (Seg %p) At %p Error: %s\n",
  698. Heap,
  699. Segment,
  700. Entry,
  701. Data
  702. );
  703. return TRUE;
  704. }
  705. if (Context == CONTEXT_END_BLOCKS) {
  706. if (RtlpLDPreviousPage) {
  707. RtlpPushPageDescriptor(RtlpLDPreviousPage, 1);
  708. }
  709. RtlpLDPreviousPage = 0;
  710. RtlpLDNumBlocks = 0;
  711. } else if (Context == CONTEXT_BUSY_BLOCK) {
  712. ULONG_PTR EndPage;
  713. //
  714. // EnrtySize is assuming is the same as heap granularity
  715. //
  716. EndPage = (((ULONG_PTR)(Entry + Entry->Size)) - 1)/ PAGE_SIZE;
  717. //
  718. // Check whether we received a valid block
  719. //
  720. if ((Context == CONTEXT_BUSY_BLOCK) &&
  721. !RtlpGetMemoryFlag(&RtlpProcessMemoryMap, (ULONG_PTR)Entry)) {
  722. DbgPrint("%p address isn't from the heap\n", Entry);
  723. }
  724. //
  725. // Determine the starting page that contains the block
  726. //
  727. RtlpLDCrtPage = ((ULONG_PTR)Entry) / PAGE_SIZE;
  728. if (RtlpLDCrtPage != RtlpLDPreviousPage) {
  729. //
  730. // We moved to an other page, so we need to save the previous
  731. // information before going further
  732. //
  733. if (RtlpLDPreviousPage) {
  734. RtlpPushPageDescriptor(RtlpLDPreviousPage, 1);
  735. }
  736. //
  737. // Reset the temporary data. We're starting a new page now
  738. //
  739. RtlpLDPreviousPage = RtlpLDCrtPage;
  740. RtlpLDNumBlocks = 0;
  741. }
  742. //
  743. // Add this block to the current list
  744. //
  745. RtlpTempBlocks[RtlpLDNumBlocks].BlockAddress = (ULONG_PTR)Entry;
  746. RtlpTempBlocks[RtlpLDNumBlocks].Count = 0;
  747. RtlpTempBlocks[RtlpLDNumBlocks].Size = Entry->Size * sizeof(HEAP_ENTRY);
  748. RtlpLDNumBlocks++;
  749. if (EndPage != RtlpLDCrtPage) {
  750. //
  751. // The block ends on a different page. We can then save the
  752. // starting page and all others but the last one
  753. //
  754. RtlpPushPageDescriptor(RtlpLDCrtPage, 1);
  755. RtlpLDNumBlocks = 0;
  756. RtlpTempBlocks[RtlpLDNumBlocks].BlockAddress = (ULONG_PTR)Entry;
  757. RtlpTempBlocks[RtlpLDNumBlocks].Count = 0;
  758. RtlpTempBlocks[RtlpLDNumBlocks].Size = Entry->Size * sizeof(HEAP_ENTRY);
  759. RtlpLDNumBlocks += 1;
  760. if (EndPage - RtlpLDCrtPage > 1) {
  761. RtlpPushPageDescriptor(RtlpLDCrtPage + 1, EndPage - RtlpLDCrtPage - 1);
  762. }
  763. RtlpLDPreviousPage = EndPage;
  764. }
  765. } else if (Context == CONTEXT_VIRTUAL_BLOCK) {
  766. PHEAP_VIRTUAL_ALLOC_ENTRY VirtualAllocBlock = (PHEAP_VIRTUAL_ALLOC_ENTRY)Data;
  767. ULONG_PTR EndPage;
  768. //
  769. // EnrtySize is assuming is the same as heap granularity
  770. //
  771. EndPage = ((ULONG_PTR)Data + VirtualAllocBlock->CommitSize - 1)/ PAGE_SIZE;
  772. RtlpLDCrtPage = (Data) / PAGE_SIZE;
  773. if (RtlpLDCrtPage != RtlpLDPreviousPage) {
  774. //
  775. // Save the previous data if we're moving to a new page
  776. //
  777. if (RtlpLDPreviousPage) {
  778. RtlpPushPageDescriptor(RtlpLDPreviousPage, 1);
  779. }
  780. RtlpLDPreviousPage = RtlpLDCrtPage;
  781. RtlpLDNumBlocks = 0;
  782. }
  783. //
  784. // Initialize the block descriptor structure as we are
  785. // starting a new page
  786. //
  787. RtlpLDNumBlocks = 0;
  788. RtlpTempBlocks[RtlpLDNumBlocks].BlockAddress = (ULONG_PTR)Entry;
  789. RtlpTempBlocks[RtlpLDNumBlocks].Count = 0;
  790. RtlpTempBlocks[RtlpLDNumBlocks].Size = VirtualAllocBlock->CommitSize;
  791. RtlpLDNumBlocks += 1;
  792. RtlpPushPageDescriptor(RtlpLDCrtPage, EndPage - RtlpLDCrtPage + 1);
  793. RtlpLDPreviousPage = 0;
  794. } else if ( Context == CONTEXT_LOOKASIDE_BLOCK ) {
  795. PBLOCK_DESCR PBlockDescr;
  796. LONG i;
  797. //
  798. // Check whether we received a valid block
  799. //
  800. if (!RtlpGetMemoryFlag(&RtlpProcessMemoryMap, (ULONG_PTR)Entry)) {
  801. DbgPrint("%p address isn't from the heap\n", Entry);
  802. }
  803. PBlockDescr = RtlpGetBlockInfo( &RtlpProcessMemoryMap, (ULONG_PTR)Entry );
  804. if (!PBlockDescr) {
  805. DbgPrint("Error finding block from lookaside %p\n", Entry);
  806. return FALSE;
  807. }
  808. //
  809. // Find the block in the block descriptor
  810. //
  811. for (i = 0; i < PBlockDescr->Count; i++) {
  812. if ((PBlockDescr->Blocks[i].BlockAddress <= (ULONG_PTR)Entry) &&
  813. (PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > (ULONG_PTR)Entry)) {
  814. PBlockDescr->Blocks[i].Count = -10000;
  815. //
  816. // Remove the block from the busy list
  817. //
  818. RemoveEntryList(&PBlockDescr->Blocks[i].Entry);
  819. return TRUE;
  820. }
  821. }
  822. //
  823. // A block from lookaside should be busy for the heap structures.
  824. // If we didn't find the block in the block list, something went
  825. // wrong. We make some noise here.
  826. //
  827. DbgPrint("Error, block %p from lookaside not found in allocated block list\n", Entry);
  828. }
  829. return TRUE;
  830. }
  831. PHEAP_BLOCK
  832. RtlpGetHeapBlock (
  833. IN ULONG_PTR Address
  834. )
  835. /*++
  836. Routine Description:
  837. The function performs a lookup for the block descriptor
  838. for a given address. The address can point somewhere inside the
  839. block.
  840. Arguments:
  841. Address - The lookup address.
  842. Return Value:
  843. Returns a pointer to the heap descriptor structure if found.
  844. This is not NULL if the given address belongs to any busy heap block.
  845. --*/
  846. {
  847. PBLOCK_DESCR PBlockDescr;
  848. LONG i;
  849. //
  850. // Find the block descriptor for the given address
  851. //
  852. PBlockDescr = RtlpGetBlockInfo( &RtlpProcessMemoryMap, Address );
  853. if ( (PBlockDescr != NULL)
  854. &&
  855. (PBlockDescr->Heap != RtlpLeakHeapAddress)) {
  856. //
  857. // Search through the blocks
  858. //
  859. for (i = 0; i < PBlockDescr->Count; i++) {
  860. if ((PBlockDescr->Blocks[i].BlockAddress <= Address) &&
  861. (PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > Address)) {
  862. //
  863. // Search again if the caller didn't pass a start address
  864. //
  865. if (PBlockDescr->Blocks[i].BlockAddress != Address) {
  866. return RtlpGetHeapBlock(PBlockDescr->Blocks[i].BlockAddress);
  867. }
  868. //
  869. // we found a block here.
  870. //
  871. return &(PBlockDescr->Blocks[i]);
  872. }
  873. }
  874. }
  875. return NULL;
  876. }
  877. VOID
  878. RtlpDumpEntryHeader ( )
  879. /*++
  880. Routine Description:
  881. Writes the table header
  882. Arguments:
  883. Return Value:
  884. --*/
  885. {
  886. DbgPrint("Entry User Heap Size PrevSize Flags\n");
  887. DbgPrint("------------------------------------------------------------\n");
  888. }
  889. VOID
  890. RtlpDumpEntryFlagDescription(
  891. IN ULONG Flags
  892. )
  893. /*++
  894. Routine Description:
  895. The function writes a description string for the given block flag
  896. Arguments:
  897. Flags - Block flags
  898. Return Value:
  899. --*/
  900. {
  901. if (Flags & HEAP_ENTRY_BUSY) DbgPrint("busy "); else DbgPrint("free ");
  902. if (Flags & HEAP_ENTRY_EXTRA_PRESENT) DbgPrint("extra ");
  903. if (Flags & HEAP_ENTRY_FILL_PATTERN) DbgPrint("fill ");
  904. if (Flags & HEAP_ENTRY_VIRTUAL_ALLOC) DbgPrint("virtual ");
  905. if (Flags & HEAP_ENTRY_LAST_ENTRY) DbgPrint("last ");
  906. if (Flags & HEAP_ENTRY_SETTABLE_FLAGS) DbgPrint("user_flag ");
  907. }
  908. VOID
  909. RtlpDumpEntryInfo(
  910. IN ULONG_PTR HeapAddress,
  911. IN PHEAP_ENTRY Entry
  912. )
  913. /*++
  914. Routine Description:
  915. The function logs a heap block information
  916. Arguments:
  917. HeapAddress - The heap that contains the entry to be displayied
  918. Entry - The block entry
  919. Return Value:
  920. None.
  921. --*/
  922. {
  923. DbgPrint("%p %p %p %8lx %8lx ",
  924. Entry,
  925. (Entry + 1),
  926. HeapAddress,
  927. Entry->Size << HEAP_GRANULARITY_SHIFT,
  928. Entry->PreviousSize << HEAP_GRANULARITY_SHIFT
  929. );
  930. RtlpDumpEntryFlagDescription(Entry->Flags);
  931. DbgPrint("\n");
  932. }
  933. BOOLEAN
  934. RtlpScanHeapAllocBlocks ( )
  935. /*++
  936. Routine Description:
  937. The function does:
  938. - Scan all busy blocks and update the references to all other blocks
  939. - Build the list with leaked blocks
  940. - Reports the leaks
  941. Arguments:
  942. Return Value:
  943. Return TRUE if succeeds.
  944. --*/
  945. {
  946. PLIST_ENTRY Next;
  947. //
  948. // walk the busy list
  949. //
  950. Next = RtlpBusyList.Flink;
  951. while (Next != &RtlpBusyList) {
  952. PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
  953. PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + sizeof(HEAP_ENTRY));
  954. //
  955. // Move to the next block in the list
  956. //
  957. Next = Next->Flink;
  958. //
  959. // Iterate through block space and update
  960. // the references for every block found here
  961. //
  962. while ((ULONG_PTR)CrtAddress < Block->BlockAddress + Block->Size) {
  963. PHEAP_BLOCK pBlock = RtlpGetHeapBlock( *CrtAddress );
  964. if (pBlock) {
  965. //
  966. // We found a block. we increment then the reference count
  967. //
  968. if (pBlock->Count == 0) {
  969. RemoveEntryList(&pBlock->Entry);
  970. InsertTailList(&RtlpBusyList, &pBlock->Entry);
  971. }
  972. pBlock->Count += 1;
  973. if (pBlock->BlockAddress == RtlpBreakAtAddress) {
  974. DbgBreakPoint();
  975. }
  976. }
  977. //
  978. // Go to the next possible pointer
  979. //
  980. CrtAddress++;
  981. }
  982. }
  983. //
  984. // Now walk the leak list, and report leaks.
  985. // Also any pointer found here will be dereferenced and added to
  986. // the end of list.
  987. //
  988. Next = RtlpLeakList.Flink;
  989. while (Next != &RtlpLeakList) {
  990. PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
  991. PBLOCK_DESCR PBlockDescr = RtlpGetBlockInfo( &RtlpProcessMemoryMap, Block->BlockAddress );
  992. PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + sizeof(HEAP_ENTRY));
  993. if (PBlockDescr) {
  994. //
  995. // First time we need to display the header
  996. //
  997. if (RtlpLeaksCount == 0) {
  998. RtlpDumpEntryHeader();
  999. }
  1000. //
  1001. // Display the information for this block
  1002. //
  1003. RtlpDumpEntryInfo( PBlockDescr->Heap, (PHEAP_ENTRY)Block->BlockAddress);
  1004. RtlpLeaksCount += 1;
  1005. }
  1006. //
  1007. // Go to the next item from the leak list
  1008. //
  1009. Next = Next->Flink;
  1010. }
  1011. return TRUE;
  1012. }
  1013. BOOLEAN
  1014. RtlpScanProcessVirtualMemory()
  1015. /*++
  1016. Routine Description:
  1017. This function scan the whole process virtual address space and lookup
  1018. for possible references to busy blocks
  1019. Arguments:
  1020. Return Value:
  1021. Return TRUE if succeeds.
  1022. --*/
  1023. {
  1024. ULONG_PTR lpAddress = 0;
  1025. MEMORY_BASIC_INFORMATION Buffer;
  1026. NTSTATUS Status = STATUS_SUCCESS;
  1027. //
  1028. // Loop through virtual memory zones, we'll skip the heap space here
  1029. //
  1030. while ( NT_SUCCESS( Status ) ) {
  1031. Status = ZwQueryVirtualMemory( NtCurrentProcess(),
  1032. (PVOID)lpAddress,
  1033. MemoryBasicInformation,
  1034. &Buffer,
  1035. sizeof(Buffer),
  1036. NULL );
  1037. if (NT_SUCCESS( Status )) {
  1038. //
  1039. // If the page can be written, it might contain pointers to heap blocks
  1040. // We'll exclude at this point the heap address space. We scan the heaps
  1041. // later.
  1042. //
  1043. if ((Buffer.AllocationProtect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
  1044. &&
  1045. (Buffer.State & MEM_COMMIT)
  1046. &&
  1047. !RtlpGetMemoryFlag(&RtlpProcessMemoryMap, (ULONG_PTR)lpAddress)) {
  1048. PULONG_PTR Pointers = (PULONG_PTR)lpAddress;
  1049. ULONG_PTR i, Count;
  1050. //
  1051. // compute the number of possible pointers
  1052. //
  1053. Count = Buffer.RegionSize / sizeof(ULONG_PTR);
  1054. try {
  1055. //
  1056. // Loop through pages and check any possible pointer reference
  1057. //
  1058. for (i = 0; i < Count; i++) {
  1059. //
  1060. // Check whether we have a pointer to a busy heap block
  1061. //
  1062. PHEAP_BLOCK pBlock = RtlpGetHeapBlock(*Pointers);
  1063. if (pBlock) {
  1064. if (pBlock->BlockAddress == RtlpBreakAtAddress) {
  1065. DbgBreakPoint();
  1066. }
  1067. if (pBlock->Count == 0) {
  1068. RemoveEntryList(&pBlock->Entry);
  1069. InsertTailList(&RtlpBusyList, &pBlock->Entry);
  1070. }
  1071. pBlock->Count += 1;
  1072. }
  1073. //
  1074. // Move to the next pointer
  1075. //
  1076. Pointers++;
  1077. }
  1078. } except( EXCEPTION_EXECUTE_HANDLER ) {
  1079. //
  1080. // Nothing more to do
  1081. //
  1082. }
  1083. }
  1084. //
  1085. // Move to the next VM range to query
  1086. //
  1087. lpAddress += Buffer.RegionSize;
  1088. }
  1089. }
  1090. //
  1091. // Now update the references provided by the busy blocks
  1092. //
  1093. RtlpScanHeapAllocBlocks( );
  1094. return TRUE;
  1095. }
  1096. VOID
  1097. RtlDetectHeapLeaks ()
  1098. /*++
  1099. Routine Description:
  1100. This routine detects and display the leaks found in the current process
  1101. NOTE: The caller must make sure no other thread can change some heap data
  1102. while a tread is executing this one. In general this function is supposed
  1103. to be called from LdrShutdownProcess.
  1104. Arguments:
  1105. Return Value:
  1106. --*/
  1107. {
  1108. //
  1109. // Check if the global flag has the leak detection enabled
  1110. //
  1111. if (RtlpShutdownProcessFlags & (INSPECT_LEAKS | BREAK_ON_LEAKS)) {
  1112. RtlpLeaksCount = 0;
  1113. //
  1114. // Create a temporary heap that will be used for any alocation
  1115. // of these functions.
  1116. //
  1117. RtlpLeakHeap = RtlCreateHeap(HEAP_NO_SERIALIZE | HEAP_GROWABLE, NULL, 0, 0, NULL, NULL);
  1118. if (RtlpLeakHeap) {
  1119. PPEB ProcessPeb = NtCurrentPeb();
  1120. HeapDebugPrint( ("Inspecting leaks at process shutdown ...\n") );
  1121. RtlpInitializeLeakDetection();
  1122. //
  1123. // The last heap from the heap list is our temporary heap
  1124. //
  1125. RtlpLeakHeapAddress = (ULONG_PTR)ProcessPeb->ProcessHeaps[ ProcessPeb->NumberOfHeaps - 1 ];
  1126. //
  1127. // Scan all process heaps, build the memory map and
  1128. // the busy block list
  1129. //
  1130. RtlpReadProcessHeaps( RtlpRegisterHeapBlocks );
  1131. //
  1132. // Scan the process virtual memory and the busy blocks
  1133. // At the end build the list with leaked blocks and report them
  1134. //
  1135. RtlpScanProcessVirtualMemory();
  1136. //
  1137. // Destroy the temporary heap
  1138. //
  1139. RtlDestroyHeap(RtlpLeakHeap);
  1140. RtlpLeakHeap = NULL;
  1141. //
  1142. // Report the final statement about the process leaks
  1143. //
  1144. if (RtlpLeaksCount) {
  1145. HeapDebugPrint(("%ld leaks detected.\n", RtlpLeaksCount));
  1146. if (RtlpShutdownProcessFlags & BREAK_ON_LEAKS) {
  1147. DbgBreakPoint();
  1148. }
  1149. } else {
  1150. HeapDebugPrint( ("No leaks detected.\n"));
  1151. }
  1152. }
  1153. }
  1154. }