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.

1863 lines
42 KiB

  1. //
  2. // Copyright (c) 2001 Microsoft Corporation
  3. //
  4. // Module Name
  5. //
  6. // gc.c
  7. //
  8. // Abstract
  9. //
  10. // Implementation of APIs used for garbage collection in UMDH. These
  11. // APIs are declared in the header file gc.h
  12. //
  13. // Garbage Collection: The automatic reclamation of heap-allocated
  14. // storage after its last use by a program.
  15. //
  16. // Garbage collection is done in two steps-
  17. // 1. Identify the garbage
  18. // 2. Reclaim the garbage
  19. //
  20. // Since UMDH is not an in-process tool, only part 1 is accomplished
  21. // in this implementation.
  22. //
  23. // Garbage Collection Algorithm:
  24. //
  25. // It uses Mark-Sweep (not exactly) to identify live objects from
  26. // garbage.
  27. //
  28. // 1. Grovel through the entire process virtual memory and identify
  29. // the live objects by incrementing the reference counts of the
  30. // heap objects.
  31. //
  32. // 2. Create a list for those heap objects (garbage) whose reference
  33. // count is zero.
  34. //
  35. // 3. Identify the heap objects (not in garbage) referenced by these
  36. // objects from the garbage and decrement the count by one. If the
  37. // reference count drops to zero, add the heap object to the list
  38. // of objects in garbage.
  39. //
  40. // 4. Continue till all the objects in the garbage are traversed and
  41. // reference counts are incremented/decremented accordingly.
  42. //
  43. // 5. Dump the list of objects in garbage.
  44. //
  45. // To improve the number of leaks detected by this algorithm, reference
  46. // counts of the heap objects are not incremented, if the object
  47. // reference is from invalid stack regions (those regions of the stack
  48. // which are read/write but above the stack pointer) when grovelling
  49. // through the virtual memory in step one.
  50. //
  51. //
  52. // Authors
  53. //
  54. // Narayana Batchu (nbatchu) 21-Jun-01
  55. //
  56. // Revision History
  57. //
  58. // NBatchu 21-Jun-01 Initial version
  59. // NBatchu 24-Sep-01 Performance optimizations
  60. //
  61. //
  62. // Wish List
  63. //
  64. //
  65. // [-] Producing stack traces along with the leak table
  66. //
  67. // [-] Sorting the leaks by the stack traces (TraceIndex)
  68. //
  69. // [-] Adding the logic to detect circular reference counting. When blocks
  70. // are circularly referenced, then this algorithm would not be able to
  71. // detect leaks
  72. //
  73. // [-] Adding code for ia64, to filter out invalid stack regions. As of now
  74. // this is implemented for x86 machines only
  75. //
  76. //
  77. // Bugs
  78. //
  79. // [-] Partial copy error when reading process virtual memory - as of now
  80. // we are ignoring those errors
  81. //
  82. #include <ntos.h>
  83. #include <ntrtl.h>
  84. #include <nturtl.h>
  85. #include <heap.h>
  86. #include <heappriv.h>
  87. #include <windows.h>
  88. #include <stdio.h>
  89. #include <stdlib.h>
  90. #include <assert.h>
  91. #include <tlhelp32.h>
  92. #include "miscellaneous.h"
  93. #include "gc.h"
  94. #define BLOCK_CAPACITY 512
  95. #define HEAP_CAPACITY 8
  96. #define MAX_INDEX 0xffffffff
  97. #define MAX_THREADS 128
  98. #define MAX_HEAP_BLOCK_SIZE 4096
  99. #define MAX_VIRTUAL_BLOCK_SIZE (64*1024)
  100. //
  101. // Handle to the process
  102. //
  103. HANDLE g_hProcess;
  104. //
  105. // InitializeHeapBlock
  106. //
  107. // Initializes the HEAP_BLOCK structure
  108. //
  109. // Arguments
  110. //
  111. // Block Pointer to a HEAP_BLOCK to be initialized
  112. //
  113. // ReturnValue
  114. //
  115. VOID
  116. InitializeHeapBlock(
  117. PHEAP_BLOCK Block
  118. )
  119. {
  120. if (NULL == Block) {
  121. return;
  122. }
  123. Block->BlockAddress = 0;
  124. Block->BlockSize = 0;
  125. Block->RefCount = 0;
  126. Block->TraceIndex = 0;
  127. }
  128. //
  129. // SetHeapBlock
  130. //
  131. // Sets the fields of HEAP_BLOCK structure
  132. //
  133. // Arguments
  134. //
  135. // Block Pointer to a HEAP_BLOCK whose fields to be set
  136. //
  137. // ReturnValue
  138. //
  139. VOID
  140. SetHeapBlock(
  141. PHEAP_BLOCK Block,
  142. ULONG_PTR BlockAddress,
  143. ULONG BlockSize,
  144. USHORT TraceIndex
  145. )
  146. {
  147. if (NULL == Block) {
  148. return;
  149. }
  150. Block->BlockAddress = BlockAddress;
  151. Block->BlockSize = BlockSize;
  152. Block->RefCount = 0;
  153. Block->TraceIndex = TraceIndex;
  154. }
  155. //
  156. // InitializeBlockList
  157. //
  158. // Initializes the BLOCK_LIST structure
  159. //
  160. // Arguments
  161. //
  162. // BlockList Pointer to a BLOCK_LIST to be initialized
  163. //
  164. // ReturnValue
  165. //
  166. BOOL
  167. InitializeBlockList(
  168. PBLOCK_LIST BlockList
  169. )
  170. {
  171. BOOL fSuccess = TRUE;
  172. if (NULL == BlockList) {
  173. goto Exit;
  174. }
  175. BlockList->HeapAddress = 0;
  176. BlockList->BlockCount = 0;
  177. BlockList->Capacity = BLOCK_CAPACITY;
  178. BlockList->ListSorted = TRUE;
  179. BlockList->Blocks = (PHEAP_BLOCK)HeapAlloc(GetProcessHeap(),
  180. HEAP_ZERO_MEMORY,
  181. BlockList->Capacity * sizeof(HEAP_BLOCK));
  182. if (NULL == BlockList->Blocks) {
  183. BlockList->Capacity = 0;
  184. Error (__FILE__,
  185. __LINE__,
  186. "HeapAlloc failed while allocating more memory");
  187. fSuccess = FALSE;
  188. }
  189. Exit:
  190. return fSuccess;
  191. }
  192. //
  193. // FreeBlockList
  194. //
  195. // Frees the memory allocted for the Blocks field (if any)
  196. // while initializing this BLOCK_LIST structure.
  197. //
  198. // Arguments
  199. //
  200. // BlockList Pointer to a BLOCK_LIST
  201. //
  202. // ReturnValue
  203. //
  204. VOID
  205. FreeBlockList(
  206. PBLOCK_LIST BlockList
  207. )
  208. {
  209. if (NULL == BlockList) {
  210. return;
  211. }
  212. BlockList->HeapAddress = 0;
  213. BlockList->BlockCount = 0;
  214. BlockList->Capacity = 0;
  215. BlockList->ListSorted = TRUE;
  216. if (NULL != BlockList->Blocks) {
  217. HeapFree(GetProcessHeap(), 0, BlockList->Blocks);
  218. BlockList->Blocks = NULL;
  219. }
  220. }
  221. //
  222. // IntializeHeapList
  223. //
  224. // Initializes HEAP_LIST structure
  225. //
  226. // Arguments
  227. //
  228. // HeapList Pointer to a HEAP_LIST
  229. //
  230. // ReturnValue
  231. //
  232. BOOL
  233. InitializeHeapList(
  234. PHEAP_LIST HeapList
  235. )
  236. {
  237. ULONG Index;
  238. BOOL fSuccess = TRUE;
  239. if (NULL == HeapList) {
  240. goto Exit;
  241. }
  242. HeapList->Capacity = HEAP_CAPACITY;
  243. HeapList->HeapCount = 0;
  244. HeapList->Heaps =
  245. (PBLOCK_LIST)HeapAlloc(GetProcessHeap(),
  246. HEAP_ZERO_MEMORY,
  247. sizeof(BLOCK_LIST) * HeapList->Capacity);
  248. if (NULL == HeapList->Heaps) {
  249. HeapList->Capacity = 0;
  250. Error (__FILE__,
  251. __LINE__,
  252. "HeapAlloc failed while allocating more memory");
  253. fSuccess = FALSE;
  254. }
  255. Exit:
  256. return fSuccess;
  257. }
  258. //
  259. // FreeHeapList
  260. //
  261. // Frees the memory allocted for the Heaps field (if any)
  262. // while initializing this HEAP_LIST structure.
  263. //
  264. // Arguments
  265. //
  266. // BlockList Pointer to a HEAP_LIST
  267. //
  268. // ReturnValue
  269. //
  270. VOID
  271. FreeHeapList(
  272. PHEAP_LIST HeapList
  273. )
  274. {
  275. ULONG Index;
  276. if (NULL == HeapList) {
  277. return;
  278. }
  279. HeapList->Capacity = 0;
  280. HeapList->HeapCount = 0;
  281. if (NULL != HeapList->Heaps) {
  282. for (Index=0; Index<HeapList->HeapCount; Index++) {
  283. FreeBlockList(&HeapList->Heaps[Index]);
  284. }
  285. HeapFree(GetProcessHeap(), 0, HeapList->Heaps);
  286. HeapList->Heaps = NULL;
  287. }
  288. }
  289. //
  290. // InitializeAddressList
  291. //
  292. // Initializes a ADDRESS_LIST object
  293. //
  294. // Arguments
  295. //
  296. // AddressList Pointer to ADDRESS_LIST structure
  297. //
  298. // ReturnValue
  299. //
  300. VOID
  301. InitializeAddressList(
  302. PADDRESS_LIST AddressList
  303. )
  304. {
  305. if (NULL == AddressList) {
  306. return;
  307. }
  308. AddressList->Address = 0;
  309. InitializeListHead(&(AddressList->Next));
  310. }
  311. //
  312. // FreeAddressList
  313. //
  314. // Frees the memory allocated for the linked list
  315. //
  316. // Arguments
  317. //
  318. // AddressList Pointer to ADDRESS_LIST to be freed
  319. //
  320. // ReturnValue
  321. //
  322. VOID
  323. FreeAddressList(
  324. PADDRESS_LIST AddressList
  325. )
  326. {
  327. PLIST_ENTRY NextEntry;
  328. PLIST_ENTRY Entry;
  329. PADDRESS_LIST List;
  330. if (NULL == AddressList) {
  331. return;
  332. }
  333. //
  334. // Walk through the list and free up the memory
  335. //
  336. NextEntry = &AddressList->Next;
  337. while (!IsListEmpty(NextEntry)) {
  338. Entry = RemoveHeadList(NextEntry);
  339. List = CONTAINING_RECORD(Entry, ADDRESS_LIST, Next);
  340. HeapFree(GetProcessHeap(), 0, List);
  341. }
  342. }
  343. //
  344. // IncreaseBlockListCapacity
  345. //
  346. // Increases the storing capacity for the BLOCK_LIST
  347. // structure. Every time this function is called the storing
  348. // capacity doubles. There is a trade off between the number
  349. // of times HeapReAlloc is called and the amount of memory
  350. // is allocated.
  351. //
  352. // Arguments
  353. //
  354. // BlockList Pointer to a BLOCK_LIST object
  355. //
  356. // ReturnValue
  357. //
  358. // BOOL Returns TRUE if successful in increasing the
  359. // capacity of BlockList.
  360. //
  361. BOOL
  362. IncreaseBlockListCapacity(
  363. PBLOCK_LIST BlockList
  364. )
  365. {
  366. BOOL fSuccess = FALSE;
  367. ULONG NewCapacity;
  368. PVOID NewBlockList;
  369. if (NULL == BlockList) {
  370. goto Exit;
  371. }
  372. NewCapacity = BlockList->Capacity * 2;
  373. if (0 == NewCapacity) {
  374. fSuccess = InitializeBlockList(BlockList);
  375. goto Exit;
  376. }
  377. NewBlockList = HeapReAlloc(GetProcessHeap(),
  378. HEAP_ZERO_MEMORY,
  379. BlockList->Blocks,
  380. NewCapacity * sizeof(HEAP_BLOCK));
  381. if (NULL != NewBlockList) {
  382. BlockList->Blocks = (PHEAP_BLOCK)NewBlockList;
  383. BlockList->Capacity = NewCapacity;
  384. fSuccess = TRUE;
  385. }
  386. else {
  387. Error (__FILE__,
  388. __LINE__,
  389. "HeapReAlloc failed while allocating more memory");
  390. }
  391. Exit:
  392. return fSuccess;
  393. }
  394. //
  395. // IncreaseHeapListCapacity
  396. //
  397. // Increases the storing capacity for the HEAP_LIST
  398. // structure. Every time this function is called the storing
  399. // capacity doubles. There is a trade off between the number
  400. // of times HeapReAlloc is called and the amount of memory
  401. // is allocated.
  402. //
  403. // Arguments
  404. //
  405. // BlockList Pointer to a HEAP_LIST object
  406. //
  407. // ReturnValue
  408. //
  409. // BOOL Returns TRUE if successful in increasing the
  410. // capacity of HeapList.
  411. //
  412. BOOL
  413. IncreaseHeapListCapacity(
  414. PHEAP_LIST HeapList
  415. )
  416. {
  417. BOOL fSuccess = FALSE;
  418. ULONG NewCapacity;
  419. PVOID NewHeapList;
  420. if (NULL == HeapList) {
  421. goto Exit;
  422. }
  423. NewCapacity = HeapList->Capacity * 2;
  424. if (0 == NewCapacity) {
  425. fSuccess = InitializeHeapList(HeapList);
  426. goto Exit;
  427. }
  428. NewHeapList = HeapReAlloc(GetProcessHeap(),
  429. HEAP_ZERO_MEMORY,
  430. HeapList->Heaps,
  431. NewCapacity * sizeof(BLOCK_LIST));
  432. if (NULL != NewHeapList) {
  433. HeapList->Heaps = (PBLOCK_LIST)NewHeapList;
  434. HeapList->Capacity = NewCapacity;
  435. fSuccess = TRUE;
  436. }
  437. else {
  438. Error(__FILE__,
  439. __LINE__,
  440. "HeapReAlloc failed while allocating more memory");
  441. }
  442. Exit:
  443. return fSuccess;
  444. }
  445. //
  446. // InsertHeapBlock
  447. //
  448. // Inserts HEAP_BLOCK object into BLOCK_LIST. BLOCK_LIST is
  449. // an array of HEAP_BLOCKs belonging to a particular heap.
  450. //
  451. // Arguments
  452. //
  453. // BlockList Pointer to BLOCK_LIST. HEAP_BLOCK is inserted
  454. // into this list.
  455. //
  456. // Block Pointer to HEAP_BLOCK to be inserted in.
  457. //
  458. // ReturnValue
  459. //
  460. // ULONG Returns the Index at which HEAP_BLOCK is inserted
  461. // in BLOCK_LIST
  462. //
  463. ULONG
  464. InsertHeapBlock(
  465. PBLOCK_LIST BlockList,
  466. PHEAP_BLOCK Block
  467. )
  468. {
  469. ULONG Index = MAX_INDEX;
  470. BOOL Result;
  471. if (NULL == BlockList || NULL == Block) {
  472. goto Exit;
  473. }
  474. Index = BlockList->BlockCount;
  475. if (Index >= BlockList->Capacity) {
  476. //
  477. // Try to increase block list capacity.
  478. //
  479. if (!IncreaseBlockListCapacity(BlockList)) {
  480. goto Exit;
  481. }
  482. }
  483. BlockList->Blocks[Index].BlockAddress = Block->BlockAddress;
  484. BlockList->Blocks[Index].BlockSize = Block->BlockSize;
  485. BlockList->Blocks[Index].RefCount = Block->RefCount;
  486. BlockList->Blocks[Index].TraceIndex = Block->TraceIndex;
  487. BlockList->BlockCount += 1;
  488. BlockList->ListSorted = FALSE;
  489. Exit:
  490. return Index;
  491. }
  492. //
  493. // InsertBlockList
  494. //
  495. // Inserts BLOCK_LIST object into HEAP_LIST. HEAP_LIST is
  496. // an array of BLOCK_LISTs belonging to a particular process.
  497. // And BLOCK_LIST is an array of HEAP_BLOCKs belonging to a
  498. // particular heap.
  499. //
  500. // Arguments
  501. //
  502. // BlockList Pointer to BLOCK_LIST. HEAP_BLOCK is inserted
  503. // into this list.
  504. //
  505. // Block Pointer to HEAP_BLOCK to be inserted in.
  506. //
  507. // ReturnValue
  508. //
  509. // ULONG Returns the Index at which HEAP_BLOCK is inserted
  510. // in BLOCK_LIST
  511. //
  512. ULONG
  513. InsertBlockList(
  514. PHEAP_LIST HeapList,
  515. PBLOCK_LIST BlockList
  516. )
  517. {
  518. ULONG I, Index = MAX_INDEX;
  519. PBLOCK_LIST NewBlockList;
  520. if (NULL == HeapList || NULL == BlockList) {
  521. goto Exit;
  522. }
  523. if (0 == BlockList->BlockCount) {
  524. goto Exit;
  525. }
  526. Index = HeapList->HeapCount;
  527. if (Index >= HeapList->Capacity) {
  528. //
  529. // Increase the heap list capacity since we hit the limit.
  530. //
  531. if (!IncreaseHeapListCapacity(HeapList)) {
  532. goto Exit;
  533. }
  534. }
  535. HeapList->Heaps[Index].Blocks = BlockList->Blocks;
  536. NewBlockList = &HeapList->Heaps[Index];
  537. //
  538. // Copy the values stored in BlockList to NewBlockList.
  539. //
  540. NewBlockList->BlockCount = BlockList->BlockCount;
  541. NewBlockList->Capacity = BlockList->Capacity;
  542. NewBlockList->HeapAddress = BlockList->HeapAddress;
  543. NewBlockList->ListSorted = BlockList->ListSorted;
  544. //
  545. // Increment the HeapCount
  546. //
  547. HeapList->HeapCount += 1;
  548. Exit:
  549. return Index;
  550. }
  551. //
  552. // GetThreadHandles
  553. //
  554. // Enumerates all the threads in the system and filters only the
  555. // threads in the process we are concerned
  556. //
  557. // Arguments
  558. //
  559. // ProcessId Process ID
  560. //
  561. // ThreadHandles Array of Handles, which receive the handles to
  562. // the enumerated threads
  563. //
  564. // Count Array count
  565. //
  566. // ReturnValue
  567. //
  568. // DWORD Returns the number of thread handles opened
  569. //
  570. DWORD
  571. GetThreadHandles(
  572. DWORD ProcessId,
  573. LPHANDLE ThreadHandles,
  574. ULONG Count
  575. )
  576. {
  577. HANDLE ThreadSnap = NULL;
  578. BOOL Result = FALSE;
  579. THREADENTRY32 ThreadEntry = {0};
  580. ULONG I, Index = 0;
  581. // SilviuC: These APIs xxxtoolhelpxxx are crappy. Keep them for now but
  582. // you should get yourself familiarized with NT apis that do the same. For
  583. // instance take a look in sdktools\systrack where there is code that gets
  584. // stack information for each thread.
  585. //
  586. // Take a snapshot of all the threads in the system
  587. //
  588. ThreadSnap = CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0);
  589. if (NULL == ThreadSnap) {
  590. Error (__FILE__,
  591. __LINE__,
  592. "CreateToolhelp32Snapshot failed with error : %ld\n",
  593. GetLastError());
  594. goto Exit;
  595. }
  596. //
  597. // Fill in the size for ThreadEntry before using it
  598. //
  599. ThreadEntry.dwSize = sizeof(THREADENTRY32);
  600. //
  601. // Walk through snap shot of threads and look for the threads
  602. // whose process ids match the process id of the process we
  603. // are looking for.
  604. //
  605. Result = Thread32First(ThreadSnap, &ThreadEntry);
  606. while (Result) {
  607. if (ThreadEntry.th32OwnerProcessID == ProcessId) {
  608. HANDLE ThreadHandle = OpenThread(THREAD_GET_CONTEXT,
  609. FALSE,
  610. ThreadEntry.th32ThreadID);
  611. if (NULL == ThreadHandle) {
  612. Error (__FILE__,
  613. __LINE__,
  614. "OpenThread failed with error : %ld\n",
  615. GetLastError());
  616. }
  617. else {
  618. if (NULL != ThreadHandles && Index < Count) {
  619. ThreadHandles[Index] = ThreadHandle;
  620. }
  621. Index += 1;
  622. }
  623. }
  624. Result = Thread32Next(ThreadSnap, &ThreadEntry);
  625. }
  626. Exit:
  627. //
  628. // Clean up the snapshot object
  629. //
  630. if (NULL != ThreadSnap) {
  631. CloseHandle (ThreadSnap);
  632. }
  633. return Index;
  634. }
  635. //
  636. // GetThreadContexts
  637. //
  638. // Gets the thread contexts of all the threads in the process
  639. //
  640. // Arguments
  641. //
  642. // ThreadContexts Array of CONTEXT structures to store thread
  643. // stack/context information
  644. //
  645. // ThreadHandles Array of thread handles
  646. //
  647. // Count Array count
  648. //
  649. // ReturnValue
  650. //
  651. // BOOL Returns true if successful
  652. //
  653. BOOL
  654. GetThreadContexts(
  655. PCONTEXT ThreadContexts,
  656. LPHANDLE ThreadHandles,
  657. ULONG Count
  658. )
  659. {
  660. ULONG Index;
  661. BOOL Result;
  662. for (Index = 0; Index < Count; Index += 1) {
  663. ZeroMemory(&ThreadContexts[Index], sizeof(CONTEXT));
  664. ThreadContexts[Index].ContextFlags =
  665. CONTEXT_INTEGER | CONTEXT_CONTROL;
  666. Result = GetThreadContext (ThreadHandles[Index],
  667. &ThreadContexts[Index]);
  668. if (FALSE == Result) {
  669. Error (__FILE__,
  670. __LINE__,
  671. "GetThreadContext Failed with error : %ld\n",
  672. GetLastError());
  673. }
  674. }
  675. return TRUE;
  676. }
  677. //
  678. // StackFilteredAddress
  679. //
  680. // Each thread in the process has its own stack and each stack
  681. // has a read/write region that is not valid (this region is
  682. // above the stack pointer). This function filters out this
  683. // region by incrementing the start address of the block to
  684. // the end of stack pointer, so that we dont search those
  685. // regions of the stack which dont contain valid data.
  686. //
  687. // As af now, this function is implemented for X86 machines only.
  688. // For IA64 machines, the register names (in the CONTEXT structure)
  689. // are different than X86 machines and different header files need
  690. // to be added to make it compile and work.
  691. //
  692. // Arguments
  693. //
  694. // Address Address to the block
  695. //
  696. // Size Size of the block pointed to 'Address'
  697. //
  698. // ThreadContexts Array to CONTEXTs of all the threads in
  699. // the process
  700. //
  701. // Count Array count
  702. //
  703. // ReturnValue
  704. //
  705. // ULONG_PTR Returns new address to the end of the
  706. // valid stack region
  707. //
  708. ULONG_PTR
  709. StackFilteredAddress(
  710. ULONG_PTR Address,
  711. SIZE_T Size,
  712. PCONTEXT ThreadContexts,
  713. ULONG Count
  714. )
  715. {
  716. ULONG Index;
  717. ULONG_PTR FilteredAddress = Address;
  718. //
  719. // SilviuC: It is easy to get the same kind of stuff for IA64. If I am not
  720. // mistaken the field is called Sp.
  721. //
  722. #ifdef X86
  723. for (Index = 0; Index < Count; Index += 1) {
  724. if (ThreadContexts[Index].Esp >= Address &&
  725. ThreadContexts[Index].Esp <= Address + Size) {
  726. FilteredAddress = ThreadContexts[Index].Esp;
  727. break;
  728. }
  729. }
  730. #endif
  731. return FilteredAddress;
  732. }
  733. //
  734. // SortByBlockAddress
  735. //
  736. // Sorts HEAP_BLOCKs belonging to a particular BLOCK_LIST
  737. // by comparing the BlockAddresses.
  738. //
  739. // Compare function required by qsort (uses quick sort to sort
  740. // the elements in the array).
  741. //
  742. // More info about the arguments and the return values could be
  743. // found in MSDN.
  744. //
  745. int __cdecl
  746. SortByBlockAddress (
  747. const PHEAP_BLOCK Block1,
  748. const PHEAP_BLOCK Block2
  749. )
  750. {
  751. int iCompare;
  752. if (Block1->BlockAddress > Block2->BlockAddress) {
  753. iCompare = +1;
  754. }
  755. else if (Block1->BlockAddress < Block2->BlockAddress) {
  756. iCompare = -1;
  757. }
  758. else {
  759. iCompare = 0;
  760. }
  761. return iCompare;
  762. }
  763. int __cdecl
  764. SortByTraceIndex (
  765. const PHEAP_BLOCK Block1,
  766. const PHEAP_BLOCK Block2
  767. )
  768. {
  769. int iCompare;
  770. //
  771. // Sort such that items with identical TraceIndex are adjacent.
  772. // (That this results in ascending order is irrelevant).
  773. //
  774. if (Block1->TraceIndex > Block2->TraceIndex) {
  775. iCompare = +1;
  776. }
  777. else if (Block1->TraceIndex < Block2->TraceIndex) {
  778. iCompare = -1;
  779. }
  780. else {
  781. iCompare = 0;
  782. }
  783. if (0 == iCompare) {
  784. //
  785. // For two items with identical TraceIndex, sort into ascending
  786. // order by BytesAllocated.
  787. //
  788. if (Block1->BlockSize > Block2->BlockSize) {
  789. iCompare = 1;
  790. }
  791. else if (Block1->BlockSize < Block2->BlockSize) {
  792. iCompare = -1;
  793. }
  794. else {
  795. iCompare = 0;
  796. }
  797. }
  798. return iCompare;
  799. }
  800. //
  801. // SortHeaps
  802. //
  803. // Sorts all the heaps in the HEAP_LIST.
  804. // Each heap is sorted by increasing value of HEAP_BLOCK
  805. // addresses. The top most entry for each heap would be
  806. // having the min address value
  807. //
  808. // Arguments
  809. //
  810. // HeapList Pointer to HEAP_LIST
  811. //
  812. // Return Value
  813. //
  814. VOID
  815. SortHeaps(
  816. PHEAP_LIST HeapList,
  817. int (__cdecl *compare )(const void *elem1, const void *elem2 )
  818. )
  819. {
  820. ULONG HeapCount;
  821. ULONG Index;
  822. if (NULL == HeapList) {
  823. return;
  824. }
  825. HeapCount = HeapList->HeapCount;
  826. for (Index = 0; Index < HeapCount; Index += 1) {
  827. //
  828. // Sort the BLOCK_LIST only if it contains heap objects
  829. //
  830. if (0 != HeapList->Heaps[Index].BlockCount) {
  831. qsort (HeapList->Heaps[Index].Blocks,
  832. HeapList->Heaps[Index].BlockCount,
  833. sizeof(HEAP_BLOCK),
  834. compare);
  835. }
  836. HeapList->Heaps[Index].ListSorted = TRUE;
  837. }
  838. }
  839. //
  840. // GetHeapBlock
  841. //
  842. // Finds a HEAP_BLOCK whose range contains the the address
  843. // pointed to by Address
  844. //
  845. // Arguments
  846. //
  847. // Address Address as ULONG_PTR
  848. //
  849. // HeapList Pointer to HEAP_LIST to be searched.
  850. //
  851. // ReturnValue
  852. //
  853. // PHEAP_BLOCK Returns the pointer to the HEAP_BLOCK that
  854. // contains the address.
  855. //
  856. PHEAP_BLOCK
  857. GetHeapBlock (
  858. ULONG_PTR Address,
  859. PHEAP_LIST HeapList
  860. )
  861. {
  862. PHEAP_BLOCK Block = NULL;
  863. ULONG I,J;
  864. ULONG Start, Mid, End;
  865. PBLOCK_LIST BlockList;
  866. //
  867. // Since most of the memory is null (zero), this check would
  868. // improve the performance
  869. //
  870. if (0 == Address ||
  871. NULL == HeapList ||
  872. 0 == HeapList->HeapCount) {
  873. goto Exit;
  874. }
  875. for (I = 0; I < HeapList->HeapCount; I += 1) {
  876. //
  877. // Ignore if the heap contains no objects
  878. //
  879. if (0 == HeapList->Heaps[I].BlockCount) {
  880. continue;
  881. }
  882. //
  883. // Binary search the address in the sorted list of heap blocks for
  884. // the current heap.
  885. //
  886. Start = 0;
  887. End = HeapList->Heaps[I].BlockCount - 1;
  888. BlockList = &HeapList->Heaps[I];
  889. while (Start <= End) {
  890. Mid = (Start + End)/2;
  891. if (Address < BlockList->Blocks[Mid].BlockAddress) {
  892. End = Mid - 1;
  893. }
  894. else if (Address >= BlockList->Blocks[Mid].BlockAddress +
  895. BlockList->Blocks[Mid].BlockSize) {
  896. Start = Mid + 1;
  897. }
  898. else {
  899. Block = &BlockList->Blocks[Mid];
  900. break;
  901. }
  902. if (Mid == Start || Mid == End) {
  903. break;
  904. }
  905. }
  906. if (NULL != Block) {
  907. break;
  908. }
  909. }
  910. Exit:
  911. return Block;
  912. }
  913. //
  914. // InsertAddress
  915. //
  916. // Inserts a node in the linked list. The new node has the
  917. // Address stored. This node is inserted at the end of the
  918. // linked list.
  919. //
  920. // Arguments
  921. //
  922. // Address Address of a block in the heap
  923. //
  924. // List Pointer to a ADDRESS_LIST
  925. //
  926. // ReturnValue
  927. //
  928. VOID
  929. InsertAddress(
  930. ULONG_PTR Address,
  931. PADDRESS_LIST List
  932. )
  933. {
  934. PADDRESS_LIST NewList;
  935. NewList = (PADDRESS_LIST) HeapAlloc(GetProcessHeap(),
  936. HEAP_ZERO_MEMORY,
  937. sizeof (ADDRESS_LIST));
  938. if (NULL == NewList) {
  939. Error (__FILE__,
  940. __LINE__,
  941. "HeapAlloc failed to allocate memory");
  942. return;
  943. }
  944. NewList->Address = Address;
  945. InsertTailList(&(List->Next), &(NewList->Next));
  946. }
  947. //
  948. // DumpLeakList
  949. //
  950. // Dumps the leak list to a file or console. Parses through
  951. // each of the HEAP_BLOCK and dumps those blocks whose RefCount
  952. // is 0 (zero).
  953. //
  954. // Arguments
  955. //
  956. // File Output file
  957. //
  958. // HeapList Pointer to a HEAP_LIST
  959. //
  960. // ReturnValue
  961. //
  962. VOID
  963. DumpLeakList(
  964. FILE * File,
  965. PHEAP_LIST HeapList
  966. )
  967. {
  968. ULONG I,J;
  969. ULONG Count = 1;
  970. USHORT RefTraceIndex = 0;
  971. ULONG TotalBytes = 0;
  972. PHEAP_BLOCK HeapBlock;
  973. SortHeaps(HeapList, SortByTraceIndex);
  974. //
  975. // Now walk the heap list, and report leaks.
  976. //
  977. fprintf(
  978. File,
  979. "\n\n*- - - - - - - - - - Leaks detected - - - - - - - - - -\n\n"
  980. );
  981. for (I = 0; I < HeapList->HeapCount; I += 1) {
  982. for (J = 0; J < HeapList->Heaps[I].BlockCount; J += 1) {
  983. HeapBlock = &(HeapList->Heaps[I].Blocks[J]);
  984. //
  985. // Merge the leaks whose trace index is same (i.e. whose
  986. // allocation stack trace is same)
  987. //
  988. if (RefTraceIndex == HeapBlock->TraceIndex && 0 == HeapBlock->RefCount) {
  989. Count += 1;
  990. TotalBytes += HeapBlock->BlockSize;
  991. }
  992. //
  993. // Display them if
  994. // 1. They are from different stack traces and there are leaks
  995. // OR
  996. // 2. This is the last Block in the list and there are leaks.
  997. //
  998. if ((RefTraceIndex != HeapBlock->TraceIndex) ||
  999. ((I+1) == HeapList->HeapCount && (J+1) == HeapList->Heaps[I].BlockCount)) {
  1000. if (0 != RefTraceIndex && 0 != TotalBytes) {
  1001. fprintf(
  1002. File,
  1003. "0x%x bytes leaked by: BackTrace%05d (in 0x%04x allocations)\n",
  1004. TotalBytes,
  1005. RefTraceIndex,
  1006. Count
  1007. );
  1008. }
  1009. //
  1010. // Update trace index, count and total bytes
  1011. //
  1012. RefTraceIndex = HeapBlock->TraceIndex;
  1013. Count = (0 == HeapBlock->RefCount) ? 1 : 0;
  1014. TotalBytes = (0 == HeapBlock->RefCount) ? HeapList->Heaps[I].Blocks[J].BlockSize : 0;
  1015. }
  1016. }
  1017. }
  1018. fprintf(
  1019. File,
  1020. "\n*- - - - - - - - - - End of Leaks - - - - - - - - - -\n\n"
  1021. );
  1022. return;
  1023. }
  1024. //
  1025. // ScanHeapFreeBlocks
  1026. //
  1027. // Scans the free list and updates the references to any of the
  1028. // busy blocks. When the refernce count of the busy blocks drops
  1029. // to zero, it is appended to the end of the free list
  1030. //
  1031. // Arguments
  1032. //
  1033. // HeapList Pointer to HEAP_LIST
  1034. //
  1035. // FreeList Pointer to ADDRESS_LIST that contains addresses to
  1036. // free heap blocks
  1037. //
  1038. // ReturnValue
  1039. //
  1040. // BOOL Returns true if successful
  1041. //
  1042. BOOL
  1043. ScanHeapFreeBlocks(
  1044. PHEAP_LIST HeapList,
  1045. PADDRESS_LIST FreeList
  1046. )
  1047. {
  1048. BOOL Result;
  1049. ULONG Count, i;
  1050. PULONG_PTR Pointer;
  1051. ULONG_PTR FinalAddress;
  1052. PHEAP_BLOCK CurrentBlock;
  1053. PVOID HeapBlock;
  1054. ULONG HeapBlockSize = 0;
  1055. BOOL Success = TRUE;
  1056. PLIST_ENTRY FirstEntry;
  1057. PLIST_ENTRY NextEntry;
  1058. PADDRESS_LIST AddressList;
  1059. //
  1060. // Allocate a chunk of memory for reading heap objects
  1061. //
  1062. HeapBlock = (PVOID) HeapAlloc(GetProcessHeap(),
  1063. HEAP_ZERO_MEMORY,
  1064. MAX_HEAP_BLOCK_SIZE);
  1065. if (NULL == HeapBlock) {
  1066. Error (__FILE__,
  1067. __LINE__,
  1068. "HeapAlloc failed to allocate memory");
  1069. Success = FALSE;
  1070. goto Exit;
  1071. }
  1072. HeapBlockSize = MAX_HEAP_BLOCK_SIZE;
  1073. //
  1074. // Walk the free list by deleting the entries read
  1075. //
  1076. FirstEntry = &(FreeList->Next);
  1077. while (!IsListEmpty(FirstEntry)) {
  1078. NextEntry = RemoveHeadList(FirstEntry);
  1079. AddressList = CONTAINING_RECORD(NextEntry,
  1080. ADDRESS_LIST,
  1081. Next);
  1082. CurrentBlock = GetHeapBlock(AddressList->Address,
  1083. HeapList);
  1084. assert(NULL != CurrentBlock);
  1085. if (NULL == CurrentBlock) {
  1086. Error (__FILE__,
  1087. __LINE__,
  1088. "GetHeapBlock returned NULL. May be because of reading stale memory");
  1089. continue;
  1090. }
  1091. if (HeapBlockSize < CurrentBlock->BlockSize) {
  1092. if (NULL != HeapBlock) {
  1093. HeapFree(GetProcessHeap(), 0, HeapBlock);
  1094. }
  1095. HeapBlock = (PVOID) HeapAlloc(GetProcessHeap(),
  1096. HEAP_ZERO_MEMORY,
  1097. CurrentBlock->BlockSize);
  1098. if (NULL == HeapBlock) {
  1099. Error (__FILE__,
  1100. __LINE__,
  1101. "HeapAlloc failed to allocate memory");
  1102. Success = FALSE;
  1103. goto Exit;
  1104. }
  1105. HeapBlockSize = CurrentBlock->BlockSize;
  1106. }
  1107. //
  1108. // Read the contents of the freed heap block
  1109. // from the target process.
  1110. //
  1111. Result = UmdhReadAtVa(__FILE__,
  1112. __LINE__,
  1113. g_hProcess,
  1114. (PVOID)CurrentBlock->BlockAddress,
  1115. HeapBlock,
  1116. CurrentBlock->BlockSize);
  1117. if (Result) {
  1118. FinalAddress = (ULONG_PTR)HeapBlock+CurrentBlock->BlockSize;
  1119. Pointer = (PULONG_PTR) HeapBlock;
  1120. while ((ULONG_PTR)Pointer < FinalAddress) {
  1121. //
  1122. // Check whether we have a pointer to a
  1123. // busy heap block
  1124. //
  1125. PHEAP_BLOCK Block = GetHeapBlock(*Pointer,HeapList);
  1126. if (NULL != Block) {
  1127. //
  1128. // We found a block. we decrement the reference
  1129. // count
  1130. //
  1131. if (0 == Block->RefCount) {
  1132. //
  1133. // This should never happen!!
  1134. //
  1135. Error (__FILE__,
  1136. __LINE__,
  1137. "Something wrong! Should not get a block whose "
  1138. "RefCount is already 0 @ %p",
  1139. Block->BlockAddress);
  1140. }
  1141. else if (1 == Block->RefCount) {
  1142. //
  1143. // Queue the newly found free block at the end of
  1144. // the list of freed heap blocks. The block has become
  1145. // eligible for this because `HeapBlock' contained the
  1146. // last remaining reference to `Block'.
  1147. //
  1148. InsertAddress(Block->BlockAddress, FreeList);
  1149. Block->RefCount = 0;
  1150. }
  1151. else {
  1152. Block->RefCount -= 1;
  1153. }
  1154. }
  1155. //
  1156. // Move to the next pointer
  1157. //
  1158. Pointer += 1;
  1159. }
  1160. }
  1161. }
  1162. Exit:
  1163. //
  1164. // Free the memory allocated at HeapBlock
  1165. //
  1166. if (NULL != HeapBlock) {
  1167. HeapFree (GetProcessHeap(), 0, HeapBlock);
  1168. HeapBlock = NULL;
  1169. }
  1170. return Success;
  1171. }
  1172. //
  1173. // ScanProcessVirtualMemory
  1174. //
  1175. // Scans the virtual memory and updates the RefCount of the heap
  1176. // blocks. This also takes care excluding the invalid stack
  1177. // regions that might contain valid pointers.
  1178. //
  1179. // Arguments
  1180. //
  1181. // Pid Process ID
  1182. //
  1183. // FreeList Pointer to a ADDRESS_LIST that holds the address of
  1184. // all free heap blocks
  1185. //
  1186. // HeapList Pointer to HEAP_LIST
  1187. //
  1188. // ReturnValue
  1189. //
  1190. // BOOL Returns true if successful in scanning through the
  1191. // virtual memory
  1192. BOOL
  1193. ScanProcessVirtualMemory(
  1194. ULONG Pid,
  1195. PADDRESS_LIST FreeList,
  1196. PHEAP_LIST HeapList
  1197. )
  1198. {
  1199. ULONG_PTR Address = 0;
  1200. MEMORY_BASIC_INFORMATION Buffer;
  1201. PVOID VirtualBlock;
  1202. ULONG VirtualBlockSize;
  1203. SYSTEM_INFO SystemInfo;
  1204. LPVOID MinAddress;
  1205. LPVOID MaxAddress;
  1206. LPHANDLE ThreadHandles;
  1207. PCONTEXT ThreadContexts;
  1208. ULONG ThreadCount;
  1209. ULONG Index;
  1210. SIZE_T dwBytesRead = 1;
  1211. BOOL Success = TRUE;
  1212. //
  1213. // Enumerate all the threads in the process and get their
  1214. // stack information
  1215. //
  1216. //
  1217. // Get the count of threads in the process
  1218. //
  1219. ThreadCount = GetThreadHandles (Pid, NULL, 0);
  1220. //
  1221. // Allocate memory for ThreadHandles
  1222. //
  1223. ThreadHandles = (LPHANDLE)HeapAlloc(GetProcessHeap(),
  1224. HEAP_ZERO_MEMORY,
  1225. ThreadCount * sizeof(HANDLE));
  1226. if (NULL == ThreadHandles) {
  1227. Error (__FILE__,
  1228. __LINE__,
  1229. "HeapAlloc failed for ThreadHandles");
  1230. ThreadCount = 0;
  1231. }
  1232. //
  1233. // Get the handles to the threads in the process
  1234. //
  1235. GetThreadHandles(Pid, ThreadHandles, ThreadCount);
  1236. //
  1237. // Allocate memory for ThreadContexts
  1238. //
  1239. ThreadContexts = (PCONTEXT)HeapAlloc(GetProcessHeap(),
  1240. HEAP_ZERO_MEMORY,
  1241. ThreadCount * sizeof(CONTEXT));
  1242. if (NULL == ThreadContexts) {
  1243. Error (__FILE__,
  1244. __LINE__,
  1245. "HeapAlloc failed for ThreadContexts");
  1246. ThreadCount = 0;
  1247. }
  1248. GetThreadContexts (ThreadContexts, ThreadHandles, ThreadCount);
  1249. //
  1250. // We need to know maximum and minimum address space that we can
  1251. // grovel. SYSTEM_INFO has this information.
  1252. //
  1253. GetSystemInfo(&SystemInfo);
  1254. MinAddress = SystemInfo.lpMinimumApplicationAddress;
  1255. MaxAddress = SystemInfo.lpMaximumApplicationAddress;
  1256. //
  1257. // Loop through virtual memory zones
  1258. //
  1259. Address = (ULONG_PTR)MinAddress;
  1260. //
  1261. // Allocate chunk of memory for virtual block
  1262. //
  1263. VirtualBlock = (PVOID) HeapAlloc(GetProcessHeap(),
  1264. HEAP_ZERO_MEMORY,
  1265. MAX_VIRTUAL_BLOCK_SIZE);
  1266. if (NULL == VirtualBlock) {
  1267. Error (__FILE__,
  1268. __LINE__,
  1269. "HeapAlloc failed to allocate memory");
  1270. Success = FALSE;
  1271. goto Exit;
  1272. }
  1273. VirtualBlockSize = MAX_VIRTUAL_BLOCK_SIZE;
  1274. //
  1275. // dwBytesRead equals 1 when we enter the loop for the first time due
  1276. // to previous initialization at the start of the function.
  1277. //
  1278. while (0 != dwBytesRead && Address < (ULONG_PTR)MaxAddress) {
  1279. dwBytesRead = VirtualQueryEx (g_hProcess,
  1280. (PVOID)Address,
  1281. &Buffer,
  1282. sizeof(Buffer));
  1283. if (0 != dwBytesRead) {
  1284. DWORD dwFlags = (PAGE_READWRITE |
  1285. PAGE_EXECUTE_READWRITE |
  1286. PAGE_WRITECOPY |
  1287. PAGE_EXECUTE_WRITECOPY);
  1288. //
  1289. // If the page can be written, it might contain pointers
  1290. // to heap blocks.
  1291. //
  1292. if ((Buffer.AllocationProtect & dwFlags) &&
  1293. (Buffer.State & MEM_COMMIT)) {
  1294. PULONG_PTR Pointer;
  1295. ULONG_PTR FinalAddress;
  1296. ULONG_PTR FilteredAddress;
  1297. SIZE_T NewRegionSize;
  1298. BOOL Result;
  1299. int j;
  1300. SIZE_T BytesRead = 0;
  1301. FilteredAddress = StackFilteredAddress(Address,
  1302. Buffer.RegionSize,
  1303. ThreadContexts,
  1304. ThreadCount);
  1305. NewRegionSize = Buffer.RegionSize -
  1306. (SIZE_T)( (ULONG_PTR)FilteredAddress - (ULONG_PTR)Address);
  1307. if (VirtualBlockSize < NewRegionSize) {
  1308. if (NULL != VirtualBlock) {
  1309. HeapFree(GetProcessHeap(), 0, VirtualBlock);
  1310. VirtualBlock = NULL;
  1311. VirtualBlockSize = 0;
  1312. }
  1313. VirtualBlock = (PVOID) HeapAlloc(GetProcessHeap(),
  1314. HEAP_ZERO_MEMORY,
  1315. NewRegionSize);
  1316. if (NULL == VirtualBlock) {
  1317. Error (
  1318. __FILE__,
  1319. __LINE__,
  1320. "HeapAlloc failed to allocate memory"
  1321. );
  1322. Success = FALSE;
  1323. goto Exit;
  1324. }
  1325. VirtualBlockSize = (ULONG)NewRegionSize;
  1326. }
  1327. Result = ReadProcessMemory(g_hProcess,
  1328. (PVOID)FilteredAddress,
  1329. VirtualBlock,
  1330. NewRegionSize,
  1331. &BytesRead);
  1332. assert(NewRegionSize == BytesRead);
  1333. FinalAddress = (ULONG_PTR)VirtualBlock + BytesRead;
  1334. Pointer = (PULONG_PTR) VirtualBlock;
  1335. //
  1336. // Loop through pages and check any possible
  1337. // pointer reference
  1338. //
  1339. while ((ULONG_PTR)Pointer < FinalAddress) {
  1340. PHEAP_BLOCK Block;
  1341. //
  1342. // Check whether we have a pointer to a
  1343. // busy heap block
  1344. //
  1345. Block = GetHeapBlock(*Pointer,HeapList);
  1346. if (NULL != Block) {
  1347. Block->RefCount += 1;
  1348. }
  1349. //
  1350. // Move to the next pointer
  1351. //
  1352. Pointer += 1;
  1353. }
  1354. }
  1355. //
  1356. // Move to the next VM range to query
  1357. //
  1358. Address += Buffer.RegionSize;
  1359. }
  1360. }
  1361. //
  1362. // Create a linked list of free heap blocks
  1363. //
  1364. {
  1365. ULONG i, j;
  1366. for (i=0; i<HeapList->HeapCount; i++)
  1367. for (j=0; j<HeapList->Heaps[i].BlockCount; j++)
  1368. if (0 == HeapList->Heaps[i].Blocks[j].RefCount)
  1369. InsertAddress(HeapList->Heaps[i].Blocks[j].BlockAddress,
  1370. FreeList);
  1371. }
  1372. Exit:
  1373. //
  1374. // Close the ThreadHandles opened
  1375. //
  1376. for (Index = 0; Index < ThreadCount; Index += 1) {
  1377. CloseHandle (ThreadHandles[Index]);
  1378. ThreadHandles[Index] = NULL;
  1379. }
  1380. //
  1381. // Cleanup the memory allocated for ThreadHandles
  1382. //
  1383. if (NULL != ThreadHandles) {
  1384. HeapFree(GetProcessHeap(), 0, ThreadHandles);
  1385. ThreadHandles = NULL;
  1386. }
  1387. //
  1388. // Cleanup the memory allocated for ThreadContexts
  1389. //
  1390. if (NULL != ThreadContexts) {
  1391. HeapFree(GetProcessHeap(), 0, ThreadContexts);
  1392. ThreadContexts = NULL;
  1393. }
  1394. //
  1395. // Free up the memory allocated for VirtualBlock
  1396. //
  1397. if (NULL != VirtualBlock ) {
  1398. HeapFree (GetProcessHeap(), 0, VirtualBlock);
  1399. VirtualBlock = NULL;
  1400. }
  1401. return Success;
  1402. }
  1403. VOID
  1404. DetectLeaks(
  1405. PHEAP_LIST HeapList,
  1406. ULONG Pid,
  1407. FILE * OutFile
  1408. )
  1409. {
  1410. ADDRESS_LIST FreeList;
  1411. //
  1412. // Initialize the linked list
  1413. //
  1414. InitializeAddressList(&FreeList);
  1415. //
  1416. // Get a handle to the process
  1417. //
  1418. g_hProcess = OpenProcess(PROCESS_QUERY_INFORMATION |
  1419. PROCESS_VM_READ |
  1420. PROCESS_SUSPEND_RESUME,
  1421. FALSE,
  1422. Pid);
  1423. if (NULL == g_hProcess) {
  1424. Error (__FILE__,
  1425. __LINE__,
  1426. "OpenProcess (%u) failed with error %u",
  1427. Pid,
  1428. GetLastError()
  1429. );
  1430. goto Exit;
  1431. }
  1432. //
  1433. // Sort Heaps
  1434. //
  1435. SortHeaps(HeapList, SortByBlockAddress);
  1436. //
  1437. // Scan through virtual memory zones
  1438. //
  1439. ScanProcessVirtualMemory(Pid, &FreeList, HeapList);
  1440. //
  1441. // Update references provided by the free blocks
  1442. //
  1443. ScanHeapFreeBlocks(HeapList, &FreeList);
  1444. //
  1445. // Dump the list of leaked blocks
  1446. //
  1447. DumpLeakList(OutFile, HeapList);
  1448. Exit:
  1449. //
  1450. // Close the process handle
  1451. //
  1452. if (NULL != g_hProcess) {
  1453. CloseHandle(g_hProcess);
  1454. g_hProcess = NULL;
  1455. }
  1456. //
  1457. // Free the memory associated with FreeList
  1458. //
  1459. FreeAddressList(&FreeList);
  1460. }