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.

1502 lines
36 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. HeapEntrySize = GetTypeSize("ntdll!_HEAP_ENTRY");
  359. LookasideSize = GetTypeSize("ntdll!_HEAP_LOOKASIDE");
  360. for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i++) {
  361. if (ScanLevel < SCANHEAP) {
  362. return FALSE;
  363. }
  364. GetFieldValue(LookasideAddress, "ntdll!_HEAP_LOOKASIDE", "ListHead.Next", Next);
  365. if (Is64BitArchitecture) {
  366. Next <<= 3;
  367. }
  368. LoopCount = 0;
  369. while (Next) {
  370. if (++LoopCount >= LoopLimit) {
  371. dprintf("Walking the lookaside block list index %ld exceeded the %ld limit\n", i, LoopLimit);
  372. break;
  373. }
  374. (*HeapCallback)( CONTEXT_LOOKASIDE_BLOCK,
  375. HeapAddress,
  376. 0,
  377. Next - HeapEntrySize,
  378. i*HeapEntrySize
  379. );
  380. if (!ReadPointer(Next, &Next)) {
  381. (*HeapCallback)( CONTEXT_ERROR,
  382. HeapAddress,
  383. 0,
  384. Next,
  385. (ULONG64)(&"Unable to read lookaside block\n")
  386. );
  387. break;
  388. }
  389. }
  390. LookasideAddress += LookasideSize;
  391. }
  392. }
  393. if (LFHAddress) {
  394. (*HeapCallback)( CONTEXT_LFH_HEAP,
  395. HeapAddress,
  396. LFHAddress,
  397. 0,
  398. 0
  399. );
  400. }
  401. (*HeapCallback)( CONTEXT_END_HEAP,
  402. HeapAddress,
  403. 0,
  404. 0,
  405. 0
  406. );
  407. return TRUE;
  408. }
  409. void
  410. ScanProcessHeaps(
  411. IN ULONG64 AddressToDump,
  412. IN ULONG64 ProcessPeb,
  413. HEAP_ITERATOR_CALLBACK HeapCallback
  414. )
  415. {
  416. ULONG NumberOfHeaps;
  417. ULONG64 pHeapsList;
  418. ULONG64 * Heaps;
  419. ULONG PtrSize;
  420. ULONG HeapNumber;
  421. if (AddressToDump) {
  422. ReadHeapData ( AddressToDump, HeapCallback);
  423. return;
  424. }
  425. if (!(*HeapCallback)( CONTEXT_START_GLOBALS,
  426. 0,
  427. 0,
  428. 0,
  429. ProcessPeb
  430. )) {
  431. return;
  432. }
  433. ScanLevel = SCANPROCESS;
  434. GetFieldValue(ProcessPeb, "ntdll!_PEB", "NumberOfHeaps", NumberOfHeaps);
  435. GetFieldValue(ProcessPeb, "ntdll!_PEB", "ProcessHeaps", pHeapsList);
  436. if (NumberOfHeaps == 0) {
  437. dprintf( "No heaps to display.\n" );
  438. return;
  439. }
  440. if (!pHeapsList) {
  441. dprintf( "Unable to get address of ProcessHeaps array\n" );
  442. return;
  443. }
  444. Heaps = malloc( NumberOfHeaps * sizeof(ULONG64) );
  445. if (!Heaps) {
  446. dprintf( "Unable to allocate memory to hold ProcessHeaps array\n" );
  447. return;
  448. }
  449. PtrSize = IsPtr64() ? 8 : 4;
  450. for (HeapNumber=0; HeapNumber<NumberOfHeaps ; HeapNumber++) {
  451. if (!ReadPointer( pHeapsList + HeapNumber*PtrSize,
  452. &Heaps[HeapNumber] ) ) {
  453. dprintf( "%08p: Unable to read ProcessHeaps array\n", pHeapsList );
  454. free(Heaps);
  455. return;
  456. }
  457. }
  458. for ( HeapNumber = 0; HeapNumber < NumberOfHeaps; HeapNumber++ ) {
  459. if (ScanLevel < SCANPROCESS) {
  460. free(Heaps);
  461. return;
  462. }
  463. if ((AddressToDump == 0)
  464. ||
  465. (AddressToDump == Heaps[HeapNumber])) {
  466. ReadHeapData ( Heaps[HeapNumber], HeapCallback);
  467. }
  468. }
  469. free(Heaps);
  470. }
  471. //
  472. // Allocation routines
  473. //
  474. HANDLE TempHeap;
  475. #define AllocateBlock(Size) HeapAlloc(TempHeap, 0, Size)
  476. #define FreeBlock(P) HeapFree(TempHeap, 0, P)
  477. //
  478. // Leak detector code
  479. //
  480. typedef enum _USAGE_TYPE {
  481. UsageUnknown,
  482. UsageModule,
  483. UsageHeap,
  484. UsageOther
  485. } USAGE_TYPE;
  486. typedef struct _HEAP_BLOCK {
  487. LIST_ENTRY Entry;
  488. ULONG64 BlockAddress;
  489. ULONG64 Size;
  490. LONG Count;
  491. } HEAP_BLOCK, *PHEAP_BLOCK;
  492. typedef struct _BLOCK_DESCR {
  493. USAGE_TYPE Type;
  494. ULONG64 Heap;
  495. LONG Count;
  496. HEAP_BLOCK Blocks[1];
  497. }BLOCK_DESCR, *PBLOCK_DESCR;
  498. typedef struct _MEMORY_MAP {
  499. ULONG64 Granularity;
  500. ULONG64 Offset;
  501. ULONG64 MaxAddress;
  502. CHAR FlagsBitmap[256 / 8];
  503. union{
  504. struct _MEMORY_MAP * Details[ 256 ];
  505. PBLOCK_DESCR Usage[ 256 ];
  506. };
  507. struct _MEMORY_MAP * Parent;
  508. } MEMORY_MAP, *PMEMORY_MAP;
  509. MEMORY_MAP ProcessMemory;
  510. ULONG LeaksCount = 0;
  511. ULONG64 PreviousPage = 0;
  512. ULONG64 CrtPage = 0;
  513. LONG NumBlocks = 0;
  514. PHEAP_BLOCK TempBlocks;
  515. ULONG64 LastHeapAddress = 0;
  516. ULONG64 RtlpPreviousStartAddress = 0;
  517. LIST_ENTRY HeapBusyList;
  518. LIST_ENTRY HeapLeakList;
  519. void InitializeMap(PMEMORY_MAP MemMap, PMEMORY_MAP Parent)
  520. {
  521. memset(MemMap, 0, sizeof(*MemMap));
  522. MemMap->Parent = Parent;
  523. if (Parent) {
  524. MemMap->Granularity = Parent->Granularity / 256;
  525. }
  526. }
  527. void
  528. SetBlockInfo(PMEMORY_MAP MemMap, ULONG64 Base, ULONG64 Size, PBLOCK_DESCR BlockDescr)
  529. {
  530. ULONG64 Start, End;
  531. ULONG64 i;
  532. if (((Base + Size - 1) < MemMap->Offset) ||
  533. (Base > MemMap->MaxAddress)
  534. ) {
  535. return;
  536. }
  537. if (Base > MemMap->Offset) {
  538. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  539. } else {
  540. Start = 0;
  541. }
  542. End = (Base - MemMap->Offset + Size - 1) / MemMap->Granularity;
  543. if (End > 255) {
  544. End = 255;
  545. }
  546. for (i = Start; i <= End; i++) {
  547. if (MemMap->Granularity == PageSize) {
  548. if (BlockDescr) {
  549. if (MemMap->Usage[i] != NULL) {
  550. if (MemMap->Usage[i] != BlockDescr) {
  551. dprintf("Error\n");
  552. }
  553. }
  554. MemMap->Usage[i] = BlockDescr;
  555. } else {
  556. MemMap->FlagsBitmap[i / 8] |= 1 << (i % 8);
  557. }
  558. } else {
  559. if (!MemMap->Details[i]) {
  560. MemMap->Details[i] = AllocateBlock(sizeof(*MemMap));
  561. if (!MemMap->Details[i]) {
  562. dprintf("Error allocate\n");
  563. return;
  564. }
  565. InitializeMap(MemMap->Details[i], MemMap);
  566. MemMap->Details[i]->Offset = MemMap->Offset + MemMap->Granularity * i;
  567. MemMap->Details[i]->MaxAddress = MemMap->Offset + MemMap->Granularity * (i+1) - 1;
  568. }
  569. SetBlockInfo(MemMap->Details[i], Base, Size, BlockDescr);
  570. }
  571. }
  572. }
  573. PBLOCK_DESCR
  574. GetBlockInfo(PMEMORY_MAP MemMap, ULONG64 Base)
  575. {
  576. ULONG64 Start;
  577. PBLOCK_DESCR BlockDescr = NULL;
  578. if ((Base < MemMap->Offset) ||
  579. (Base > MemMap->MaxAddress)
  580. ) {
  581. return NULL;
  582. }
  583. if (Base > MemMap->Offset) {
  584. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  585. } else {
  586. Start = 0;
  587. }
  588. if (MemMap->Granularity == PageSize) {
  589. return MemMap->Usage[Start];
  590. } else {
  591. if (MemMap->Details[Start]) {
  592. return GetBlockInfo(MemMap->Details[Start], Base);
  593. }
  594. }
  595. return NULL;
  596. }
  597. BOOLEAN
  598. GetFlag(PMEMORY_MAP MemMap, ULONG64 Base)
  599. {
  600. ULONG64 Start;
  601. PBLOCK_DESCR BlockDescr = NULL;
  602. /*
  603. dprintf("GetFlag %p %p %p\n",
  604. MemMap->Offset,
  605. MemMap->MaxAddress,
  606. MemMap->Granularity
  607. );
  608. */
  609. if ((Base < MemMap->Offset) ||
  610. (Base > MemMap->MaxAddress)
  611. ) {
  612. return FALSE;
  613. }
  614. if (Base > MemMap->Offset) {
  615. Start = (Base - MemMap->Offset) / MemMap->Granularity;
  616. } else {
  617. Start = 0;
  618. }
  619. if (MemMap->Granularity == PageSize) {
  620. ULONG Flag = (MemMap->FlagsBitmap[Start / 8] & (1 << (Start % 8))) != 0;
  621. return (MemMap->FlagsBitmap[Start / 8] & (1 << (Start % 8))) != 0;
  622. } else {
  623. if (MemMap->Details[Start]) {
  624. return GetFlag(MemMap->Details[Start], Base);
  625. }
  626. }
  627. return FALSE;
  628. }
  629. void InitializeSystem()
  630. {
  631. ULONG64 AddressRange = PageSize;
  632. ULONG64 PreviousAddressRange = PageSize;
  633. InitializeMap(&ProcessMemory, NULL);
  634. InitializeListHead( &HeapBusyList );
  635. InitializeListHead( &HeapLeakList );
  636. while (TRUE) {
  637. AddressRange = AddressRange * 256;
  638. if ((AddressRange < PreviousAddressRange)
  639. ||
  640. (AddressRange > HeapLargestAddress)
  641. ) {
  642. ProcessMemory.MaxAddress = HeapLargestAddress;
  643. ProcessMemory.Granularity = PreviousAddressRange;
  644. break;
  645. }
  646. PreviousAddressRange = AddressRange;
  647. }
  648. TempBlocks = AllocateBlock(PageSize);
  649. if (TempBlocks == NULL) {
  650. dprintf("Cannot allocate temp buffer\n");
  651. }
  652. }
  653. BOOLEAN
  654. PushPageDescriptor(ULONG64 Page, ULONG64 NumPages)
  655. {
  656. PBLOCK_DESCR PBlockDescr;
  657. PBLOCK_DESCR PreviousDescr;
  658. LONG i;
  659. PreviousDescr = GetBlockInfo(&ProcessMemory, Page * PageSize);
  660. if (PreviousDescr) {
  661. dprintf("Conflicting descriptors %08lx\n", PreviousDescr);
  662. return FALSE;
  663. }
  664. PBlockDescr = (PBLOCK_DESCR)AllocateBlock(sizeof(BLOCK_DESCR) + (NumBlocks - 1) * sizeof(HEAP_BLOCK));
  665. if (!PBlockDescr) {
  666. dprintf("Unable to allocate page descriptor\n");
  667. return FALSE;
  668. }
  669. PBlockDescr->Type = UsageHeap;
  670. PBlockDescr->Count = NumBlocks;
  671. PBlockDescr->Heap = LastHeapAddress;
  672. memcpy(PBlockDescr->Blocks, TempBlocks, NumBlocks * sizeof(HEAP_BLOCK));
  673. for (i = 0; i < NumBlocks; i++) {
  674. InitializeListHead( &PBlockDescr->Blocks[i].Entry );
  675. if (PBlockDescr->Blocks[i].BlockAddress != RtlpPreviousStartAddress) {
  676. InsertTailList(&HeapLeakList, &PBlockDescr->Blocks[i].Entry);
  677. PBlockDescr->Blocks[i].Count = 0;
  678. RtlpPreviousStartAddress = PBlockDescr->Blocks[i].BlockAddress;
  679. }
  680. }
  681. SetBlockInfo(&ProcessMemory, Page * PageSize, NumPages * PageSize, PBlockDescr);
  682. return TRUE;
  683. }
  684. BOOLEAN RegisterHeapBlocks(
  685. IN ULONG Context,
  686. IN ULONG64 HeapAddress,
  687. IN ULONG64 SegmentAddress,
  688. IN ULONG64 EntryAddress,
  689. IN ULONG64 Data
  690. )
  691. {
  692. if (Context == CONTEXT_START_HEAP) {
  693. dprintf("Heap %p\n", HeapAddress);
  694. LastHeapAddress = HeapAddress;
  695. return TRUE;
  696. }
  697. if (Context == CONTEXT_START_SEGMENT) {
  698. ULONG64 NumberOfPages;
  699. ULONG64 SegmentBaseAddress;
  700. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfPages", NumberOfPages);
  701. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "BaseAddress", SegmentBaseAddress);
  702. SetBlockInfo(&ProcessMemory, SegmentBaseAddress, NumberOfPages * PageSize, NULL);
  703. return TRUE;
  704. }
  705. if (Context == CONTEXT_ERROR) {
  706. dprintf("HEAP %p (Seg %p) At %p Error: %s\n",
  707. HeapAddress,
  708. SegmentAddress,
  709. EntryAddress,
  710. (LPSTR) (ULONG_PTR) Data
  711. );
  712. return TRUE;
  713. }
  714. if (Context == CONTEXT_END_BLOCKS) {
  715. if (PreviousPage) {
  716. PushPageDescriptor(PreviousPage, 1);
  717. }
  718. PreviousPage = 0;
  719. NumBlocks = 0;
  720. } else if (Context == CONTEXT_BUSY_BLOCK) {
  721. ULONG EntrySize;
  722. ULONG64 EndPage;
  723. EntrySize = (ULONG)Data;
  724. EndPage = (EntryAddress + (EntrySize - 1)) / PageSize;
  725. if (!GetFlag(&ProcessMemory, EntryAddress)) {
  726. dprintf("CONTEXT_BUSY_BLOCK %p address isn't from the heap\n", EntryAddress);
  727. }
  728. CrtPage = (EntryAddress) / PageSize;
  729. if (CrtPage != PreviousPage) {
  730. if (PreviousPage) {
  731. PushPageDescriptor(PreviousPage, 1);
  732. }
  733. PreviousPage = CrtPage;
  734. NumBlocks = 0;
  735. }
  736. TempBlocks[NumBlocks].BlockAddress = EntryAddress;
  737. TempBlocks[NumBlocks].Count = 0;
  738. TempBlocks[NumBlocks].Size = EntrySize;
  739. NumBlocks++;
  740. if (EndPage != CrtPage) {
  741. PushPageDescriptor(CrtPage, 1);
  742. NumBlocks = 0;
  743. TempBlocks[NumBlocks].BlockAddress = (ULONG_PTR)EntryAddress;
  744. TempBlocks[NumBlocks].Count = 0;
  745. TempBlocks[NumBlocks].Size = EntrySize;
  746. NumBlocks = 1;
  747. if (EndPage - CrtPage > 1) {
  748. PushPageDescriptor(CrtPage + 1, EndPage - CrtPage - 1);
  749. }
  750. PreviousPage = EndPage;
  751. }
  752. } else if (Context == CONTEXT_VIRTUAL_BLOCK) {
  753. ULONG64 EndPage;
  754. EndPage = (EntryAddress + Data - 1) / PageSize;
  755. CrtPage = (EntryAddress) / PageSize;
  756. if (CrtPage != PreviousPage) {
  757. if (PreviousPage) {
  758. PushPageDescriptor(PreviousPage, 1);
  759. }
  760. PreviousPage = CrtPage;
  761. NumBlocks = 0;
  762. } else {
  763. dprintf("Error in large block address\n");
  764. }
  765. TempBlocks[NumBlocks].BlockAddress = EntryAddress;
  766. TempBlocks[NumBlocks].Count = 0;
  767. TempBlocks[NumBlocks].Size = Data * HeapEntrySize;
  768. NumBlocks++;
  769. PushPageDescriptor(CrtPage, EndPage - CrtPage + 1);
  770. PreviousPage = 0;
  771. } else if ( Context == CONTEXT_LOOKASIDE_BLOCK ) {
  772. PBLOCK_DESCR PBlockDescr;
  773. LONG i;
  774. if (!GetFlag(&ProcessMemory, EntryAddress)) {
  775. dprintf("CONTEXT_LOOKASIDE_BLOCK %p address isn't from the heap\n", EntryAddress);
  776. }
  777. PBlockDescr = GetBlockInfo(&ProcessMemory, EntryAddress);
  778. if (!PBlockDescr) {
  779. dprintf("Error finding block from lookaside %p\n", EntryAddress);
  780. return FALSE;
  781. }
  782. for (i = 0; i < PBlockDescr->Count; i++) {
  783. if ((PBlockDescr->Blocks[i].BlockAddress <= (ULONG_PTR)EntryAddress) &&
  784. (PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > (ULONG_PTR)EntryAddress)) {
  785. PBlockDescr->Blocks[i].Count = -10000;
  786. RemoveEntryList(&PBlockDescr->Blocks[i].Entry);
  787. return TRUE;
  788. }
  789. }
  790. dprintf("Error, block %p from lookaside not found in allocated block list\n", EntryAddress);
  791. }
  792. return TRUE;
  793. }
  794. PHEAP_BLOCK
  795. GetHeapBlock(ULONG64 Address)
  796. {
  797. PBLOCK_DESCR PBlockDescr;
  798. LONG i;
  799. PBlockDescr = GetBlockInfo(&ProcessMemory, Address);
  800. if (PBlockDescr) {
  801. for (i = 0; i < PBlockDescr->Count; i++) {
  802. if ((PBlockDescr->Blocks[i].BlockAddress <= Address) &&
  803. (PBlockDescr->Blocks[i].BlockAddress + PBlockDescr->Blocks[i].Size > Address)) {
  804. if (PBlockDescr->Blocks[i].BlockAddress != Address) {
  805. return GetHeapBlock(PBlockDescr->Blocks[i].BlockAddress);
  806. }
  807. return &(PBlockDescr->Blocks[i]);
  808. }
  809. }
  810. }
  811. return NULL;
  812. }
  813. BOOLEAN
  814. ScanHeapAllocBlocks()
  815. {
  816. PLIST_ENTRY Next;
  817. Next = HeapBusyList.Flink;
  818. while (Next != &HeapBusyList) {
  819. PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
  820. PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + HeapEntrySize);
  821. Next = Next->Flink;
  822. while ((ULONG_PTR)CrtAddress < Block->BlockAddress + Block->Size) {
  823. ULONG_PTR Pointer;
  824. if (ReadMemory( (ULONG64)(CrtAddress),
  825. &Pointer,
  826. sizeof(Pointer),
  827. NULL
  828. )) {
  829. PHEAP_BLOCK pBlock = GetHeapBlock( Pointer );
  830. if (pBlock) {
  831. //
  832. // We found a block. we increment then the reference count
  833. //
  834. if (pBlock->Count == 0) {
  835. RemoveEntryList(&pBlock->Entry);
  836. InsertTailList(&HeapBusyList, &pBlock->Entry);
  837. }
  838. pBlock->Count += 1;
  839. }
  840. }
  841. //
  842. // Go to the next possible pointer
  843. //
  844. CrtAddress++;
  845. }
  846. }
  847. Next = HeapLeakList.Flink;
  848. while (Next != &HeapLeakList) {
  849. PHEAP_BLOCK Block = CONTAINING_RECORD(Next, HEAP_BLOCK, Entry);
  850. PBLOCK_DESCR PBlockDescr = GetBlockInfo( &ProcessMemory, Block->BlockAddress );
  851. PULONG_PTR CrtAddress = (PULONG_PTR)(Block->BlockAddress + HeapEntrySize);
  852. //
  853. // First time we need to display the header
  854. //
  855. if (LeaksCount == 0) {
  856. dprintf("\n");
  857. DumpEntryHeader();
  858. }
  859. //
  860. // Display the information for this block
  861. //
  862. if (PBlockDescr)
  863. {
  864. DumpEntryInfo(PBlockDescr->Heap, 0, Block->BlockAddress);
  865. }
  866. LeaksCount += 1;
  867. //
  868. // Go to the next item from the leak list
  869. //
  870. Next = Next->Flink;
  871. }
  872. return TRUE;
  873. }
  874. BOOLEAN
  875. ScanProcessVM (
  876. HANDLE hProcess
  877. )
  878. {
  879. NTSTATUS Status;
  880. SIZE_T BufferLen;
  881. ULONG_PTR lpAddress = 0;
  882. MEMORY_BASIC_INFORMATION Buffer;
  883. PVOID MemoryBuffer;
  884. if ( hProcess ) {
  885. PROCESS_BASIC_INFORMATION BasicInfo;
  886. dprintf("Scanning VM ...");
  887. Status = NtQueryInformationProcess(
  888. hProcess,
  889. ProcessBasicInformation,
  890. &BasicInfo,
  891. sizeof(BasicInfo),
  892. NULL
  893. );
  894. // dprintf("PEB %p\n", BasicInfo.PebBaseAddress);
  895. MemoryBuffer = AllocateBlock(PageSize);
  896. if (!MemoryBuffer) {
  897. return FALSE;
  898. }
  899. BufferLen = sizeof(Buffer);
  900. while (BufferLen) {
  901. BufferLen = VirtualQueryEx( hProcess,
  902. (LPVOID)lpAddress,
  903. &Buffer,
  904. sizeof(Buffer)
  905. );
  906. if (BufferLen) {
  907. if (( Buffer.AllocationProtect &
  908. (PAGE_READWRITE | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY))
  909. ) {
  910. ULONG64 NumPages;
  911. ULONG i, j;
  912. NumPages = Buffer.RegionSize / PageSize;
  913. for (i = 0; i < NumPages; i++) {
  914. if (ReadMemory( (ULONG64)(lpAddress + i * PageSize),
  915. MemoryBuffer,
  916. PageSize,
  917. NULL )
  918. &&
  919. !GetFlag(&ProcessMemory, lpAddress)
  920. ) {
  921. ULONG_PTR * Pointers = (ULONG_PTR *)MemoryBuffer;
  922. for (j = 0; j < PageSize/sizeof(ULONG_PTR); j++) {
  923. ULONG_PTR Address = lpAddress + i * PageSize + j * sizeof(ULONG_PTR);
  924. PHEAP_BLOCK pBlock = GetHeapBlock(*Pointers);
  925. if (pBlock) {
  926. if (pBlock->Count == 0) {
  927. RemoveEntryList(&pBlock->Entry);
  928. InsertTailList(&HeapBusyList, &pBlock->Entry);
  929. }
  930. pBlock->Count += 1;
  931. }
  932. Pointers += 1;
  933. }
  934. }
  935. if (CheckControlC()) {
  936. FreeBlock(MemoryBuffer);
  937. ScanLevel = 0;
  938. return FALSE;
  939. }
  940. }
  941. }
  942. lpAddress += Buffer.RegionSize;
  943. }
  944. }
  945. //
  946. // First scan will mark all used blocks
  947. //
  948. ScanHeapAllocBlocks();
  949. FreeBlock(MemoryBuffer);
  950. }
  951. return TRUE;
  952. }
  953. void InspectLeaks(
  954. IN ULONG64 AddressToDump,
  955. IN ULONG64 ProcessPeb
  956. )
  957. {
  958. HANDLE hProcess;
  959. LeaksCount = 0;
  960. InitializeSystem();
  961. if (TempBlocks) {
  962. ScanProcessHeaps( 0,
  963. ProcessPeb,
  964. RegisterHeapBlocks
  965. );
  966. GetCurrentProcessHandle( &hProcess );
  967. if (hProcess){
  968. ScanProcessVM(hProcess);
  969. if (LeaksCount) {
  970. dprintf("%ld leaks detected.\n", LeaksCount);
  971. } else {
  972. dprintf( "No leaks detected.\n");
  973. }
  974. } else {
  975. dprintf("Unable to get the process handle\n");
  976. }
  977. }
  978. }
  979. VOID
  980. HeapDetectLeaks()
  981. {
  982. ULONG64 Process;
  983. ULONG64 ThePeb;
  984. ULONG64 PageHeapAddress;
  985. BOOLEAN PageHeapEnabled = FALSE;
  986. ULONG PageHeapFlags = 0;
  987. if (!InitializeHeapExtension()) {
  988. return;
  989. }
  990. //
  991. // Return immediately if full page heap is enabled
  992. //
  993. PageHeapAddress = GetExpression ("ntdll!RtlpDebugPageHeap");
  994. ReadMemory (PageHeapAddress,
  995. &PageHeapEnabled,
  996. sizeof (BOOLEAN),
  997. NULL);
  998. PageHeapAddress = GetExpression ("ntdll!RtlpDphGlobalFlags");
  999. ReadMemory (PageHeapAddress,
  1000. &PageHeapFlags,
  1001. sizeof (ULONG),
  1002. NULL);
  1003. if (PageHeapEnabled == TRUE && (PageHeapFlags & 0x01)) {
  1004. dprintf ("!heap -l does not work if full page heap is enabled for the process \n");
  1005. return;
  1006. }
  1007. GetPebAddress( 0, &ThePeb);
  1008. TempHeap = HeapCreate(HEAP_NO_SERIALIZE | HEAP_GROWABLE, 0, 0);
  1009. if (!TempHeap) {
  1010. dprintf("Unable to create temporary heap\n");
  1011. return;
  1012. }
  1013. InspectLeaks( 0, ThePeb);
  1014. HeapDestroy(TempHeap);
  1015. TempHeap = NULL;
  1016. }
  1017. BOOLEAN
  1018. InitializeHeapExtension()
  1019. {
  1020. PointerSize = IsPtr64() ? 8 : 4;
  1021. HeapEntrySize = GetTypeSize("ntdll!_HEAP_ENTRY");
  1022. if ((HeapEntrySize == 0)
  1023. ||
  1024. (PointerSize == 0)) {
  1025. dprintf("Invalid type information\n");
  1026. return FALSE;
  1027. }
  1028. //
  1029. // Issue adrmarin 04/28/00: The page size should be available in the new interface
  1030. // IDebugControl::GetPageSize
  1031. //
  1032. if (PointerSize == 4) {
  1033. PageSize = 0x1000;
  1034. HeapLargestAddress = (ULONG)-1;
  1035. Is64BitArchitecture = FALSE;
  1036. } else {
  1037. PageSize = 0x2000;
  1038. HeapLargestAddress = (ULONGLONG)-1;
  1039. Is64BitArchitecture = TRUE;
  1040. }
  1041. return TRUE;
  1042. }