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.

1494 lines
39 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. ULONG64 AddressToFind;
  17. ULONG64 HeapAddressFind;
  18. ULONG64 SegmentAddressFind;
  19. ULONG64 HeapEntryFind;
  20. ULONG64 HeapEntryFindSize;
  21. BOOLEAN Lookaside;
  22. BOOLEAN VerifyBlocks;
  23. ULONG64 DumpOptions = 0xffffff;
  24. #define HEAP_DUMP_FREE_LISTS 1
  25. #define HEAP_DUMP_GFLAGS 2
  26. #define HEAP_DUMP_GTAGS 4
  27. #define HEAP_DUMP_FREE_LISTS_DETAILS 8
  28. BOOLEAN ScanVM;
  29. ULONG
  30. GetFieldSize (
  31. IN LPCSTR Type,
  32. IN LPCSTR Field
  33. )
  34. {
  35. FIELD_INFO flds = {(PUCHAR)Field, NULL, 0, DBG_DUMP_FIELD_FULL_NAME, 0, NULL};
  36. SYM_DUMP_PARAM Sym = {
  37. sizeof (SYM_DUMP_PARAM), (PUCHAR)Type, DBG_DUMP_NO_PRINT, 0,
  38. NULL, NULL, NULL, 1, &flds
  39. };
  40. ULONG RetVal;
  41. RetVal = Ioctl( IG_DUMP_SYMBOL_INFO, &Sym, Sym.size );
  42. return flds.size;
  43. }
  44. ULONG
  45. ReadLongValue(LPTSTR Symbol)
  46. {
  47. ULONG64 Address;
  48. ULONG Value = 0;
  49. Address = GetExpression( Symbol );
  50. if ( (Address == 0) ||
  51. !ReadMemory( Address, &Value, sizeof( Value ), NULL )) {
  52. dprintf( "HEAPEXT: Unable to read %s\n", Symbol );
  53. }
  54. return Value;
  55. }
  56. #define CheckAndPrintFlag(x)\
  57. if (Flags & (x)) dprintf(#x" ");
  58. void
  59. DumpFlagDescription(ULONG Flags)
  60. {
  61. CheckAndPrintFlag(HEAP_NO_SERIALIZE);
  62. CheckAndPrintFlag(HEAP_GROWABLE);
  63. CheckAndPrintFlag(HEAP_GENERATE_EXCEPTIONS);
  64. CheckAndPrintFlag(HEAP_ZERO_MEMORY);
  65. CheckAndPrintFlag(HEAP_REALLOC_IN_PLACE_ONLY);
  66. CheckAndPrintFlag(HEAP_TAIL_CHECKING_ENABLED);
  67. CheckAndPrintFlag(HEAP_FREE_CHECKING_ENABLED);
  68. CheckAndPrintFlag(HEAP_DISABLE_COALESCE_ON_FREE);
  69. CheckAndPrintFlag(HEAP_CREATE_ALIGN_16);
  70. CheckAndPrintFlag(HEAP_CREATE_ENABLE_TRACING);
  71. }
  72. void
  73. DumpEntryFlagDescription(ULONG Flags)
  74. {
  75. if (Flags & HEAP_ENTRY_BUSY) dprintf("busy "); else dprintf("free ");
  76. if (Flags & HEAP_ENTRY_EXTRA_PRESENT) dprintf("extra ");
  77. if (Flags & HEAP_ENTRY_FILL_PATTERN) dprintf("fill ");
  78. if (Flags & HEAP_ENTRY_VIRTUAL_ALLOC) dprintf("virtual ");
  79. if (Flags & HEAP_ENTRY_LAST_ENTRY) dprintf("last ");
  80. if (Flags & HEAP_ENTRY_SETTABLE_FLAGS) dprintf("user_flag ");
  81. }
  82. void
  83. DumpEntryHeader()
  84. {
  85. dprintf("Entry User Heap Segment Size PrevSize Flags\n");
  86. dprintf("----------------------------------------------------------------------\n");
  87. }
  88. void
  89. DumpEntryInfo(
  90. IN ULONG64 HeapAddress,
  91. IN ULONG64 SegmentAddress,
  92. ULONG64 EntryAddress
  93. )
  94. {
  95. ULONG SizeOfEntry;
  96. ULONG PreviousSize;
  97. ULONG Flags;
  98. ULONG Size;
  99. UCHAR SegmentIndex;
  100. UCHAR SmallTagIndex;
  101. SizeOfEntry = GetTypeSize("_HEAP_ENTRY"); // same as granularity
  102. InitTypeRead(EntryAddress, _HEAP_ENTRY);
  103. PreviousSize = (ULONG)ReadField(PreviousSize) * SizeOfEntry;
  104. Size = (ULONG) ReadField(Size) * SizeOfEntry;
  105. Flags = (ULONG) ReadField(Flags);
  106. SegmentIndex = (UCHAR) ReadField(SegmentIndex);
  107. SmallTagIndex = (UCHAR) ReadField(SmallTagIndex);
  108. if (SegmentIndex != 0xff) {
  109. dprintf("%p %p %p %p %8lx %8lx ",
  110. EntryAddress,
  111. EntryAddress + SizeOfEntry,
  112. HeapAddress,
  113. SegmentAddress,
  114. Size,
  115. PreviousSize
  116. );
  117. DumpEntryFlagDescription(Flags);
  118. if (Lookaside) {
  119. dprintf(" - lookaside ");
  120. }
  121. } else {
  122. ULONG64 SubSegment = ReadField(SubSegment);
  123. ULONG64 BlockSize;
  124. GetFieldValue(SubSegment, "ntdll!_HEAP_SUBSEGMENT", "BlockSize", BlockSize);
  125. Size = (ULONG)BlockSize * SizeOfEntry;
  126. dprintf("%p %p %p %p %8lx - ",
  127. EntryAddress,
  128. EntryAddress + SizeOfEntry,
  129. HeapAddress,
  130. SegmentAddress,
  131. Size
  132. );
  133. if (SmallTagIndex) {
  134. dprintf("LFH_BUSY; ");
  135. } else {
  136. dprintf("LFH_FREE; ");
  137. }
  138. DumpEntryFlagDescription(Flags);
  139. }
  140. dprintf("\n");
  141. }
  142. void
  143. DumpHeapStructure (ULONG64 HeapAddress)
  144. {
  145. ULONG AlignRound, Offset;
  146. if (InitTypeRead(HeapAddress, _HEAP)) {
  147. return;
  148. }
  149. GetFieldOffset("_HEAP", "VirtualAllocdBlocks", &Offset);
  150. AlignRound = (ULONG)ReadField(AlignRound) - GetTypeSize( "_HEAP_ENTRY" );
  151. if ((ULONG)ReadField(Flags) & HEAP_TAIL_CHECKING_ENABLED) {
  152. AlignRound -= CHECK_HEAP_TAIL_SIZE;
  153. }
  154. AlignRound += 1;
  155. dprintf( " Flags: %08x ", (ULONG)ReadField(Flags) );
  156. DumpFlagDescription((ULONG)ReadField(Flags)); dprintf("\n");
  157. dprintf( " ForceFlags: %08x ", (ULONG)ReadField(ForceFlags) );
  158. DumpFlagDescription((ULONG)ReadField(ForceFlags)); dprintf("\n");
  159. dprintf( " Granularity: %u bytes\n", AlignRound );
  160. dprintf( " Segment Reserve: %08x\n", (ULONG)ReadField(SegmentReserve));
  161. dprintf( " Segment Commit: %08x\n", (ULONG)ReadField(SegmentCommit) );
  162. dprintf( " DeCommit Block Thres:%08x\n", (ULONG)ReadField(DeCommitFreeBlockThreshold) );
  163. dprintf( " DeCommit Total Thres:%08x\n", (ULONG)ReadField(DeCommitTotalFreeThreshold) );
  164. dprintf( " Total Free Size: %08x\n", (ULONG)ReadField(TotalFreeSize) );
  165. dprintf( " Max. Allocation Size:%08x\n", (ULONG)ReadField(MaximumAllocationSize) );
  166. dprintf( " Lock Variable at: %p\n", ReadField(LockVariable) );
  167. dprintf( " Next TagIndex: %04x\n", (ULONG)ReadField(NextAvailableTagIndex) );
  168. dprintf( " Maximum TagIndex: %04x\n", (ULONG)ReadField(MaximumTagIndex) );
  169. dprintf( " Tag Entries: %08x\n", (ULONG)ReadField(TagEntries) );
  170. dprintf( " PsuedoTag Entries: %08x\n", (ULONG)ReadField(PseudoTagEntries) );
  171. dprintf( " Virtual Alloc List: %p\n", HeapAddress + Offset);
  172. if (DumpOptions & (HEAP_DUMP_FREE_LISTS | HEAP_DUMP_FREE_LISTS_DETAILS)) {
  173. ULONG i, ListSize, FreeListOffset;
  174. dprintf( " FreeList Usage: %08x %08x %08x %08x\n",
  175. (ULONG)ReadField(u.FreeListsInUseUlong[0]),
  176. (ULONG)ReadField(u.FreeListsInUseUlong[1]),
  177. (ULONG)ReadField(u.FreeListsInUseUlong[2]),
  178. (ULONG)ReadField(u.FreeListsInUseUlong[3])
  179. );
  180. GetFieldOffset ("_HEAP", "FreeLists", &Offset);
  181. ListSize = GetTypeSize("LIST_ENTRY");
  182. GetFieldOffset ("_HEAP_FREE_ENTRY", "FreeList", &FreeListOffset);
  183. for (i=0; i<HEAP_MAXIMUM_FREELISTS; i++) {
  184. ULONG64 Flink, Blink, Next;
  185. ULONG64 FreeListHead;
  186. ULONG Count = 0;
  187. ULONG MinSize = 0, MaxSize = 0;
  188. FreeListHead = HeapAddress + Offset + ListSize * i;
  189. GetFieldValue(FreeListHead, "LIST_ENTRY", "Flink", Flink);
  190. GetFieldValue(FreeListHead, "LIST_ENTRY", "Blink", Blink);
  191. if (Flink != FreeListHead) {
  192. dprintf( " FreeList[ %02x ] at %08p",
  193. i,
  194. FreeListHead
  195. );
  196. if (DumpOptions & HEAP_DUMP_FREE_LISTS_DETAILS) {
  197. dprintf("\n");
  198. }
  199. Next = Flink;
  200. while (Next != FreeListHead) {
  201. ULONG Size, PrevSize, Flags;
  202. ULONG64 FreeEntryAddress;
  203. FreeEntryAddress = Next - FreeListOffset;
  204. if (InitTypeRead ( FreeEntryAddress, _HEAP_FREE_ENTRY)) {
  205. if (Count) {
  206. dprintf( " Total: %ld blocks (%08lx, %08lx) * Error reading %p\n",
  207. Count,
  208. MinSize,
  209. MaxSize,
  210. FreeEntryAddress
  211. );
  212. } else {
  213. dprintf( " No blocks in list. Error reading %p\n",
  214. FreeEntryAddress
  215. );
  216. }
  217. break;
  218. }
  219. Size = (ULONG)ReadField(Size) * AlignRound;
  220. PrevSize = (ULONG)ReadField(PreviousSize) * AlignRound;
  221. Flags = (ULONG)ReadField(Flags);
  222. if (!Count) {
  223. MinSize = MaxSize = Size;
  224. } else {
  225. if (Size < MinSize) MinSize = Size;
  226. if (Size > MaxSize) MaxSize = Size;
  227. }
  228. if (DumpOptions & HEAP_DUMP_FREE_LISTS_DETAILS) {
  229. dprintf( " %08p: %05x . %05x [%02x] - ",
  230. FreeEntryAddress,
  231. PrevSize,
  232. Size,
  233. Flags
  234. );
  235. DumpEntryFlagDescription(Flags);
  236. dprintf("\n");
  237. }
  238. Count += 1;
  239. ReadPointer(Next, &Next);
  240. if (CheckControlC()) {
  241. return;
  242. }
  243. }
  244. if (Count) {
  245. if (DumpOptions & HEAP_DUMP_FREE_LISTS_DETAILS) {
  246. dprintf(" ");
  247. }
  248. dprintf( " * Total %ld block(s) (%08lx, %08lx)\n",
  249. Count,
  250. MinSize,
  251. MaxSize
  252. );
  253. }
  254. }
  255. }
  256. }
  257. }
  258. void
  259. DumpGlobals()
  260. {
  261. ULONG64 pNtGlobalFlag, NtGlobalFlag = 0;
  262. ULONG64 pNtTempGlobal, NtTempGlobal = 0;
  263. NtGlobalFlag = ReadLongValue("NTDLL!NtGlobalFlag");
  264. if ((NtGlobalFlag & (FLG_HEAP_ENABLE_TAIL_CHECK |
  265. FLG_HEAP_ENABLE_FREE_CHECK |
  266. FLG_HEAP_VALIDATE_PARAMETERS |
  267. FLG_HEAP_VALIDATE_ALL |
  268. FLG_HEAP_ENABLE_TAGGING |
  269. FLG_USER_STACK_TRACE_DB |
  270. FLG_HEAP_DISABLE_COALESCING )) != 0 ) {
  271. dprintf( "NtGlobalFlag enables following debugging aids for new heaps:" );
  272. if (NtGlobalFlag & FLG_HEAP_ENABLE_TAIL_CHECK) {
  273. dprintf( " tail checking\n" );
  274. }
  275. if (NtGlobalFlag & FLG_HEAP_ENABLE_FREE_CHECK) {
  276. dprintf( " free checking\n" );
  277. }
  278. if (NtGlobalFlag & FLG_HEAP_VALIDATE_PARAMETERS) {
  279. dprintf( " validate parameters\n" );
  280. }
  281. if (NtGlobalFlag & FLG_HEAP_VALIDATE_ALL) {
  282. dprintf( " validate on call\n" );
  283. }
  284. if (NtGlobalFlag & FLG_HEAP_ENABLE_TAGGING) {
  285. dprintf( " heap tagging\n" );
  286. }
  287. if (NtGlobalFlag & FLG_USER_STACK_TRACE_DB) {
  288. dprintf( " stack back traces\n" );
  289. }
  290. if (NtGlobalFlag & FLG_HEAP_DISABLE_COALESCING) {
  291. dprintf( " disable coalescing of free blocks\n" );
  292. }
  293. }
  294. NtTempGlobal = ReadLongValue( "NTDLL!RtlpDisableHeapLookaside" );
  295. if (NtTempGlobal) {
  296. dprintf( "The process has the following heap extended settings %08lx:\n", (ULONG)NtTempGlobal );
  297. if (NtTempGlobal & 1) {
  298. dprintf(" - Lookasides disabled\n");
  299. }
  300. if (NtTempGlobal & 2) {
  301. dprintf(" - Large blocks cache disabled\n");
  302. }
  303. if (NtTempGlobal & 8) {
  304. dprintf(" - Low Fragmentation Heap activated for all heaps\n");
  305. }
  306. dprintf("\n");
  307. }
  308. pNtTempGlobal = GetExpression( "NTDLL!RtlpAffinityState" );
  309. if ( pNtTempGlobal ) {
  310. ULONG64 TempValue;
  311. ULONG64 AffinitySwaps;
  312. ULONG64 AffinityResets;
  313. ULONG64 AffinityAllocs;
  314. GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "Limit", TempValue);
  315. if (TempValue) {
  316. dprintf("Affinity manager status:\n");
  317. dprintf(" - Virtual affinity limit %I64ld\n", TempValue);
  318. GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "CrtLimit", TempValue);
  319. dprintf(" - Current entries in use %ld\n", (LONG)TempValue);
  320. GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "AffinitySwaps", AffinitySwaps);
  321. GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "AffinityResets", AffinityResets);
  322. GetFieldValue(pNtTempGlobal, "ntdll!_AFFINITY_STATE", "AffinityAllocs", AffinityAllocs);
  323. dprintf(" - Statistics: Swaps=%I64ld, Resets=%I64ld, Allocs=%I64ld\n\n",
  324. AffinitySwaps,
  325. AffinityResets,
  326. AffinityAllocs
  327. );
  328. }
  329. }
  330. }
  331. BOOLEAN HeapFindRoutine(
  332. IN ULONG Context,
  333. IN ULONG64 HeapAddress,
  334. IN ULONG64 SegmentAddress,
  335. IN ULONG64 EntryAddress,
  336. IN ULONG64 Data
  337. )
  338. {
  339. switch (Context) {
  340. case CONTEXT_START_HEAP:
  341. //
  342. // we found a block, we won't need then to search in other heaps
  343. //
  344. if (HeapEntryFind) {
  345. ScanLevel = 0;
  346. }
  347. break;
  348. case CONTEXT_FREE_BLOCK:
  349. case CONTEXT_BUSY_BLOCK:
  350. case CONTEXT_LOOKASIDE_BLOCK:
  351. case CONTEXT_VIRTUAL_BLOCK:
  352. if ((AddressToFind >= EntryAddress) &&
  353. (AddressToFind < (EntryAddress + Data))) {
  354. if (HeapEntryFind == 0) {
  355. HeapAddressFind = HeapAddress;
  356. SegmentAddressFind = SegmentAddress;
  357. }
  358. if (Context == CONTEXT_LOOKASIDE_BLOCK) {
  359. Lookaside = TRUE;
  360. ScanLevel = 0;
  361. }
  362. HeapEntryFind = EntryAddress;
  363. HeapEntryFindSize = Data;
  364. }
  365. break;
  366. case CONTEXT_ERROR:
  367. dprintf("HEAP %p (Seg %p) At %p Error: %s\n",
  368. HeapAddress,
  369. SegmentAddress,
  370. EntryAddress,
  371. Data
  372. );
  373. break;
  374. }
  375. return TRUE;
  376. }
  377. BOOLEAN
  378. SearchVMReference (
  379. HANDLE hProcess,
  380. ULONG64 Base,
  381. ULONG64 EndAddress
  382. )
  383. {
  384. NTSTATUS Status;
  385. SIZE_T BufferLen;
  386. ULONG_PTR lpAddress = 0;
  387. MEMORY_BASIC_INFORMATION Buffer;
  388. PVOID MemoryBuffer;
  389. if ( hProcess ) {
  390. MemoryBuffer = malloc(PageSize);
  391. if (!MemoryBuffer) {
  392. dprintf("Not enough memory. Abording.\n");
  393. return FALSE;
  394. }
  395. dprintf("Search VM for address range %p - %p : ",Base, EndAddress);
  396. BufferLen = sizeof(Buffer);
  397. while (BufferLen) {
  398. BufferLen = VirtualQueryEx( hProcess,
  399. (LPVOID)lpAddress,
  400. &Buffer,
  401. sizeof(Buffer)
  402. );
  403. if (BufferLen) {
  404. if ((Buffer.AllocationProtect & (PAGE_READWRITE | PAGE_EXECUTE_READWRITE | PAGE_WRITECOPY | PAGE_EXECUTE_WRITECOPY)) /*&&
  405. (Buffer.Type == MEM_PRIVATE)*/) {
  406. ULONG64 NumPages;
  407. ULONG i, j;
  408. NumPages = Buffer.RegionSize / PageSize;
  409. for (i = 0; i < NumPages; i++) {
  410. if (ReadMemory( (ULONG64)(lpAddress + i * PageSize),
  411. MemoryBuffer,
  412. PageSize,
  413. NULL
  414. )) {
  415. ULONG_PTR * Pointers = (ULONG_PTR *)MemoryBuffer;
  416. for (j = 0; j < PageSize/sizeof(ULONG_PTR); j++) {
  417. ULONG_PTR Address = lpAddress + i * PageSize + j * sizeof(ULONG_PTR);
  418. if ((*Pointers >= Base) &&
  419. (*Pointers <= EndAddress)) {
  420. dprintf("%08lx (%08lx), ",
  421. Address,
  422. *Pointers
  423. );
  424. }
  425. Pointers += 1;
  426. }
  427. }
  428. }
  429. }
  430. lpAddress += Buffer.RegionSize;
  431. }
  432. }
  433. dprintf("\n");
  434. free(MemoryBuffer);
  435. }
  436. return TRUE;
  437. }
  438. VOID
  439. HeapStat(LPCTSTR szArguments);
  440. VOID
  441. HeapFindBlock(LPCTSTR szArguments)
  442. {
  443. ULONG64 Process;
  444. ULONG64 ThePeb;
  445. HANDLE hProcess;
  446. if (!InitializeHeapExtension()) {
  447. return;
  448. }
  449. HeapEntryFind = 0;
  450. HeapEntryFindSize = 0;
  451. HeapAddressFind = 0;
  452. SegmentAddressFind = 0;
  453. Lookaside = FALSE;
  454. AddressToFind = 0;
  455. GetPebAddress( 0, &ThePeb);
  456. GetCurrentProcessHandle( &hProcess );
  457. ScanVM = FALSE;
  458. {
  459. LPSTR p = (LPSTR)szArguments;
  460. while (p && *p) {
  461. if (*p == '-') {
  462. p++;
  463. if (toupper(*p) == 'V') {
  464. ScanVM = TRUE;
  465. }
  466. } else if (isxdigit(*p)) {
  467. sscanf( p, "%I64lx", &AddressToFind );
  468. while ((*p) && isxdigit(*p)) {
  469. p++;
  470. }
  471. continue;
  472. }
  473. p++;
  474. }
  475. }
  476. if (AddressToFind == 0) {
  477. dprintf("Syntax:\n!heap -x [-v] address\n");
  478. return;
  479. }
  480. ScanProcessHeaps( 0,
  481. ThePeb,
  482. HeapFindRoutine
  483. );
  484. if (HeapEntryFind) {
  485. DumpEntryHeader();
  486. DumpEntryInfo(HeapAddressFind, SegmentAddressFind, HeapEntryFind);
  487. dprintf("\n");
  488. if (ScanVM) {
  489. SearchVMReference(hProcess, HeapEntryFind, HeapEntryFind + HeapEntryFindSize - 1);
  490. }
  491. } else {
  492. if (ScanVM) {
  493. SearchVMReference(hProcess, AddressToFind, AddressToFind);
  494. }
  495. }
  496. }
  497. //
  498. // Heap stat implementation
  499. //
  500. typedef struct _SIZE_INFO {
  501. ULONG Busy;
  502. ULONG Free;
  503. ULONG FrontHeapAllocs;
  504. ULONG FrontHeapFrees;
  505. }SIZE_INFO, *PSIZE_INFO;
  506. typedef struct _HEAP_STATS {
  507. ULONG HeapIndex;
  508. ULONG64 HeapAddress;
  509. ULONG64 ReservedMemory;
  510. ULONG64 CommitedMemory;
  511. ULONG64 VirtualBytes;
  512. ULONG64 FreeSpace;
  513. ULONG Flags;
  514. ULONG FreeListLength;
  515. ULONG VirtualBlocks;
  516. ULONG UncommitedRanges;
  517. ULONG64 LockContention;
  518. ULONG FrontEndHeapType;
  519. ULONG64 FrontEndHeap;
  520. BOOLEAN ScanningSubSegment;
  521. }HEAP_STATS, *PHEAP_STATS;
  522. HEAP_STATS CrtHeapStat;
  523. PSIZE_INFO SizeInfo = NULL;
  524. ULONG BucketSize;
  525. ULONG LargestBucketIndex = 0;
  526. ULONG64 StatHeapAddress;
  527. ULONG DumpBlocksSize = 0;
  528. ULONG
  529. FASTCALL
  530. HeapStatSizeToSizeIndex(ULONG64 Size)
  531. {
  532. ULONG Index = (ULONG)(Size / BucketSize);
  533. if (Index >= LargestBucketIndex) {
  534. Index = LargestBucketIndex - 1;
  535. }
  536. return Index;
  537. }
  538. VOID
  539. HeapStatDumpBlocks()
  540. {
  541. ULONG Index;
  542. ULONG64 Busy = 0, Free = 0, FEBusy = 0, FEFree = 0;
  543. dprintf("\n Default heap Front heap \n");
  544. dprintf(" Range (bytes) Busy Free Busy Free \n");
  545. dprintf("----------------------------------------------- \n");
  546. if (SizeInfo == NULL) {
  547. return;
  548. }
  549. for ( Index = 0; Index < LargestBucketIndex; Index++ ) {
  550. if (SizeInfo[Index].Busy
  551. ||
  552. SizeInfo[Index].Free
  553. ||
  554. SizeInfo[Index].FrontHeapAllocs
  555. ||
  556. SizeInfo[Index].FrontHeapFrees) {
  557. dprintf("%6ld - %6ld %6ld %6ld %6ld %6ld\n",
  558. Index * BucketSize,
  559. (Index + 1) * BucketSize,
  560. SizeInfo[Index].Busy,
  561. SizeInfo[Index].Free,
  562. SizeInfo[Index].FrontHeapAllocs,
  563. SizeInfo[Index].FrontHeapFrees
  564. );
  565. FEBusy += SizeInfo[Index].FrontHeapAllocs;
  566. FEFree += SizeInfo[Index].FrontHeapFrees;
  567. Busy += SizeInfo[Index].Busy;
  568. Free += SizeInfo[Index].Free;
  569. }
  570. }
  571. dprintf("----------------------------------------------- \n");
  572. dprintf(" Total %6I64d %6I64d %6I64d %6I64d \n",
  573. Busy,
  574. Free,
  575. FEBusy,
  576. FEFree
  577. );
  578. }
  579. VOID
  580. DumpBlock (
  581. IN ULONG64 BlockAddress,
  582. IN UCHAR Flag
  583. )
  584. {
  585. UCHAR Buffer[33];
  586. PULONG pLongArray = (PULONG)Buffer;
  587. ULONG i;
  588. memchr(Buffer, '?', sizeof( Buffer ));
  589. if (!ReadMemory( BlockAddress, &Buffer, sizeof( Buffer ), NULL )) {
  590. dprintf("%p ?\n", BlockAddress);
  591. return;
  592. }
  593. dprintf("%p %c %08lx %08lx %08lx %08lx ",
  594. BlockAddress,
  595. Flag,
  596. pLongArray[0],
  597. pLongArray[1],
  598. pLongArray[2],
  599. pLongArray[3]
  600. );
  601. for (i = 0; i < 32; i++) {
  602. if (!isprint(Buffer[i])) {
  603. Buffer[i] = '.';
  604. }
  605. }
  606. Buffer[32] = 0;
  607. dprintf("%s\n", Buffer);
  608. }
  609. VOID
  610. CollectHeapInfo(
  611. ULONG64 HeapAddress
  612. )
  613. {
  614. ULONG64 TempValue;
  615. ULONG64 FrontEndHeapType;
  616. memset(&CrtHeapStat, 0, sizeof(CrtHeapStat));
  617. CrtHeapStat.HeapAddress = HeapAddress;
  618. GetFieldValue(HeapAddress, "ntdll!_HEAP", "TotalFreeSize", CrtHeapStat.FreeSpace);
  619. CrtHeapStat.FreeSpace *= HeapEntrySize;
  620. GetFieldValue(HeapAddress, "ntdll!_HEAP", "NonDedicatedListLength", CrtHeapStat.FreeListLength);
  621. GetFieldValue(HeapAddress, "ntdll!_HEAP", "Flags", TempValue);
  622. CrtHeapStat.Flags = (ULONG)TempValue;
  623. GetFieldValue(HeapAddress, "ntdll!_HEAP", "Signature", TempValue);
  624. if (TempValue != 0xeeffeeff) {
  625. dprintf("Error: Heap %p has an invalid signature %08lx\n", HeapAddress, 0xeeffeeff);
  626. }
  627. if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "LockVariable", TempValue) == 0) {
  628. if (TempValue != 0) {
  629. GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION", "DebugInfo", TempValue);
  630. GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION_DEBUG", "ContentionCount", CrtHeapStat.LockContention);
  631. } else {
  632. CrtHeapStat.LockContention = 0xbad;
  633. }
  634. }
  635. CrtHeapStat.FrontEndHeapType = 0;
  636. if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "Lookaside", TempValue)) {
  637. if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeapType", FrontEndHeapType)) {
  638. dprintf("Front-end heap type info is not available\n");
  639. } else {
  640. CrtHeapStat.FrontEndHeapType = (ULONG)FrontEndHeapType;
  641. GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeap", CrtHeapStat.FrontEndHeap);
  642. }
  643. } else {
  644. if (TempValue) {
  645. CrtHeapStat.FrontEndHeapType = 1;
  646. }
  647. CrtHeapStat.FrontEndHeap = TempValue;
  648. }
  649. }
  650. typedef struct _BUCKET_INFO {
  651. ULONG TotalBlocks;
  652. ULONG SubSegmentCounts;
  653. ULONG BlockUnits;
  654. ULONG UseAffinity;
  655. LONG Conversions;
  656. } BUCKET_INFO, *PBUCKET_INFO;
  657. VOID
  658. DumpLowfHeap(
  659. ULONG64 HeapAddress
  660. )
  661. {
  662. ULONG64 TempValue, CrtAddress;
  663. ULONG64 Head;
  664. ULONG TempOffset, i;
  665. ULONG64 Next;
  666. ULONG Counter, TempSize;
  667. ULONG Values[20];
  668. PBUCKET_INFO BucketInfo;
  669. ULONG CacheSize = GetFieldSize("ntdll!_USER_MEMORY_CACHE", "AvailableBlocks") / sizeof(ULONG);
  670. if (CacheSize > 20) {
  671. CacheSize = 20;
  672. }
  673. InitTypeRead(HeapAddress, _LFH_HEAP);
  674. if (GetFieldValue(HeapAddress, "ntdll!_LFH_HEAP", "Lock.DebugInfo", TempValue) == 0) {
  675. if (TempValue != 0) {
  676. ULONG64 Contention;
  677. GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION_DEBUG", "ContentionCount", Contention);
  678. dprintf(" Lock contention %7ld\n", (ULONG) Contention);
  679. }
  680. }
  681. GetFieldValue(HeapAddress, "ntdll!_LFH_HEAP", "SubSegmentZones.Flink", Head);
  682. Counter = 0;
  683. ReadPtr(Head, &Next);
  684. while (Next != Head) {
  685. Counter += 1;
  686. if (ReadPtr(Next, &Next)) {
  687. dprintf("ERROR Cannot read SubSegmentZones list at %p\n", Next);
  688. break;
  689. }
  690. }
  691. dprintf(" Metadata usage %7ld\n", Counter * 1024);
  692. dprintf(" Statistics:\n");
  693. dprintf(" Segments created %7ld\n", ReadField(SegmentCreate));
  694. dprintf(" Segments deleted %7ld\n", ReadField(SegmentDelete));
  695. dprintf(" Segments reused %7ld\n", ReadField(SegmentInsertInFree));
  696. dprintf(" Conversions %7ld\n", ReadField(Conversions));
  697. dprintf(" ConvertedSpace %7ld\n\n", ReadField(ConvertedSpace));
  698. GetFieldOffset("ntdll!_LFH_HEAP", "UserBlockCache", &TempOffset);
  699. CrtAddress = TempValue = HeapAddress + TempOffset;
  700. InitTypeRead(TempValue, _USER_MEMORY_CACHE);
  701. dprintf(" Block cache:\n");
  702. dprintf(" Free blocks %7ld\n", ReadField(FreeBlocks));
  703. dprintf(" Sequence %7ld\n", ReadField(Sequence));
  704. dprintf(" Cache blocks");
  705. for (i = 0; i < CacheSize; i++) {
  706. ULONG64 Depth;
  707. GetFieldValue(TempValue, "ntdll!_SLIST_HEADER", "Depth", Depth);
  708. dprintf(" %6ld", (ULONG)Depth);
  709. TempValue += GetTypeSize("_SLIST_HEADER");
  710. }
  711. GetFieldOffset("ntdll!_USER_MEMORY_CACHE", "AvailableBlocks", &TempOffset);
  712. dprintf("\n Available ");
  713. TempValue = CrtAddress + TempOffset;
  714. if (ReadMemory( TempValue, &Values, CacheSize * sizeof(ULONG), NULL )) {
  715. for (i = 0; i < CacheSize; i++) {
  716. dprintf(" %6ld", Values[i]);
  717. }
  718. }
  719. dprintf("\n");
  720. GetFieldOffset("ntdll!_LFH_HEAP", "Buckets", &TempOffset);
  721. CrtAddress = HeapAddress + TempOffset;
  722. TempSize = GetTypeSize("_HEAP_BUCKET");
  723. BucketInfo = (PBUCKET_INFO)malloc(128 * sizeof(BUCKET_INFO));
  724. if (BucketInfo) {
  725. dprintf(" Buckets info:\n");
  726. dprintf(" Size Blocks Seg Aff Conv\n");
  727. dprintf("-----------------------------\n");
  728. for (i = 0; i < 128; i++) {
  729. InitTypeRead(CrtAddress + (i * TempSize), _HEAP_BUCKET);
  730. BucketInfo[i].TotalBlocks = (ULONG)ReadField(Counters.TotalBlocks);
  731. BucketInfo[i].BlockUnits = (ULONG)ReadField(BlockUnits);
  732. BucketInfo[i].Conversions = (ULONG)ReadField(Conversions);
  733. BucketInfo[i].SubSegmentCounts = (ULONG)ReadField(Counters.SubSegmentCounts);
  734. BucketInfo[i].UseAffinity = (ULONG)ReadField(UseAffinity);
  735. if (BucketInfo[i].TotalBlocks) {
  736. dprintf("%6ld %7ld %5ld %ld %4ld\n",
  737. BucketInfo[i].BlockUnits*HeapEntrySize,
  738. BucketInfo[i].TotalBlocks,
  739. BucketInfo[i].SubSegmentCounts,
  740. BucketInfo[i].UseAffinity,
  741. BucketInfo[i].Conversions
  742. );
  743. }
  744. }
  745. dprintf("-----------------------------\n");
  746. free(BucketInfo);
  747. }
  748. }
  749. VOID
  750. DumpHeapInfo(
  751. ULONG64 HeapAddress
  752. )
  753. {
  754. ULONG64 TempValue;
  755. dprintf("\n%2ld: Heap %p\n",
  756. CrtHeapStat.HeapIndex,
  757. CrtHeapStat.HeapAddress);
  758. dprintf(" Flags %08lx - ", CrtHeapStat.Flags);
  759. DumpFlagDescription(CrtHeapStat.Flags);
  760. dprintf("\n");
  761. dprintf(" Reserved %I64d (k)\n", CrtHeapStat.ReservedMemory/1024);
  762. dprintf(" Commited %I64d (k)\n", CrtHeapStat.CommitedMemory/1024);
  763. dprintf(" Virtual bytes %I64d (k)\n", CrtHeapStat.VirtualBytes/1024);
  764. dprintf(" Free space %I64d (k)\n", CrtHeapStat.FreeSpace/1024);
  765. if (CrtHeapStat.CommitedMemory) {
  766. dprintf(" External fragmentation %ld%% (%ld free blocks)\n",
  767. (ULONG)(CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory),
  768. CrtHeapStat.FreeListLength
  769. );
  770. }
  771. if (CrtHeapStat.VirtualBytes) {
  772. dprintf(" Virtual address fragmentation %ld%% (%ld uncommited ranges)\n",
  773. (ULONG)((CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes),
  774. CrtHeapStat.UncommitedRanges
  775. );
  776. }
  777. dprintf(" Virtual blocks %ld\n", CrtHeapStat.VirtualBlocks);
  778. dprintf(" Lock contention %ld\n", CrtHeapStat.LockContention);
  779. GetFieldValue(HeapAddress, "ntdll!_HEAP", "LastSegmentIndex", TempValue);
  780. dprintf(" Segments %ld\n", (ULONG)TempValue + 1);
  781. GetFieldValue(HeapAddress, "ntdll!_HEAP", "LargeBlocksIndex", TempValue);
  782. if (TempValue) {
  783. ULONG PerfOffset;
  784. InitTypeRead(TempValue, _HEAP_INDEX);
  785. dprintf(" %ld hash table for the free list\n", (ULONG) ReadField(ArraySize));
  786. dprintf(" Commits %ld\n", (ULONG) ReadField(Committs));
  787. dprintf(" Decommitts %ld\n", (ULONG) ReadField(Decommitts));
  788. }
  789. switch (CrtHeapStat.FrontEndHeapType) {
  790. case 1:
  791. dprintf("\n Lookaside heap %p\n", CrtHeapStat.FrontEndHeap);
  792. break;
  793. case 2:
  794. dprintf("\n Low fragmentation heap %p\n", CrtHeapStat.FrontEndHeap);
  795. DumpLowfHeap(CrtHeapStat.FrontEndHeap);
  796. break;
  797. }
  798. }
  799. BOOLEAN
  800. HeapStatRoutine(
  801. IN ULONG Context,
  802. IN ULONG64 HeapAddress,
  803. IN ULONG64 SegmentAddress,
  804. IN ULONG64 EntryAddress,
  805. IN ULONG64 Data
  806. )
  807. {
  808. ULONG SizeIndex;
  809. switch (Context) {
  810. case CONTEXT_START_HEAP:
  811. {
  812. if (DumpBlocksSize == 0) {
  813. dprintf("Walking the heap %p ", HeapAddress);
  814. }
  815. CollectHeapInfo(HeapAddress);
  816. //
  817. // Allow scanning the heap
  818. //
  819. return TRUE;
  820. }
  821. break;
  822. case CONTEXT_END_HEAP:
  823. if (DumpBlocksSize == 0) {
  824. dprintf("\r");
  825. }
  826. if (SizeInfo == NULL) {
  827. dprintf("%p %08lx %7I64d %6I64d %6I64d %6I64d %5ld %5ld %4ld %6lx ",
  828. CrtHeapStat.HeapAddress,
  829. CrtHeapStat.Flags,
  830. (CrtHeapStat.ReservedMemory / 1024),
  831. (CrtHeapStat.CommitedMemory / 1024),
  832. (CrtHeapStat.VirtualBytes / 1024),
  833. (CrtHeapStat.FreeSpace / 1024),
  834. CrtHeapStat.FreeListLength,
  835. CrtHeapStat.UncommitedRanges,
  836. CrtHeapStat.VirtualBlocks,
  837. CrtHeapStat.LockContention
  838. );
  839. switch (CrtHeapStat.FrontEndHeapType) {
  840. case 1:
  841. dprintf("L ");
  842. break;
  843. case 2:
  844. dprintf("LFH");
  845. break;
  846. default:
  847. dprintf(" ");
  848. }
  849. dprintf("\n");
  850. //
  851. // Report external fragmentation is the heap uses more than 1M
  852. // The threshold to report is 10%
  853. //
  854. if ((CrtHeapStat.CommitedMemory > 1024*1024)
  855. &&
  856. CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory > 10) {
  857. dprintf(" External fragmentation %ld %% (%ld free blocks)\n",
  858. (ULONG)(CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory),
  859. CrtHeapStat.FreeListLength
  860. );
  861. }
  862. //
  863. // Report virtual address fragmentation is the heap has more than 100 UCR
  864. // The threshold to report is 10%
  865. //
  866. if (CrtHeapStat.UncommitedRanges > 100
  867. &&
  868. (CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes > 10) {
  869. dprintf(" Virtual address fragmentation %ld %% (%ld uncommited ranges)\n",
  870. (ULONG)((CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes),
  871. CrtHeapStat.UncommitedRanges
  872. );
  873. }
  874. //
  875. // Make noise about lock contention. The value is 1M, and it's arbitrary.
  876. // Over a long run this value can be legitimate
  877. //
  878. if (CrtHeapStat.LockContention > 1024*1024) {
  879. dprintf(" Lock contention %ld \n",
  880. CrtHeapStat.LockContention);
  881. }
  882. } else {
  883. DumpHeapInfo(StatHeapAddress);
  884. HeapStatDumpBlocks();
  885. }
  886. break;
  887. case CONTEXT_START_SEGMENT:
  888. {
  889. ULONG64 NumberOfPages, NumberOfUnCommittedPages, LargestUnCommittedRange, NumberOfUnCommittedRanges;
  890. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfPages", NumberOfPages);
  891. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfUnCommittedPages", NumberOfUnCommittedPages);
  892. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "LargestUnCommittedRange", LargestUnCommittedRange);
  893. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfUnCommittedRanges", NumberOfUnCommittedRanges);
  894. CrtHeapStat.ReservedMemory += NumberOfPages * PageSize;
  895. CrtHeapStat.CommitedMemory += (NumberOfPages - NumberOfUnCommittedPages) * PageSize;
  896. CrtHeapStat.UncommitedRanges += (ULONG)NumberOfUnCommittedRanges;
  897. CrtHeapStat.VirtualBytes += NumberOfPages * PageSize - LargestUnCommittedRange;
  898. }
  899. if ((SizeInfo != NULL)
  900. &&
  901. (DumpBlocksSize == 0)
  902. ) {
  903. dprintf(".");
  904. }
  905. if (VerifyBlocks) {
  906. dprintf(".");
  907. return TRUE;
  908. }
  909. //
  910. // Do not walk the blocks. We need to return FALSE
  911. //
  912. return (SizeInfo != NULL);
  913. case CONTEXT_START_SUBSEGMENT:
  914. CrtHeapStat.ScanningSubSegment = TRUE;
  915. break;
  916. case CONTEXT_END_SUBSEGMENT:
  917. CrtHeapStat.ScanningSubSegment = FALSE;
  918. break;
  919. case CONTEXT_FREE_BLOCK:
  920. if (SizeInfo) {
  921. if (CrtHeapStat.ScanningSubSegment) {
  922. SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapFrees += 1;
  923. } else {
  924. SizeInfo[HeapStatSizeToSizeIndex(Data)].Free += 1;
  925. }
  926. }
  927. if (Data == DumpBlocksSize) {
  928. DumpBlock(EntryAddress + HeapEntrySize, 'F');
  929. }
  930. break;
  931. case CONTEXT_BUSY_BLOCK:
  932. if (SizeInfo) {
  933. if (CrtHeapStat.ScanningSubSegment) {
  934. SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapAllocs += 1;
  935. } else {
  936. SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy += 1;
  937. }
  938. }
  939. if (Data == DumpBlocksSize) {
  940. DumpBlock(EntryAddress + HeapEntrySize, 'B');
  941. }
  942. break;
  943. case CONTEXT_LOOKASIDE_BLOCK:
  944. if (SizeInfo) {
  945. SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapFrees += 1;
  946. SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy -= 1;
  947. }
  948. if (Data == DumpBlocksSize) {
  949. DumpBlock(EntryAddress + HeapEntrySize, 'f');
  950. }
  951. break;
  952. case CONTEXT_VIRTUAL_BLOCK:
  953. if (SizeInfo) {
  954. SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy += 1;
  955. }
  956. CrtHeapStat.VirtualBlocks += 1;
  957. break;
  958. case CONTEXT_ERROR:
  959. dprintf("HEAP %p (Seg %p) At %p Error: %s\n",
  960. HeapAddress,
  961. SegmentAddress,
  962. EntryAddress,
  963. Data
  964. );
  965. break;
  966. }
  967. return TRUE;
  968. }
  969. VOID
  970. HeapStat(LPCTSTR szArguments)
  971. {
  972. ULONG64 Process;
  973. ULONG64 ThePeb;
  974. HANDLE hProcess;
  975. int LastCommand = ' ';
  976. if (!InitializeHeapExtension()) {
  977. return;
  978. }
  979. HeapEntryFind = 0;
  980. HeapEntryFindSize = 0;
  981. HeapAddressFind = 0;
  982. SegmentAddressFind = 0;
  983. Lookaside = FALSE;
  984. StatHeapAddress = 0;
  985. BucketSize = 1024;
  986. DumpBlocksSize = 0;
  987. VerifyBlocks = FALSE;
  988. GetPebAddress( 0, &ThePeb);
  989. GetCurrentProcessHandle( &hProcess );
  990. ScanVM = FALSE;
  991. {
  992. LPSTR p = (LPSTR)szArguments;
  993. while (p && *p) {
  994. if (*p == '-') {
  995. p++;
  996. LastCommand = toupper(*p);
  997. if (LastCommand == 'V') {
  998. VerifyBlocks = TRUE;
  999. }
  1000. } else if (isxdigit(*p)) {
  1001. ULONG64 HexInput;
  1002. sscanf( p, "%I64lx", &HexInput );
  1003. while ((*p) && isxdigit(*p)) {
  1004. p++;
  1005. }
  1006. switch (LastCommand) {
  1007. case 'B':
  1008. BucketSize = (ULONG)HexInput;
  1009. break;
  1010. case 'D':
  1011. DumpBlocksSize = (ULONG)HexInput;
  1012. break;
  1013. default:
  1014. if (StatHeapAddress != 0) {
  1015. dprintf("Parameter error: unexpected second heap address %I64d\n", HexInput);
  1016. } else {
  1017. StatHeapAddress = HexInput;
  1018. }
  1019. }
  1020. continue;
  1021. }
  1022. p++;
  1023. }
  1024. }
  1025. if (StatHeapAddress == 0) {
  1026. DumpGlobals();
  1027. if (PointerSize == 8) {
  1028. dprintf(" ");
  1029. }
  1030. dprintf(" Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast \n");
  1031. if (PointerSize == 8) {
  1032. dprintf(" ");
  1033. }
  1034. dprintf(" (k) (k) (k) (k) length blocks cont. heap \n");
  1035. if (PointerSize == 8) {
  1036. dprintf("--------");
  1037. }
  1038. dprintf("-----------------------------------------------------------------------------\n");
  1039. ScanProcessHeaps( 0,
  1040. ThePeb,
  1041. HeapStatRoutine
  1042. );
  1043. if (PointerSize == 8) {
  1044. dprintf("--------");
  1045. }
  1046. dprintf("-----------------------------------------------------------------------------\n");
  1047. } else {
  1048. //
  1049. // Do not handle blocks over 512k, which is close to virtual alloc limit
  1050. //
  1051. LargestBucketIndex = (512*1024)/BucketSize;
  1052. SizeInfo = malloc(LargestBucketIndex * sizeof(SIZE_INFO));
  1053. if (SizeInfo == NULL) {
  1054. dprintf("cannot allocate thye statistics buffer\n");
  1055. return;
  1056. }
  1057. memset(SizeInfo, 0, LargestBucketIndex * sizeof(SIZE_INFO));
  1058. ScanProcessHeaps( StatHeapAddress,
  1059. ThePeb,
  1060. HeapStatRoutine
  1061. );
  1062. free(SizeInfo);
  1063. SizeInfo = NULL;
  1064. }
  1065. }