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.

1500 lines
35 KiB

  1. /*++
  2. Copyright (c) 2000 Microsoft Corporation
  3. Module Name:
  4. heapleak.c
  5. Abstract:
  6. WinDbg Extension Api
  7. Author:
  8. Adrian Marinescu (adrmarin) 04/17/2000
  9. Environment:
  10. User Mode.
  11. Revision History:
  12. --*/
  13. #include "precomp.h"
  14. #include "heap.h"
  15. #pragma hdrstop
  16. ULONG PageSize;
  17. ULONG HeapEntrySize;
  18. ULONG PointerSize;
  19. ULONG64 HeapLargestAddress;
  20. BOOLEAN Is64BitArchitecture;
  21. ULONG FrontEndHeapType;
  22. ULONG CrtSegmentIndex;
  23. ULONG ScanLevel;
  24. ULONG LoopLimit = 100000;
  25. BOOLEAN
  26. ReadHeapSubSegment(
  27. ULONG64 HeapAddress,
  28. ULONG64 SegmentAddress,
  29. ULONG64 SubSegmentAddress,
  30. HEAP_ITERATOR_CALLBACK HeapCallback
  31. )
  32. {
  33. ULONG64 SubSegmentDescriptor;
  34. ULONG64 BlockCount = 0, BlockSize;
  35. ULONG i;
  36. ULONG64 CrtAddress;
  37. ULONG64 EntryAddress;
  38. GetFieldValue(SubSegmentAddress, "ntdll!_HEAP_USERDATA_HEADER", "SubSegment", SubSegmentDescriptor);
  39. if (!(*HeapCallback)( CONTEXT_START_SUBSEGMENT,
  40. HeapAddress,
  41. SubSegmentAddress,
  42. SubSegmentDescriptor,
  43. 0
  44. )) {
  45. return FALSE;
  46. }
  47. GetFieldValue(SubSegmentDescriptor, "ntdll!_HEAP_SUBSEGMENT", "BlockCount", BlockCount);
  48. if (GetFieldValue(SubSegmentDescriptor, "ntdll!_HEAP_SUBSEGMENT", "BlockSize", BlockSize)) {
  49. (*HeapCallback)( CONTEXT_ERROR,
  50. HeapAddress,
  51. SegmentAddress,
  52. SubSegmentAddress,
  53. (ULONG64)(&"subsegment cannot access the block size\n")
  54. );
  55. return FALSE;
  56. }
  57. if (BlockSize <= 1) {
  58. (*HeapCallback)( CONTEXT_ERROR,
  59. HeapAddress,
  60. SegmentAddress,
  61. SubSegmentAddress,
  62. (ULONG64)(&"invalid block size\n")
  63. );
  64. return FALSE;
  65. }
  66. CrtAddress = SubSegmentAddress + GetTypeSize("ntdll!_HEAP_USERDATA_HEADER");
  67. for (i = 0; i < BlockCount; i++) {
  68. ULONG64 SmallTagIndex, BlockSegIndex;
  69. EntryAddress = CrtAddress + i * BlockSize * HeapEntrySize;
  70. GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "SegmentIndex", BlockSegIndex);
  71. if (BlockSegIndex != 0xFF) {
  72. (*HeapCallback)( CONTEXT_ERROR,
  73. HeapAddress,
  74. SegmentAddress,
  75. EntryAddress,
  76. (ULONG64)(&"SegmentIndex field corrupted\n")
  77. );
  78. }
  79. GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "SmallTagIndex", SmallTagIndex);
  80. if (SmallTagIndex) {
  81. (*HeapCallback)( CONTEXT_BUSY_BLOCK,
  82. HeapAddress,
  83. SegmentAddress,
  84. EntryAddress,
  85. BlockSize * HeapEntrySize
  86. );
  87. } else {
  88. (*HeapCallback)( CONTEXT_FREE_BLOCK,
  89. HeapAddress,
  90. SegmentAddress,
  91. EntryAddress,
  92. BlockSize * HeapEntrySize
  93. );
  94. }
  95. }
  96. if (!(*HeapCallback)( CONTEXT_END_SUBSEGMENT,
  97. HeapAddress,
  98. SubSegmentAddress,
  99. SubSegmentDescriptor,
  100. 0
  101. )) {
  102. return FALSE;
  103. }
  104. return TRUE;
  105. }
  106. //
  107. // Walking heap routines
  108. //
  109. BOOLEAN
  110. ReadHeapSegment(
  111. ULONG64 HeapAddress,
  112. ULONG SegmentIndex,
  113. ULONG64 SegmentAddress,
  114. HEAP_ITERATOR_CALLBACK HeapCallback
  115. )
  116. {
  117. ULONG64 SegmentBaseAddress;
  118. ULONG64 PrevEntryAddress, EntryAddress, NextEntryAddress;
  119. ULONG64 EntrySize, EntryFlags, BlockSegIndex;
  120. ULONG64 SegmentLastValidEntry;
  121. ULONG64 UnCommittedRange, UnCommittedRangeAddress = 0, UnCommittedRangeSize = 0;
  122. ULONG LoopCount;
  123. BOOLEAN IsSubsegment;
  124. ScanLevel = SCANSEGMENT;
  125. if (!(*HeapCallback)( CONTEXT_START_SEGMENT,
  126. HeapAddress,
  127. SegmentAddress,
  128. 0,
  129. 0
  130. )) {
  131. return FALSE;
  132. }
  133. CrtSegmentIndex = SegmentIndex;
  134. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "BaseAddress", SegmentBaseAddress);
  135. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "LastValidEntry", SegmentLastValidEntry);
  136. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "UnCommittedRanges", UnCommittedRange);
  137. if (UnCommittedRange) {
  138. GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Address", UnCommittedRangeAddress);
  139. GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Size", UnCommittedRangeSize);
  140. }
  141. // dprintf("Uncommitted: %p %p %p\n", UnCommittedRange, UnCommittedRangeAddress, UnCommittedRangeSize);
  142. if (SegmentBaseAddress == HeapAddress) {
  143. EntryAddress = HeapAddress;
  144. } else {
  145. EntryAddress = SegmentAddress;
  146. }
  147. PrevEntryAddress = 0;
  148. LoopCount = 0;
  149. while (EntryAddress < SegmentLastValidEntry) {
  150. if (++LoopCount >= LoopLimit) {
  151. dprintf("Walking the segment exceeded the %ld limit\n", LoopLimit);
  152. break;
  153. }
  154. if (ScanLevel < SCANSEGMENT) {
  155. break;
  156. }
  157. if (GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "Size", EntrySize)) {
  158. (*HeapCallback)( CONTEXT_ERROR,
  159. HeapAddress,
  160. SegmentAddress,
  161. EntryAddress,
  162. (ULONG64)(&"unable to read uncommited range structure at\n")
  163. );
  164. break;
  165. }
  166. if (EntrySize <= 1) {
  167. (*HeapCallback)( CONTEXT_ERROR,
  168. HeapAddress,
  169. SegmentAddress,
  170. EntryAddress,
  171. (ULONG64)(&"invalid block size\n")
  172. );
  173. break;
  174. }
  175. EntrySize *= HeapEntrySize;
  176. NextEntryAddress = EntryAddress + EntrySize;
  177. GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "Flags", EntryFlags);
  178. GetFieldValue(EntryAddress, "ntdll!_HEAP_ENTRY", "SegmentIndex", BlockSegIndex);
  179. if (BlockSegIndex != CrtSegmentIndex) {
  180. (*HeapCallback)( CONTEXT_ERROR,
  181. HeapAddress,
  182. SegmentAddress,
  183. EntryAddress,
  184. (ULONG64)(&"SegmentIndex field corrupted\n")
  185. );
  186. }
  187. IsSubsegment = FALSE;
  188. if (FrontEndHeapType == 2) {
  189. ULONG64 Signature;
  190. GetFieldValue(EntryAddress + HeapEntrySize, "ntdll!_HEAP_USERDATA_HEADER", "Signature", Signature);
  191. if ((ULONG)Signature == 0xF0E0D0C0) {
  192. ReadHeapSubSegment( HeapAddress,
  193. SegmentAddress,
  194. EntryAddress + HeapEntrySize,
  195. HeapCallback );
  196. IsSubsegment = TRUE;
  197. if (CheckControlC()) {
  198. ScanLevel = 0;
  199. return FALSE;
  200. }
  201. }
  202. }
  203. if (!IsSubsegment) {
  204. if (EntryFlags & HEAP_ENTRY_BUSY) {
  205. (*HeapCallback)( CONTEXT_BUSY_BLOCK,
  206. HeapAddress,
  207. SegmentAddress,
  208. EntryAddress,
  209. EntrySize
  210. );
  211. } else {
  212. (*HeapCallback)( CONTEXT_FREE_BLOCK,
  213. HeapAddress,
  214. SegmentAddress,
  215. EntryAddress,
  216. EntrySize
  217. );
  218. }
  219. }
  220. PrevEntryAddress = EntryAddress;
  221. EntryAddress = NextEntryAddress;
  222. if (EntryFlags & HEAP_ENTRY_LAST_ENTRY) {
  223. if (CheckControlC()) {
  224. ScanLevel = 0;
  225. return FALSE;
  226. }
  227. if (EntryAddress == UnCommittedRangeAddress) {
  228. PrevEntryAddress = 0;
  229. EntryAddress = UnCommittedRangeAddress + UnCommittedRangeSize;
  230. GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Next", UnCommittedRange);
  231. if (UnCommittedRange) {
  232. GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Address", UnCommittedRangeAddress);
  233. GetFieldValue(UnCommittedRange, "ntdll!_HEAP_UNCOMMMTTED_RANGE", "Size", UnCommittedRangeSize);
  234. }
  235. } else {
  236. break;
  237. }
  238. }
  239. }
  240. if (!(*HeapCallback)( CONTEXT_END_SEGMENT,
  241. HeapAddress,
  242. SegmentAddress,
  243. 0,
  244. 0
  245. )) {
  246. return FALSE;
  247. }
  248. return TRUE;
  249. }
  250. BOOLEAN
  251. ReadHeapData(ULONG64 HeapAddress, HEAP_ITERATOR_CALLBACK HeapCallback)
  252. {
  253. ULONG SegmentCount = 0;
  254. ULONG64 Head;
  255. ULONG64 Next;
  256. ULONG i;
  257. ULONG PtrSize;
  258. ULONG SegmentsOffset;
  259. ULONG VirtualBlockOffset;
  260. ULONG64 Segment;
  261. ULONG64 LookasideAddress;
  262. ULONG64 LFHAddress;
  263. ULONG LoopCount;
  264. ScanLevel = SCANHEAP;
  265. if (!(*HeapCallback)( CONTEXT_START_HEAP,
  266. HeapAddress,
  267. 0,
  268. 0,
  269. 0
  270. )) {
  271. return FALSE;
  272. }
  273. PtrSize = IsPtr64() ? 8 : 4;
  274. LookasideAddress = 0;
  275. LFHAddress = 0;
  276. FrontEndHeapType = 0;
  277. if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "Lookaside", LookasideAddress)) {
  278. if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeapType", FrontEndHeapType)) {
  279. dprintf("Front-end heap type info is not available\n");
  280. }
  281. switch (FrontEndHeapType){
  282. case 1:
  283. GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeap", LookasideAddress);
  284. break;
  285. case 2:
  286. GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeap", LFHAddress);
  287. break;
  288. }
  289. } else {
  290. if (LookasideAddress) {
  291. FrontEndHeapType = 1;
  292. }
  293. }
  294. GetFieldOffset("ntdll!_HEAP", "Segments", &SegmentsOffset);
  295. do {
  296. if (ScanLevel < SCANHEAP) {
  297. return FALSE;
  298. }
  299. if (!ReadPointer( HeapAddress + SegmentsOffset + SegmentCount*PtrSize,
  300. &Segment ) ) {
  301. break;
  302. }
  303. if (Segment) {
  304. ReadHeapSegment( HeapAddress,
  305. SegmentCount,
  306. Segment,
  307. HeapCallback
  308. );
  309. SegmentCount += 1;
  310. if (CheckControlC()) {
  311. ScanLevel = 0;
  312. return FALSE;
  313. }
  314. }
  315. } while ( Segment );
  316. GetFieldOffset("_HEAP", "VirtualAllocdBlocks", &VirtualBlockOffset);
  317. Head = HeapAddress + VirtualBlockOffset;
  318. GetFieldValue(HeapAddress, "ntdll!_HEAP", "VirtualAllocdBlocks.Flink", Next);
  319. LoopCount = 0;
  320. while (Next != Head) {
  321. ULONG64 VBlockSize;
  322. if (++LoopCount >= LoopLimit) {
  323. dprintf("Walking the virtual block list exceeded the %ld limit\n", LoopLimit);
  324. break;
  325. }
  326. if (ScanLevel < SCANHEAP) {
  327. return FALSE;
  328. }
  329. GetFieldValue(Next, "ntdll!_HEAP_VIRTUAL_ALLOC_ENTRY", "CommitSize", VBlockSize);
  330. (*HeapCallback)( CONTEXT_VIRTUAL_BLOCK,
  331. HeapAddress,
  332. 0,
  333. Next,
  334. VBlockSize
  335. );
  336. if (!ReadPointer(Next, &Next)) {
  337. (*HeapCallback)( CONTEXT_ERROR,
  338. HeapAddress,
  339. 0,
  340. Next,
  341. (ULONG64)(&"Unable to read virtual block\n")
  342. );
  343. break;
  344. }
  345. }
  346. if (!(*HeapCallback)( CONTEXT_END_BLOCKS,
  347. HeapAddress,
  348. 0,
  349. 0,
  350. 0
  351. )) {
  352. return FALSE;
  353. }
  354. // dprintf("Scanning lookasides\n");
  355. if (LookasideAddress) {
  356. ULONG LookasideSize;
  357. PVOID Lookaside;
  358. ULONG HeapEntrySize;
  359. HeapEntrySize = GetTypeSize("ntdll!_HEAP_ENTRY");
  360. LookasideSize = GetTypeSize("ntdll!_HEAP_LOOKASIDE");
  361. for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i++) {
  362. if (ScanLevel < SCANHEAP) {
  363. return FALSE;
  364. }
  365. GetFieldValue(LookasideAddress, "ntdll!_HEAP_LOOKASIDE", "ListHead.Next", Next);
  366. if (Is64BitArchitecture) {
  367. Next <<= 3;
  368. }
  369. LoopCount = 0;
  370. while (Next) {
  371. if (++LoopCount >= LoopLimit) {
  372. dprintf("Walking the lookaside block list index %ld exceeded the %ld limit\n", i, LoopLimit);
  373. break;
  374. }
  375. (*HeapCallback)( CONTEXT_LOOKASIDE_BLOCK,
  376. HeapAddress,
  377. 0,
  378. Next - HeapEntrySize,
  379. i*HeapEntrySize
  380. );
  381. if (!ReadPointer(Next, &Next)) {
  382. (*HeapCallback)( CONTEXT_ERROR,
  383. HeapAddress,
  384. 0,
  385. Next,
  386. (ULONG64)(&"Unable to read lookaside block\n")
  387. );
  388. break;
  389. }
  390. }
  391. LookasideAddress += LookasideSize;
  392. }
  393. }
  394. if (LFHAddress) {
  395. (*HeapCallback)( CONTEXT_LFH_HEAP,
  396. HeapAddress,
  397. LFHAddress,
  398. 0,
  399. 0
  400. );
  401. }
  402. (*HeapCallback)( CONTEXT_END_HEAP,
  403. HeapAddress,
  404. 0,
  405. 0,
  406. 0
  407. );
  408. return TRUE;
  409. }
  410. void
  411. ScanProcessHeaps(
  412. IN ULONG64 AddressToDump,
  413. IN ULONG64 ProcessPeb,
  414. HEAP_ITERATOR_CALLBACK HeapCallback
  415. )
  416. {
  417. ULONG NumberOfHeaps;
  418. ULONG64 pHeapsList;
  419. ULONG64 * Heaps;
  420. ULONG PtrSize;
  421. ULONG HeapNumber;
  422. if (AddressToDump) {
  423. ReadHeapData ( AddressToDump, HeapCallback);
  424. return;
  425. }
  426. if (!(*HeapCallback)( CONTEXT_START_GLOBALS,
  427. 0,
  428. 0,
  429. 0,
  430. ProcessPeb
  431. )) {
  432. return;
  433. }
  434. ScanLevel = SCANPROCESS;
  435. GetFieldValue(ProcessPeb, "ntdll!_PEB", "NumberOfHeaps", NumberOfHeaps);
  436. GetFieldValue(ProcessPeb, "ntdll!_PEB", "ProcessHeaps", pHeapsList);
  437. if (NumberOfHeaps == 0) {
  438. dprintf( "No heaps to display.\n" );
  439. return;
  440. }
  441. if (!pHeapsList) {
  442. dprintf( "Unable to get address of ProcessHeaps array\n" );
  443. return;
  444. }
  445. Heaps = malloc( NumberOfHeaps * sizeof(ULONG64) );
  446. if (!Heaps) {
  447. dprintf( "Unable to allocate memory to hold ProcessHeaps array\n" );
  448. return;
  449. }
  450. PtrSize = IsPtr64() ? 8 : 4;
  451. for (HeapNumber=0; HeapNumber<NumberOfHeaps ; HeapNumber++) {
  452. if (!ReadPointer( pHeapsList + HeapNumber*PtrSize,
  453. &Heaps[HeapNumber] ) ) {
  454. dprintf( "%08p: Unable to read ProcessHeaps array\n", pHeapsList );
  455. free(Heaps);
  456. return;
  457. }
  458. }
  459. for ( HeapNumber = 0; HeapNumber < NumberOfHeaps; HeapNumber++ ) {
  460. if (ScanLevel < SCANPROCESS) {
  461. free(Heaps);
  462. return;
  463. }
  464. if ((AddressToDump == 0)
  465. ||
  466. (AddressToDump == Heaps[HeapNumber])) {
  467. ReadHeapData ( Heaps[HeapNumber], HeapCallback);
  468. }
  469. }
  470. free(Heaps);
  471. }
  472. //
  473. // Allocation routines
  474. //
  475. HANDLE TempHeap;
  476. #define AllocateBlock(Size) HeapAlloc(TempHeap, 0, Size)
  477. #define FreeBlock(P) HeapFree(TempHeap, 0, P)
  478. //
  479. // Leak detector code
  480. //
  481. typedef enum _USAGE_TYPE {
  482. UsageUnknown,
  483. UsageModule,
  484. UsageHeap,
  485. UsageOther
  486. } USAGE_TYPE;
  487. typedef struct _HEAP_BLOCK {
  488. LIST_ENTRY Entry;
  489. ULONG64 BlockAddress;
  490. ULONG64 Size;
  491. LONG Count;
  492. } HEAP_BLOCK, *PHEAP_BLOCK;
  493. typedef struct _BLOCK_DESCR {
  494. USAGE_TYPE Type;
  495. ULONG64 Heap;
  496. LONG Count;
  497. HEAP_BLOCK Blocks[1];
  498. }BLOCK_DESCR, *PBLOCK_DESCR;
  499. typedef struct _MEMORY_MAP {
  500. ULONG64 Granularity;
  501. ULONG64 Offset;
  502. ULONG64 MaxAddress;
  503. CHAR FlagsBitmap[256 / 8];
  504. union{
  505. struct _MEMORY_MAP * Details[ 256 ];
  506. PBLOCK_DESCR Usage[ 256 ];
  507. };
  508. struct _MEMORY_MAP * Parent;
  509. } MEMORY_MAP, *PMEMORY_MAP;
  510. MEMORY_MAP ProcessMemory;
  511. ULONG LeaksCount = 0;
  512. ULONG64 PreviousPage = 0;
  513. ULONG64 CrtPage = 0;
  514. LONG NumBlocks = 0;
  515. PHEAP_BLOCK TempBlocks;
  516. ULONG64 LastHeapAddress = 0;
  517. ULONG64 RtlpPreviousStartAddress = 0;
  518. LIST_ENTRY HeapBusyList;
  519. LIST_ENTRY HeapLeakList;
  520. void InitializeMap(PMEMORY_MAP MemMap, PMEMORY_MAP Parent)
  521. {
  522. memset(MemMap, 0, sizeof(*MemMap));
  523. MemMap->Parent = Parent;
  524. if (Parent) {
  525. MemMap->Granularity = Parent->Granularity / 256;
  526. }
  527. }
  528. void
  529. SetBlockInfo(PMEMORY_MAP MemMap, ULONG64 Base, ULONG64 Size, PBLOCK_DESCR BlockDescr)
  530. {
  531. ULONG64 Start, End;
  532. ULONG64 i;
  533. if (((Base + Size - 1) < MemMap->Offset) ||
  534. (Base > MemMap->MaxAddress)
  535. ) {
  536. return;
  537. }
  538. if (Base > MemMap->Offset) {
  539. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  540. } else {
  541. Start = 0;
  542. }
  543. End = (Base - MemMap->Offset + Size - 1) / MemMap->Granularity;
  544. if (End > 255) {
  545. End = 255;
  546. }
  547. for (i = Start; i <= End; i++) {
  548. if (MemMap->Granularity == PageSize) {
  549. if (BlockDescr) {
  550. if (MemMap->Usage[i] != NULL) {
  551. if (MemMap->Usage[i] != BlockDescr) {
  552. dprintf("Error\n");
  553. }
  554. }
  555. MemMap->Usage[i] = BlockDescr;
  556. } else {
  557. MemMap->FlagsBitmap[i / 8] |= 1 << (i % 8);
  558. }
  559. } else {
  560. if (!MemMap->Details[i]) {
  561. MemMap->Details[i] = AllocateBlock(sizeof(*MemMap));
  562. if (!MemMap->Details[i]) {
  563. dprintf("Error allocate\n");
  564. return;
  565. }
  566. InitializeMap(MemMap->Details[i], MemMap);
  567. MemMap->Details[i]->Offset = MemMap->Offset + MemMap->Granularity * i;
  568. MemMap->Details[i]->MaxAddress = MemMap->Offset + MemMap->Granularity * (i+1) - 1;
  569. }
  570. SetBlockInfo(MemMap->Details[i], Base, Size, BlockDescr);
  571. }
  572. }
  573. }
  574. PBLOCK_DESCR
  575. GetBlockInfo(PMEMORY_MAP MemMap, ULONG64 Base)
  576. {
  577. ULONG64 Start;
  578. PBLOCK_DESCR BlockDescr = NULL;
  579. if ((Base < MemMap->Offset) ||
  580. (Base > MemMap->MaxAddress)
  581. ) {
  582. return NULL;
  583. }
  584. if (Base > MemMap->Offset) {
  585. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  586. } else {
  587. Start = 0;
  588. }
  589. if (MemMap->Granularity == PageSize) {
  590. return MemMap->Usage[Start];
  591. } else {
  592. if (MemMap->Details[Start]) {
  593. return GetBlockInfo(MemMap->Details[Start], Base);
  594. }
  595. }
  596. return NULL;
  597. }
  598. BOOLEAN
  599. GetFlag(PMEMORY_MAP MemMap, ULONG64 Base)
  600. {
  601. ULONG64 Start;
  602. PBLOCK_DESCR BlockDescr = NULL;
  603. /*
  604. dprintf("GetFlag %p %p %p\n",
  605. MemMap->Offset,
  606. MemMap->MaxAddress,
  607. MemMap->Granularity
  608. );
  609. */
  610. if ((Base < MemMap->Offset) ||
  611. (Base > MemMap->MaxAddress)
  612. ) {
  613. return FALSE;
  614. }
  615. if (Base > MemMap->Offset) {
  616. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  617. } else {
  618. Start = 0;
  619. }
  620. if (MemMap->Granularity == PageSize) {
  621. ULONG Flag = (MemMap->FlagsBitmap[Start / 8] & (1 << (Start % 8))) != 0;
  622. return (MemMap->FlagsBitmap[Start / 8] & (1 << (Start % 8))) != 0;
  623. } else {
  624. if (MemMap->Details[Start]) {
  625. return GetFlag(MemMap->Details[Start], Base);
  626. }
  627. }
  628. return FALSE;
  629. }
  630. void InitializeSystem()
  631. {
  632. ULONG64 AddressRange = PageSize;
  633. ULONG64 PreviousAddressRange = PageSize;
  634. InitializeMap(&ProcessMemory, NULL);
  635. InitializeListHead( &HeapBusyList );
  636. InitializeListHead( &HeapLeakList );
  637. while (TRUE) {
  638. AddressRange = AddressRange * 256;
  639. if ((AddressRange < PreviousAddressRange)
  640. ||
  641. (AddressRange > HeapLargestAddress)
  642. ) {
  643. ProcessMemory.MaxAddress = HeapLargestAddress;
  644. ProcessMemory.Granularity = PreviousAddressRange;
  645. break;
  646. }
  647. PreviousAddressRange = AddressRange;
  648. }
  649. TempBlocks = AllocateBlock(PageSize);
  650. if (TempBlocks == NULL) {
  651. dprintf("Cannot allocate temp buffer\n");
  652. }
  653. }
  654. BOOLEAN
  655. PushPageDescriptor(ULONG64 Page, ULONG64 NumPages)
  656. {
  657. PBLOCK_DESCR PBlockDescr;
  658. PBLOCK_DESCR PreviousDescr;
  659. LONG i;
  660. PreviousDescr = GetBlockInfo(&ProcessMemory, Page * PageSize);
  661. if (PreviousDescr) {
  662. dprintf("Conflicting descriptors %08lx\n", PreviousDescr);
  663. return FALSE;
  664. }
  665. PBlockDescr = (PBLOCK_DESCR)AllocateBlock(sizeof(BLOCK_DESCR) + (NumBlocks - 1) * sizeof(HEAP_BLOCK));
  666. if (!PBlockDescr) {
  667. dprintf("Unable to allocate page descriptor\n");
  668. return FALSE;
  669. }
  670. PBlockDescr->Type = UsageHeap;
  671. PBlockDescr->Count = NumBlocks;
  672. PBlockDescr->Heap = LastHeapAddress;
  673. memcpy(PBlockDescr->Blocks, TempBlocks, NumBlocks * sizeof(HEAP_BLOCK));
  674. for (i = 0; i < NumBlocks; i++) {
  675. InitializeListHead( &PBlockDescr->Blocks[i].Entry );
  676. if (PBlockDescr->Blocks[i].BlockAddress != RtlpPreviousStartAddress) {
  677. InsertTailList(&HeapLeakList, &PBlockDescr->Blocks[i].Entry);
  678. PBlockDescr->Blocks[i].Count = 0;
  679. RtlpPreviousStartAddress = PBlockDescr->Blocks[i].BlockAddress;
  680. }
  681. }
  682. SetBlockInfo(&ProcessMemory, Page * PageSize, NumPages * PageSize, PBlockDescr);
  683. return TRUE;
  684. }
  685. BOOLEAN RegisterHeapBlocks(
  686. IN ULONG Context,
  687. IN ULONG64 HeapAddress,
  688. IN ULONG64 SegmentAddress,
  689. IN ULONG64 EntryAddress,
  690. IN ULONG64 Data
  691. )
  692. {
  693. if (Context == CONTEXT_START_HEAP) {
  694. dprintf("Heap %p\n", HeapAddress);
  695. LastHeapAddress = HeapAddress;
  696. return TRUE;
  697. }
  698. if (Context == CONTEXT_START_SEGMENT) {
  699. ULONG64 NumberOfPages;
  700. ULONG64 SegmentBaseAddress;
  701. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfPages", NumberOfPages);
  702. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "BaseAddress", SegmentBaseAddress);
  703. SetBlockInfo(&ProcessMemory, SegmentBaseAddress, NumberOfPages * PageSize, NULL);
  704. return TRUE;
  705. }
  706. if (Context == CONTEXT_ERROR) {
  707. dprintf("HEAP %p (Seg %p) At %p Error: %s\n",
  708. HeapAddress,
  709. SegmentAddress,
  710. EntryAddress,
  711. Data
  712. );
  713. return TRUE;
  714. }
  715. if (Context == CONTEXT_END_BLOCKS) {
  716. if (PreviousPage) {
  717. PushPageDescriptor(PreviousPage, 1);
  718. }
  719. PreviousPage = 0;
  720. NumBlocks = 0;
  721. } else if (Context == CONTEXT_BUSY_BLOCK) {
  722. ULONG EntrySize;
  723. ULONG64 EndPage;
  724. EntrySize = (ULONG)Data;
  725. EndPage = (EntryAddress + (EntrySize - 1)) / PageSize;
  726. if (!GetFlag(&ProcessMemory, EntryAddress)) {
  727. dprintf("CONTEXT_BUSY_BLOCK %p address isn't from the heap\n", EntryAddress);
  728. }
  729. CrtPage = (EntryAddress) / PageSize;
  730. if (CrtPage != PreviousPage) {
  731. if (PreviousPage) {
  732. PushPageDescriptor(PreviousPage, 1);
  733. }
  734. PreviousPage = CrtPage;
  735. NumBlocks = 0;
  736. }
  737. TempBlocks[NumBlocks].BlockAddress = EntryAddress;
  738. TempBlocks[NumBlocks].Count = 0;
  739. TempBlocks[NumBlocks].Size = EntrySize;
  740. NumBlocks++;
  741. if (EndPage != CrtPage) {
  742. PushPageDescriptor(CrtPage, 1);
  743. NumBlocks = 0;
  744. TempBlocks[NumBlocks].BlockAddress = (ULONG_PTR)EntryAddress;
  745. TempBlocks[NumBlocks].Count = 0;
  746. TempBlocks[NumBlocks].Size = EntrySize;
  747. NumBlocks = 1;
  748. if (EndPage - CrtPage > 1) {
  749. PushPageDescriptor(CrtPage + 1, EndPage - CrtPage - 1);
  750. }
  751. PreviousPage = EndPage;
  752. }
  753. } else if (Context == CONTEXT_VIRTUAL_BLOCK) {
  754. ULONG64 EndPage;
  755. EndPage = (EntryAddress + Data - 1) / PageSize;
  756. CrtPage = (EntryAddress) / PageSize;
  757. if (CrtPage != PreviousPage) {
  758. if (PreviousPage) {
  759. PushPageDescriptor(PreviousPage, 1);
  760. }
  761. PreviousPage = CrtPage;
  762. NumBlocks = 0;
  763. } else {
  764. dprintf("Error in large block address\n");
  765. }
  766. TempBlocks[NumBlocks].BlockAddress = EntryAddress;
  767. TempBlocks[NumBlocks].Count = 0;
  768. TempBlocks[NumBlocks].Size = Data * HeapEntrySize;
  769. NumBlocks++;
  770. PushPageDescriptor(CrtPage, EndPage - CrtPage + 1);
  771. PreviousPage = 0;
  772. } else if ( Context == CONTEXT_LOOKASIDE_BLOCK ) {
  773. PBLOCK_DESCR PBlockDescr;
  774. LONG i;
  775. if (!GetFlag(&ProcessMemory, EntryAddress)) {
  776. dprintf("CONTEXT_LOOKASIDE_BLOCK %p address isn't from the heap\n", EntryAddress);
  777. }
  778. PBlockDescr = GetBlockInfo(&ProcessMemory, EntryAddress);
  779. if (!PBlockDescr) {
  780. dprintf("Error finding block from lookaside %p\n", EntryAddress);
  781. return FALSE;
  782. }
  783. for (i = 0; i < PBlockDescr->Count; i++) {
  784. if ((PBlockDescr->Blocks[i].BlockAddress <= (ULONG_PTR)EntryAddress) &&
  785. (PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > (ULONG_PTR)EntryAddress)) {
  786. PBlockDescr->Blocks[i].Count = -10000;
  787. RemoveEntryList(&PBlockDescr->Blocks[i].Entry);
  788. return TRUE;
  789. }
  790. }
  791. dprintf("Error, block %p from lookaside not found in allocated block list\n", EntryAddress);
  792. }
  793. return TRUE;
  794. }
  795. PHEAP_BLOCK
  796. GetHeapBlock(ULONG64 Address)
  797. {
  798. PBLOCK_DESCR PBlockDescr;
  799. LONG i;
  800. PBlockDescr = GetBlockInfo(&ProcessMemory, Address);
  801. if (PBlockDescr) {
  802. for (i = 0; i < PBlockDescr->Count; i++) {
  803. if ((PBlockDescr->Blocks[i].BlockAddress <= Address) &&
  804. (PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > Address)) {
  805. if (PBlockDescr->Blocks[i].BlockAddress != Address) {
  806. return GetHeapBlock(PBlockDescr->Blocks[i].BlockAddress);
  807. }
  808. return &(PBlockDescr->Blocks[i]);
  809. }
  810. }
  811. }
  812. return NULL;
  813. }
  814. BOOLEAN
  815. ScanHeapAllocBlocks()
  816. {
  817. PLIST_ENTRY Next;
  818. Next = HeapBusyList.Flink;
  819. while (Next != &HeapBusyList) {
  820. PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
  821. PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + HeapEntrySize);
  822. Next = Next->Flink;
  823. while ((ULONG_PTR)CrtAddress < Block->BlockAddress + Block->Size) {
  824. ULONG_PTR Pointer;
  825. if (ReadMemory( (ULONG64)(CrtAddress),
  826. &Pointer,
  827. sizeof(Pointer),
  828. NULL
  829. )) {
  830. PHEAP_BLOCK pBlock = GetHeapBlock( Pointer );
  831. if (pBlock) {
  832. //
  833. // We found a block. we increment then the reference count
  834. //
  835. if (pBlock->Count == 0) {
  836. RemoveEntryList(&pBlock->Entry);
  837. InsertTailList(&HeapBusyList, &pBlock->Entry);
  838. }
  839. pBlock->Count += 1;
  840. }
  841. }
  842. //
  843. // Go to the next possible pointer
  844. //
  845. CrtAddress++;
  846. }
  847. }
  848. Next = HeapLeakList.Flink;
  849. while (Next != &HeapLeakList) {
  850. PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
  851. PBLOCK_DESCR PBlockDescr = GetBlockInfo( &ProcessMemory, Block->BlockAddress );
  852. PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + HeapEntrySize);
  853. //
  854. // First time we need to display the header
  855. //
  856. if (LeaksCount == 0) {
  857. dprintf("\n");
  858. DumpEntryHeader();
  859. }
  860. //
  861. // Display the information for this block
  862. //
  863. DumpEntryInfo(PBlockDescr->Heap, 0, Block->BlockAddress);
  864. LeaksCount += 1;
  865. //
  866. // Go to the next item from the leak list
  867. //
  868. Next = Next->Flink;
  869. }
  870. return TRUE;
  871. }
  872. BOOLEAN
  873. ScanProcessVM (
  874. HANDLE hProcess
  875. )
  876. {
  877. NTSTATUS Status;
  878. SIZE_T BufferLen;
  879. ULONG_PTR lpAddress = 0;
  880. MEMORY_BASIC_INFORMATION Buffer;
  881. PVOID MemoryBuffer;
  882. if ( hProcess ) {
  883. PROCESS_BASIC_INFORMATION BasicInfo;
  884. dprintf("Scanning VM ...");
  885. Status = NtQueryInformationProcess(
  886. hProcess,
  887. ProcessBasicInformation,
  888. &BasicInfo,
  889. sizeof(BasicInfo),
  890. NULL
  891. );
  892. // dprintf("PEB %p\n", BasicInfo.PebBaseAddress);
  893. MemoryBuffer = AllocateBlock(PageSize);
  894. if (!MemoryBuffer) {
  895. return FALSE;
  896. }
  897. BufferLen = sizeof(Buffer);
  898. while (BufferLen) {
  899. BufferLen = VirtualQueryEx( hProcess,
  900. (LPVOID)lpAddress,
  901. &Buffer,
  902. sizeof(Buffer)
  903. );
  904. if (BufferLen) {
  905. if (( Buffer.AllocationProtect &
  906. (PAGE_READWRITE | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
  907. ) {
  908. ULONG64 NumPages;
  909. ULONG i, j;
  910. NumPages = Buffer.RegionSize / PageSize;
  911. for (i = 0; i < NumPages; i++) {
  912. if (ReadMemory( (ULONG64)(lpAddress + i * PageSize),
  913. MemoryBuffer,
  914. PageSize,
  915. NULL )
  916. &&
  917. !GetFlag(&ProcessMemory, lpAddress)
  918. ) {
  919. ULONG_PTR * Pointers = (ULONG_PTR *)MemoryBuffer;
  920. for (j = 0; j < PageSize/sizeof(ULONG_PTR); j++) {
  921. ULONG_PTR Address = lpAddress + i * PageSize + j * sizeof(ULONG_PTR);
  922. PHEAP_BLOCK pBlock = GetHeapBlock(*Pointers);
  923. if (pBlock) {
  924. if (pBlock->Count == 0) {
  925. RemoveEntryList(&pBlock->Entry);
  926. InsertTailList(&HeapBusyList, &pBlock->Entry);
  927. }
  928. pBlock->Count += 1;
  929. }
  930. Pointers += 1;
  931. }
  932. }
  933. if (CheckControlC()) {
  934. FreeBlock(MemoryBuffer);
  935. ScanLevel = 0;
  936. return FALSE;
  937. }
  938. }
  939. }
  940. lpAddress += Buffer.RegionSize;
  941. }
  942. }
  943. //
  944. // First scan will mark all used blocks
  945. //
  946. ScanHeapAllocBlocks();
  947. FreeBlock(MemoryBuffer);
  948. }
  949. return TRUE;
  950. }
  951. void InspectLeaks(
  952. IN ULONG64 AddressToDump,
  953. IN ULONG64 ProcessPeb
  954. )
  955. {
  956. HANDLE hProcess;
  957. LeaksCount = 0;
  958. InitializeSystem();
  959. if (TempBlocks) {
  960. ScanProcessHeaps( 0,
  961. ProcessPeb,
  962. RegisterHeapBlocks
  963. );
  964. GetCurrentProcessHandle( &hProcess );
  965. if (hProcess){
  966. ScanProcessVM(hProcess);
  967. if (LeaksCount) {
  968. dprintf("%ld leaks detected.\n", LeaksCount);
  969. } else {
  970. dprintf( "No leaks detected.\n");
  971. }
  972. } else {
  973. dprintf("Unable to get the process handle\n");
  974. }
  975. }
  976. }
  977. VOID
  978. HeapDetectLeaks()
  979. {
  980. ULONG64 Process;
  981. ULONG64 ThePeb;
  982. ULONG64 PageHeapAddress;
  983. BOOLEAN PageHeapEnabled = FALSE;
  984. ULONG PageHeapFlags = 0;
  985. if (!InitializeHeapExtension()) {
  986. return;
  987. }
  988. //
  989. // Return immediately if full page heap is enabled
  990. //
  991. PageHeapAddress = GetExpression ("ntdll!RtlpDebugPageHeap");
  992. ReadMemory (PageHeapAddress,
  993. &PageHeapEnabled,
  994. sizeof (BOOLEAN),
  995. NULL);
  996. PageHeapAddress = GetExpression ("ntdll!RtlpDphGlobalFlags");
  997. ReadMemory (PageHeapAddress,
  998. &PageHeapFlags,
  999. sizeof (ULONG),
  1000. NULL);
  1001. if (PageHeapEnabled == TRUE && (PageHeapFlags & 0x01)) {
  1002. dprintf ("!heap -l does not work if full page heap is enabled for the process \n");
  1003. return;
  1004. }
  1005. GetPebAddress( 0, &ThePeb);
  1006. TempHeap = HeapCreate(HEAP_NO_SERIALIZE | HEAP_GROWABLE, 0, 0);
  1007. if (!TempHeap) {
  1008. dprintf("Unable to create temporary heap\n");
  1009. return;
  1010. }
  1011. InspectLeaks( 0, ThePeb);
  1012. HeapDestroy(TempHeap);
  1013. TempHeap = NULL;
  1014. }
  1015. BOOLEAN
  1016. InitializeHeapExtension()
  1017. {
  1018. PointerSize = IsPtr64() ? 8 : 4;
  1019. HeapEntrySize = GetTypeSize("ntdll!_HEAP_ENTRY");
  1020. if ((HeapEntrySize == 0)
  1021. ||
  1022. (PointerSize == 0)) {
  1023. dprintf("Invalid type information\n");
  1024. return FALSE;
  1025. }
  1026. //
  1027. // Issue adrmarin 04/28/00: The page size should be available in the new interface
  1028. // IDebugControl::GetPageSize
  1029. //
  1030. if (PointerSize == 4) {
  1031. PageSize = 0x1000;
  1032. HeapLargestAddress = (ULONG)-1;
  1033. Is64BitArchitecture = FALSE;
  1034. } else {
  1035. PageSize = 0x2000;
  1036. HeapLargestAddress = (ULONGLONG)-1;
  1037. Is64BitArchitecture = TRUE;
  1038. }
  1039. return TRUE;
  1040. }