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.

1484 lines
40 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("nt!_HEAP_ENTRY"); // same as granularity
  102. InitTypeRead(EntryAddress, nt!_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(SmallTagCode);
  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, nt!_HEAP)) {
  147. return;
  148. }
  149. GetFieldOffset("nt!_HEAP", "VirtualAllocdBlocks", &Offset);
  150. AlignRound = (ULONG)ReadField(AlignRound) - GetTypeSize( "nt!_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 ("nt!_HEAP", "FreeLists", &Offset);
  181. ListSize = GetTypeSize("nt!_LIST_ENTRY");
  182. GetFieldOffset ("nt!_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, "nt!_LIST_ENTRY", "Flink", Flink);
  190. GetFieldValue(FreeListHead, "nt!_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, nt!_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. (LPSTR) (ULONG_PTR) 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. GetExpressionEx(p, &AddressToFind, &p);
  468. continue;
  469. }
  470. p++;
  471. }
  472. }
  473. if (AddressToFind == 0) {
  474. dprintf("Syntax:\n!heap -x [-v] address\n");
  475. return;
  476. }
  477. ScanProcessHeaps( 0,
  478. ThePeb,
  479. HeapFindRoutine
  480. );
  481. if (HeapEntryFind) {
  482. DumpEntryHeader();
  483. DumpEntryInfo(HeapAddressFind, SegmentAddressFind, HeapEntryFind);
  484. dprintf("\n");
  485. if (ScanVM) {
  486. SearchVMReference(hProcess, HeapEntryFind, HeapEntryFind + HeapEntryFindSize - 1);
  487. }
  488. } else {
  489. if (ScanVM) {
  490. SearchVMReference(hProcess, AddressToFind, AddressToFind);
  491. }
  492. }
  493. }
  494. //
  495. // Heap stat implementation
  496. //
  497. typedef struct _SIZE_INFO {
  498. ULONG Busy;
  499. ULONG Free;
  500. ULONG FrontHeapAllocs;
  501. ULONG FrontHeapFrees;
  502. }SIZE_INFO, *PSIZE_INFO;
  503. typedef struct _HEAP_STATS {
  504. ULONG HeapIndex;
  505. ULONG64 HeapAddress;
  506. ULONG64 ReservedMemory;
  507. ULONG64 CommitedMemory;
  508. ULONG64 VirtualBytes;
  509. ULONG64 FreeSpace;
  510. ULONG Flags;
  511. ULONG FreeListLength;
  512. ULONG VirtualBlocks;
  513. ULONG UncommitedRanges;
  514. ULONG64 LockContention;
  515. ULONG FrontEndHeapType;
  516. ULONG64 FrontEndHeap;
  517. BOOLEAN ScanningSubSegment;
  518. }HEAP_STATS, *PHEAP_STATS;
  519. HEAP_STATS CrtHeapStat;
  520. PSIZE_INFO SizeInfo = NULL;
  521. ULONG BucketSize;
  522. ULONG LargestBucketIndex = 0;
  523. ULONG64 StatHeapAddress;
  524. ULONG DumpBlocksSize = 0;
  525. ULONG
  526. FASTCALL
  527. HeapStatSizeToSizeIndex(ULONG64 Size)
  528. {
  529. ULONG Index = (ULONG)(Size / BucketSize);
  530. if (Index >= LargestBucketIndex) {
  531. Index = LargestBucketIndex - 1;
  532. }
  533. return Index;
  534. }
  535. VOID
  536. HeapStatDumpBlocks()
  537. {
  538. ULONG Index;
  539. ULONG64 Busy = 0, Free = 0, FEBusy = 0, FEFree = 0;
  540. dprintf("\n Default heap Front heap \n");
  541. dprintf(" Range (bytes) Busy Free Busy Free \n");
  542. dprintf("----------------------------------------------- \n");
  543. if (SizeInfo == NULL) {
  544. return;
  545. }
  546. for ( Index = 0; Index < LargestBucketIndex; Index++ ) {
  547. if (SizeInfo[Index].Busy
  548. ||
  549. SizeInfo[Index].Free
  550. ||
  551. SizeInfo[Index].FrontHeapAllocs
  552. ||
  553. SizeInfo[Index].FrontHeapFrees) {
  554. dprintf("%6ld - %6ld %6ld %6ld %6ld %6ld\n",
  555. Index * BucketSize,
  556. (Index + 1) * BucketSize,
  557. SizeInfo[Index].Busy,
  558. SizeInfo[Index].Free,
  559. SizeInfo[Index].FrontHeapAllocs,
  560. SizeInfo[Index].FrontHeapFrees
  561. );
  562. FEBusy += SizeInfo[Index].FrontHeapAllocs;
  563. FEFree += SizeInfo[Index].FrontHeapFrees;
  564. Busy += SizeInfo[Index].Busy;
  565. Free += SizeInfo[Index].Free;
  566. }
  567. }
  568. dprintf("----------------------------------------------- \n");
  569. dprintf(" Total %6I64d %6I64d %6I64d %6I64d \n",
  570. Busy,
  571. Free,
  572. FEBusy,
  573. FEFree
  574. );
  575. }
  576. VOID
  577. DumpBlock (
  578. IN ULONG64 BlockAddress,
  579. IN UCHAR Flag
  580. )
  581. {
  582. UCHAR Buffer[33];
  583. PULONG pLongArray = (PULONG)Buffer;
  584. ULONG i;
  585. memchr(Buffer, '?', sizeof( Buffer ));
  586. if (!ReadMemory( BlockAddress, &Buffer[0], sizeof( Buffer ), NULL )) {
  587. dprintf("%p ?\n", BlockAddress);
  588. return;
  589. }
  590. dprintf("%p %c %08lx %08lx %08lx %08lx ",
  591. BlockAddress,
  592. Flag,
  593. pLongArray[0],
  594. pLongArray[1],
  595. pLongArray[2],
  596. pLongArray[3]
  597. );
  598. for (i = 0; i < 32; i++) {
  599. if (!isprint(Buffer[i])) {
  600. Buffer[i] = '.';
  601. }
  602. }
  603. Buffer[32] = 0;
  604. dprintf("%s\n", Buffer);
  605. }
  606. VOID
  607. CollectHeapInfo(
  608. ULONG64 HeapAddress
  609. )
  610. {
  611. ULONG64 TempValue;
  612. ULONG64 FrontEndHeapType;
  613. memset(&CrtHeapStat, 0, sizeof(CrtHeapStat));
  614. CrtHeapStat.HeapAddress = HeapAddress;
  615. GetFieldValue(HeapAddress, "ntdll!_HEAP", "TotalFreeSize", CrtHeapStat.FreeSpace);
  616. CrtHeapStat.FreeSpace *= HeapEntrySize;
  617. GetFieldValue(HeapAddress, "ntdll!_HEAP", "NonDedicatedListLength", CrtHeapStat.FreeListLength);
  618. GetFieldValue(HeapAddress, "ntdll!_HEAP", "Flags", TempValue);
  619. CrtHeapStat.Flags = (ULONG)TempValue;
  620. GetFieldValue(HeapAddress, "ntdll!_HEAP", "Signature", TempValue);
  621. if (TempValue != 0xeeffeeff) {
  622. dprintf("Error: Heap %p has an invalid signature %08lx\n", HeapAddress, 0xeeffeeff);
  623. }
  624. if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "LockVariable", TempValue) == 0) {
  625. if (TempValue != 0) {
  626. GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION", "DebugInfo", TempValue);
  627. GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION_DEBUG", "ContentionCount", CrtHeapStat.LockContention);
  628. } else {
  629. CrtHeapStat.LockContention = 0xbad;
  630. }
  631. }
  632. CrtHeapStat.FrontEndHeapType = 0;
  633. if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "Lookaside", TempValue)) {
  634. if (GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeapType", FrontEndHeapType)) {
  635. dprintf("Front-end heap type info is not available\n");
  636. } else {
  637. CrtHeapStat.FrontEndHeapType = (ULONG)FrontEndHeapType;
  638. GetFieldValue(HeapAddress, "ntdll!_HEAP", "FrontEndHeap", CrtHeapStat.FrontEndHeap);
  639. }
  640. } else {
  641. if (TempValue) {
  642. CrtHeapStat.FrontEndHeapType = 1;
  643. }
  644. CrtHeapStat.FrontEndHeap = TempValue;
  645. }
  646. }
  647. typedef struct _BUCKET_INFO {
  648. ULONG TotalBlocks;
  649. ULONG SubSegmentCounts;
  650. ULONG BlockUnits;
  651. ULONG UseAffinity;
  652. LONG Conversions;
  653. } BUCKET_INFO, *PBUCKET_INFO;
  654. VOID
  655. DumpLowfHeap(
  656. ULONG64 HeapAddress
  657. )
  658. {
  659. ULONG64 TempValue, CrtAddress;
  660. ULONG64 Head;
  661. ULONG TempOffset, i;
  662. ULONG64 Next;
  663. ULONG Counter, TempSize;
  664. ULONG Values[20];
  665. PBUCKET_INFO BucketInfo;
  666. ULONG CacheSize = GetFieldSize("ntdll!_USER_MEMORY_CACHE", "AvailableBlocks") / sizeof(ULONG);
  667. if (CacheSize > 20) {
  668. CacheSize = 20;
  669. }
  670. InitTypeRead(HeapAddress, nt!_LFH_HEAP);
  671. if (GetFieldValue(HeapAddress, "ntdll!_LFH_HEAP", "Lock.DebugInfo", TempValue) == 0) {
  672. if (TempValue != 0) {
  673. ULONG64 Contention;
  674. GetFieldValue(TempValue, "ntdll!_RTL_CRITICAL_SECTION_DEBUG", "ContentionCount", Contention);
  675. dprintf(" Lock contention %7ld\n", (ULONG) Contention);
  676. }
  677. }
  678. GetFieldValue(HeapAddress, "ntdll!_LFH_HEAP", "SubSegmentZones.Flink", Head);
  679. Counter = 0;
  680. ReadPointer(Head, &Next);
  681. while (Next != Head) {
  682. Counter += 1;
  683. if (!ReadPointer(Next, &Next)) {
  684. dprintf("ERROR Cannot read SubSegmentZones list at %p\n", Next);
  685. break;
  686. }
  687. }
  688. dprintf(" Metadata usage %7ld\n", Counter * 1024);
  689. dprintf(" Statistics:\n");
  690. dprintf(" Segments created %7ld\n", ReadField(SegmentCreate));
  691. dprintf(" Segments deleted %7ld\n", ReadField(SegmentDelete));
  692. dprintf(" Segments reused %7ld\n", ReadField(SegmentInsertInFree));
  693. dprintf(" Conversions %7ld\n", ReadField(Conversions));
  694. dprintf(" ConvertedSpace %7ld\n\n", ReadField(ConvertedSpace));
  695. GetFieldOffset("ntdll!_LFH_HEAP", "UserBlockCache", &TempOffset);
  696. CrtAddress = TempValue = HeapAddress + TempOffset;
  697. InitTypeRead(TempValue, nt!_USER_MEMORY_CACHE);
  698. dprintf(" Block cache:\n");
  699. dprintf(" Free blocks %7ld\n", ReadField(FreeBlocks));
  700. dprintf(" Sequence %7ld\n", ReadField(Sequence));
  701. dprintf(" Cache blocks");
  702. for (i = 0; i < CacheSize; i++) {
  703. ULONG64 Depth;
  704. GetFieldValue(TempValue, "ntdll!_SLIST_HEADER", "Depth", Depth);
  705. dprintf(" %6ld", (ULONG)Depth);
  706. TempValue += GetTypeSize("_SLIST_HEADER");
  707. }
  708. GetFieldOffset("ntdll!_USER_MEMORY_CACHE", "AvailableBlocks", &TempOffset);
  709. dprintf("\n Available ");
  710. TempValue = CrtAddress + TempOffset;
  711. if (ReadMemory( TempValue, Values, CacheSize * sizeof(ULONG), NULL )) {
  712. for (i = 0; i < CacheSize; i++) {
  713. dprintf(" %6ld", Values[i]);
  714. }
  715. }
  716. dprintf("\n");
  717. GetFieldOffset("ntdll!_LFH_HEAP", "Buckets", &TempOffset);
  718. CrtAddress = HeapAddress + TempOffset;
  719. TempSize = GetTypeSize("nt!_HEAP_BUCKET");
  720. BucketInfo = (PBUCKET_INFO)malloc(128 * sizeof(BUCKET_INFO));
  721. if (BucketInfo) {
  722. dprintf(" Buckets info:\n");
  723. dprintf(" Size Blocks Seg Aff Conv\n");
  724. dprintf("-----------------------------\n");
  725. for (i = 0; i < 128; i++) {
  726. InitTypeRead(CrtAddress + (i * TempSize), nt!_HEAP_BUCKET);
  727. BucketInfo[i].TotalBlocks = (ULONG)ReadField(Counters.TotalBlocks);
  728. BucketInfo[i].BlockUnits = (ULONG)ReadField(BlockUnits);
  729. BucketInfo[i].Conversions = (ULONG)ReadField(Conversions);
  730. BucketInfo[i].SubSegmentCounts = (ULONG)ReadField(Counters.SubSegmentCounts);
  731. BucketInfo[i].UseAffinity = (ULONG)ReadField(UseAffinity);
  732. if (BucketInfo[i].TotalBlocks) {
  733. dprintf("%6ld %7ld %5ld %ld %4ld\n",
  734. BucketInfo[i].BlockUnits*HeapEntrySize,
  735. BucketInfo[i].TotalBlocks,
  736. BucketInfo[i].SubSegmentCounts,
  737. BucketInfo[i].UseAffinity,
  738. BucketInfo[i].Conversions
  739. );
  740. }
  741. }
  742. dprintf("-----------------------------\n");
  743. free(BucketInfo);
  744. }
  745. }
  746. VOID
  747. DumpHeapInfo(
  748. ULONG64 HeapAddress
  749. )
  750. {
  751. ULONG64 TempValue;
  752. dprintf("\n%2ld: Heap %p\n",
  753. CrtHeapStat.HeapIndex,
  754. CrtHeapStat.HeapAddress);
  755. dprintf(" Flags %08lx - ", CrtHeapStat.Flags);
  756. DumpFlagDescription(CrtHeapStat.Flags);
  757. dprintf("\n");
  758. dprintf(" Reserved %I64d (k)\n", CrtHeapStat.ReservedMemory/1024);
  759. dprintf(" Commited %I64d (k)\n", CrtHeapStat.CommitedMemory/1024);
  760. dprintf(" Virtual bytes %I64d (k)\n", CrtHeapStat.VirtualBytes/1024);
  761. dprintf(" Free space %I64d (k)\n", CrtHeapStat.FreeSpace/1024);
  762. if (CrtHeapStat.CommitedMemory) {
  763. dprintf(" External fragmentation %ld%% (%ld free blocks)\n",
  764. (ULONG)(CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory),
  765. CrtHeapStat.FreeListLength
  766. );
  767. }
  768. if (CrtHeapStat.VirtualBytes) {
  769. dprintf(" Virtual address fragmentation %ld%% (%ld uncommited ranges)\n",
  770. (ULONG)((CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes),
  771. CrtHeapStat.UncommitedRanges
  772. );
  773. }
  774. dprintf(" Virtual blocks %ld\n", CrtHeapStat.VirtualBlocks);
  775. dprintf(" Lock contention %ld\n", CrtHeapStat.LockContention);
  776. GetFieldValue(HeapAddress, "ntdll!_HEAP", "LastSegmentIndex", TempValue);
  777. dprintf(" Segments %ld\n", (ULONG)TempValue + 1);
  778. GetFieldValue(HeapAddress, "ntdll!_HEAP", "LargeBlocksIndex", TempValue);
  779. if (TempValue) {
  780. ULONG PerfOffset;
  781. InitTypeRead(TempValue, nt!_HEAP_INDEX);
  782. dprintf(" %ld hash table for the free list\n", (ULONG) ReadField(ArraySize));
  783. dprintf(" Commits %ld\n", (ULONG) ReadField(Committs));
  784. dprintf(" Decommitts %ld\n", (ULONG) ReadField(Decommitts));
  785. }
  786. switch (CrtHeapStat.FrontEndHeapType) {
  787. case 1:
  788. dprintf("\n Lookaside heap %p\n", CrtHeapStat.FrontEndHeap);
  789. break;
  790. case 2:
  791. dprintf("\n Low fragmentation heap %p\n", CrtHeapStat.FrontEndHeap);
  792. DumpLowfHeap(CrtHeapStat.FrontEndHeap);
  793. break;
  794. }
  795. }
  796. BOOLEAN
  797. HeapStatRoutine(
  798. IN ULONG Context,
  799. IN ULONG64 HeapAddress,
  800. IN ULONG64 SegmentAddress,
  801. IN ULONG64 EntryAddress,
  802. IN ULONG64 Data
  803. )
  804. {
  805. ULONG SizeIndex;
  806. switch (Context) {
  807. case CONTEXT_START_HEAP:
  808. {
  809. if (DumpBlocksSize == 0) {
  810. dprintf("Walking the heap %p ", HeapAddress);
  811. }
  812. CollectHeapInfo(HeapAddress);
  813. //
  814. // Allow scanning the heap
  815. //
  816. return TRUE;
  817. }
  818. break;
  819. case CONTEXT_END_HEAP:
  820. if (DumpBlocksSize == 0) {
  821. dprintf("\r");
  822. }
  823. if (SizeInfo == NULL) {
  824. dprintf("%p %08lx %7I64d %6I64d %6I64d %6I64d %5ld %5ld %4ld %6lx ",
  825. CrtHeapStat.HeapAddress,
  826. CrtHeapStat.Flags,
  827. (CrtHeapStat.ReservedMemory / 1024),
  828. (CrtHeapStat.CommitedMemory / 1024),
  829. (CrtHeapStat.VirtualBytes / 1024),
  830. (CrtHeapStat.FreeSpace / 1024),
  831. CrtHeapStat.FreeListLength,
  832. CrtHeapStat.UncommitedRanges,
  833. CrtHeapStat.VirtualBlocks,
  834. CrtHeapStat.LockContention
  835. );
  836. switch (CrtHeapStat.FrontEndHeapType) {
  837. case 1:
  838. dprintf("L ");
  839. break;
  840. case 2:
  841. dprintf("LFH");
  842. break;
  843. default:
  844. dprintf(" ");
  845. }
  846. dprintf("\n");
  847. //
  848. // Report external fragmentation is the heap uses more than 1M
  849. // The threshold to report is 10%
  850. //
  851. if ((CrtHeapStat.CommitedMemory > 1024*1024)
  852. &&
  853. CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory > 10) {
  854. dprintf(" External fragmentation %ld %% (%ld free blocks)\n",
  855. (ULONG)(CrtHeapStat.FreeSpace *100 / CrtHeapStat.CommitedMemory),
  856. CrtHeapStat.FreeListLength
  857. );
  858. }
  859. //
  860. // Report virtual address fragmentation is the heap has more than 100 UCR
  861. // The threshold to report is 10%
  862. //
  863. if (CrtHeapStat.UncommitedRanges > 100
  864. &&
  865. (CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes > 10) {
  866. dprintf(" Virtual address fragmentation %ld %% (%ld uncommited ranges)\n",
  867. (ULONG)((CrtHeapStat.VirtualBytes - CrtHeapStat.CommitedMemory) *100 / CrtHeapStat.VirtualBytes),
  868. CrtHeapStat.UncommitedRanges
  869. );
  870. }
  871. //
  872. // Make noise about lock contention. The value is 1M, and it's arbitrary.
  873. // Over a long run this value can be legitimate
  874. //
  875. if (CrtHeapStat.LockContention > 1024*1024) {
  876. dprintf(" Lock contention %ld \n",
  877. CrtHeapStat.LockContention);
  878. }
  879. } else {
  880. DumpHeapInfo(StatHeapAddress);
  881. HeapStatDumpBlocks();
  882. }
  883. break;
  884. case CONTEXT_START_SEGMENT:
  885. {
  886. ULONG64 NumberOfPages, NumberOfUnCommittedPages, LargestUnCommittedRange, NumberOfUnCommittedRanges;
  887. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfPages", NumberOfPages);
  888. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfUnCommittedPages", NumberOfUnCommittedPages);
  889. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "LargestUnCommittedRange", LargestUnCommittedRange);
  890. GetFieldValue(SegmentAddress, "ntdll!_HEAP_SEGMENT", "NumberOfUnCommittedRanges", NumberOfUnCommittedRanges);
  891. CrtHeapStat.ReservedMemory += NumberOfPages * PageSize;
  892. CrtHeapStat.CommitedMemory += (NumberOfPages - NumberOfUnCommittedPages) * PageSize;
  893. CrtHeapStat.UncommitedRanges += (ULONG)NumberOfUnCommittedRanges;
  894. CrtHeapStat.VirtualBytes += NumberOfPages * PageSize - LargestUnCommittedRange;
  895. }
  896. if ((SizeInfo != NULL)
  897. &&
  898. (DumpBlocksSize == 0)
  899. ) {
  900. dprintf(".");
  901. }
  902. if (VerifyBlocks) {
  903. dprintf(".");
  904. return TRUE;
  905. }
  906. //
  907. // Do not walk the blocks. We need to return FALSE
  908. //
  909. return (SizeInfo != NULL);
  910. case CONTEXT_START_SUBSEGMENT:
  911. CrtHeapStat.ScanningSubSegment = TRUE;
  912. break;
  913. case CONTEXT_END_SUBSEGMENT:
  914. CrtHeapStat.ScanningSubSegment = FALSE;
  915. break;
  916. case CONTEXT_FREE_BLOCK:
  917. if (SizeInfo) {
  918. if (CrtHeapStat.ScanningSubSegment) {
  919. SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapFrees += 1;
  920. } else {
  921. SizeInfo[HeapStatSizeToSizeIndex(Data)].Free += 1;
  922. }
  923. }
  924. if (Data == DumpBlocksSize) {
  925. DumpBlock(EntryAddress + HeapEntrySize, 'F');
  926. }
  927. break;
  928. case CONTEXT_BUSY_BLOCK:
  929. if (SizeInfo) {
  930. if (CrtHeapStat.ScanningSubSegment) {
  931. SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapAllocs += 1;
  932. } else {
  933. SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy += 1;
  934. }
  935. }
  936. if (Data == DumpBlocksSize) {
  937. DumpBlock(EntryAddress + HeapEntrySize, 'B');
  938. }
  939. break;
  940. case CONTEXT_LOOKASIDE_BLOCK:
  941. if (SizeInfo) {
  942. SizeInfo[HeapStatSizeToSizeIndex(Data)].FrontHeapFrees += 1;
  943. SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy -= 1;
  944. }
  945. if (Data == DumpBlocksSize) {
  946. DumpBlock(EntryAddress + HeapEntrySize, 'f');
  947. }
  948. break;
  949. case CONTEXT_VIRTUAL_BLOCK:
  950. if (SizeInfo) {
  951. SizeInfo[HeapStatSizeToSizeIndex(Data)].Busy += 1;
  952. }
  953. CrtHeapStat.VirtualBlocks += 1;
  954. break;
  955. case CONTEXT_ERROR:
  956. dprintf("HEAP %p (Seg %p) At %p Error: %s\n",
  957. HeapAddress,
  958. SegmentAddress,
  959. EntryAddress,
  960. (LPSTR) (ULONG_PTR) Data
  961. );
  962. break;
  963. }
  964. return TRUE;
  965. }
  966. VOID
  967. HeapStat(LPCTSTR szArguments)
  968. {
  969. ULONG64 Process;
  970. ULONG64 ThePeb;
  971. HANDLE hProcess;
  972. int LastCommand = ' ';
  973. if (!InitializeHeapExtension()) {
  974. return;
  975. }
  976. HeapEntryFind = 0;
  977. HeapEntryFindSize = 0;
  978. HeapAddressFind = 0;
  979. SegmentAddressFind = 0;
  980. Lookaside = FALSE;
  981. StatHeapAddress = 0;
  982. BucketSize = 1024;
  983. DumpBlocksSize = 0;
  984. VerifyBlocks = FALSE;
  985. GetPebAddress( 0, &ThePeb);
  986. GetCurrentProcessHandle( &hProcess );
  987. ScanVM = FALSE;
  988. {
  989. LPSTR p = (LPSTR)szArguments;
  990. while (p && *p) {
  991. if (*p == '-') {
  992. p++;
  993. LastCommand = toupper(*p);
  994. if (LastCommand == 'V') {
  995. VerifyBlocks = TRUE;
  996. }
  997. } else if (isxdigit(*p)) {
  998. ULONG64 HexInput = 0;
  999. GetExpressionEx(p, &HexInput, &p);
  1000. switch (LastCommand) {
  1001. case 'B':
  1002. BucketSize = (ULONG)HexInput;
  1003. break;
  1004. case 'D':
  1005. DumpBlocksSize = (ULONG)HexInput;
  1006. break;
  1007. default:
  1008. if (StatHeapAddress != 0) {
  1009. dprintf("Parameter error: unexpected second heap address %I64d\n", HexInput);
  1010. } else {
  1011. StatHeapAddress = HexInput;
  1012. }
  1013. }
  1014. LastCommand = '\0';
  1015. continue;
  1016. }
  1017. p++;
  1018. }
  1019. }
  1020. if (StatHeapAddress == 0) {
  1021. DumpGlobals();
  1022. if (PointerSize == 8) {
  1023. dprintf(" ");
  1024. }
  1025. dprintf(" Heap Flags Reserv Commit Virt Free List UCR Virt Lock Fast \n");
  1026. if (PointerSize == 8) {
  1027. dprintf(" ");
  1028. }
  1029. dprintf(" (k) (k) (k) (k) length blocks cont. heap \n");
  1030. if (PointerSize == 8) {
  1031. dprintf("--------");
  1032. }
  1033. dprintf("-----------------------------------------------------------------------------\n");
  1034. ScanProcessHeaps( 0,
  1035. ThePeb,
  1036. HeapStatRoutine
  1037. );
  1038. if (PointerSize == 8) {
  1039. dprintf("--------");
  1040. }
  1041. dprintf("-----------------------------------------------------------------------------\n");
  1042. } else {
  1043. //
  1044. // Do not handle blocks over 512k, which is close to virtual alloc limit
  1045. //
  1046. LargestBucketIndex = (512*1024)/BucketSize;
  1047. SizeInfo = malloc(LargestBucketIndex * sizeof(SIZE_INFO));
  1048. if (SizeInfo == NULL) {
  1049. dprintf("cannot allocate thye statistics buffer\n");
  1050. return;
  1051. }
  1052. memset(SizeInfo, 0, LargestBucketIndex * sizeof(SIZE_INFO));
  1053. ScanProcessHeaps( StatHeapAddress,
  1054. ThePeb,
  1055. HeapStatRoutine
  1056. );
  1057. free(SizeInfo);
  1058. SizeInfo = NULL;
  1059. }
  1060. }