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.

693 lines
17 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. perfheap.c
  5. Abstract:
  6. This file implements an Performance Object that presents
  7. Heap performance object data
  8. Created:
  9. Adrian Marinescu 9-Mar-2000
  10. Revision History:
  11. --*/
  12. //
  13. // Include Files
  14. //
  15. #include <nt.h>
  16. #include <ntrtl.h>
  17. #include <nturtl.h>
  18. #include <windows.h>
  19. #include <assert.h>
  20. #include <winperf.h>
  21. #include <ntprfctr.h>
  22. #include <perfutil.h>
  23. #include "perfsprc.h"
  24. #include "perfmsg.h"
  25. #include "dataheap.h"
  26. //
  27. // Redefinition for heap data
  28. //
  29. #define MAX_HEAP_COUNT 200
  30. #define HEAP_MAXIMUM_FREELISTS 128
  31. #define HEAP_MAXIMUM_SEGMENTS 64
  32. #define HEAP_OP_COUNT 2
  33. #define HEAP_OP_ALLOC 0
  34. #define HEAP_OP_FREE 1
  35. typedef struct _HEAP_ENTRY {
  36. USHORT Size;
  37. USHORT PreviousSize;
  38. UCHAR SegmentIndex;
  39. UCHAR Flags;
  40. UCHAR UnusedBytes;
  41. UCHAR SmallTagIndex;
  42. #if defined(_WIN64)
  43. ULONGLONG Reserved1;
  44. #endif
  45. } HEAP_ENTRY, *PHEAP_ENTRY;
  46. typedef struct _HEAP_SEGMENT {
  47. HEAP_ENTRY Entry;
  48. ULONG Signature;
  49. ULONG Flags;
  50. struct _HEAP *Heap;
  51. SIZE_T LargestUnCommittedRange;
  52. PVOID BaseAddress;
  53. ULONG NumberOfPages;
  54. PHEAP_ENTRY FirstEntry;
  55. PHEAP_ENTRY LastValidEntry;
  56. ULONG NumberOfUnCommittedPages;
  57. ULONG NumberOfUnCommittedRanges;
  58. PVOID UnCommittedRanges;
  59. USHORT AllocatorBackTraceIndex;
  60. USHORT Reserved;
  61. PHEAP_ENTRY LastEntryInSegment;
  62. } HEAP_SEGMENT, *PHEAP_SEGMENT;
  63. typedef struct _HEAP {
  64. HEAP_ENTRY Entry;
  65. ULONG Signature;
  66. ULONG Flags;
  67. ULONG ForceFlags;
  68. ULONG VirtualMemoryThreshold;
  69. SIZE_T SegmentReserve;
  70. SIZE_T SegmentCommit;
  71. SIZE_T DeCommitFreeBlockThreshold;
  72. SIZE_T DeCommitTotalFreeThreshold;
  73. SIZE_T TotalFreeSize;
  74. SIZE_T MaximumAllocationSize;
  75. USHORT ProcessHeapsListIndex;
  76. USHORT HeaderValidateLength;
  77. PVOID HeaderValidateCopy;
  78. USHORT NextAvailableTagIndex;
  79. USHORT MaximumTagIndex;
  80. PVOID TagEntries;
  81. PVOID UCRSegments;
  82. PVOID UnusedUnCommittedRanges;
  83. ULONG AlignRound;
  84. ULONG AlignMask;
  85. LIST_ENTRY VirtualAllocdBlocks;
  86. PHEAP_SEGMENT Segments[ HEAP_MAXIMUM_SEGMENTS ];
  87. union {
  88. ULONG FreeListsInUseUlong[ HEAP_MAXIMUM_FREELISTS / 32 ];
  89. UCHAR FreeListsInUseBytes[ HEAP_MAXIMUM_FREELISTS / 8 ];
  90. } u;
  91. USHORT FreeListsInUseTerminate;
  92. USHORT AllocatorBackTraceIndex;
  93. ULONG NonDedicatedListLength;
  94. PVOID LargeBlocksIndex;
  95. PVOID PseudoTagEntries;
  96. LIST_ENTRY FreeLists[ HEAP_MAXIMUM_FREELISTS ];
  97. PVOID LockVariable;
  98. PVOID CommitRoutine;
  99. PVOID Lookaside;
  100. ULONG LookasideLockCount;
  101. } HEAP, *PHEAP;
  102. typedef struct _HEAP_PERF_DATA {
  103. UINT64 CountFrequence;
  104. UINT64 OperationTime[HEAP_OP_COUNT];
  105. //
  106. // The data bellow are only for sampling
  107. //
  108. ULONG Sequence;
  109. UINT64 TempTime[HEAP_OP_COUNT];
  110. ULONG TempCount[HEAP_OP_COUNT];
  111. } HEAP_PERF_DATA, *PHEAP_PERF_DATA;
  112. //
  113. // The heap index structure
  114. //
  115. typedef struct _HEAP_INDEX {
  116. ULONG ArraySize;
  117. ULONG VirtualMemorySize;
  118. HEAP_PERF_DATA PerfData;
  119. union {
  120. PULONG FreeListsInUseUlong;
  121. PUCHAR FreeListsInUseBytes;
  122. } u;
  123. PVOID *FreeListHints;
  124. } HEAP_INDEX, *PHEAP_INDEX;
  125. typedef struct _HEAP_LOOKASIDE {
  126. SLIST_HEADER ListHead;
  127. USHORT Depth;
  128. USHORT MaximumDepth;
  129. ULONG TotalAllocates;
  130. ULONG AllocateMisses;
  131. ULONG TotalFrees;
  132. ULONG FreeMisses;
  133. ULONG LastTotalAllocates;
  134. ULONG LastAllocateMisses;
  135. ULONG Counters[2];
  136. } HEAP_LOOKASIDE, *PHEAP_LOOKASIDE;
  137. //
  138. // Local variables
  139. //
  140. static HEAP_LOOKASIDE LookasideBuffer[HEAP_MAXIMUM_FREELISTS];
  141. static DWORD PageSize = 0;
  142. //
  143. // Implementation for heap query function
  144. //
  145. BOOLEAN
  146. ReadHeapData (
  147. IN HANDLE hProcess,
  148. IN ULONG HeapNumber,
  149. IN PHEAP Heap,
  150. OUT PHEAP_COUNTER_DATA pHCD
  151. )
  152. /*++
  153. Routine Description:
  154. The routine loads into the given heap couter structure the
  155. data from the heap structure
  156. Arguments:
  157. hProcess - The process containing the heap
  158. Heap - the heap address
  159. pPerfInstanceDefinition - Performance instance definition data
  160. pHCD - Counter data
  161. Returns:
  162. Returns TRUE if query succeeds.
  163. --*/
  164. {
  165. HEAP_SEGMENT CrtSegment;
  166. HEAP CrtHeap;
  167. ULONG SegmentIndex;
  168. RTL_CRITICAL_SECTION CriticalSection;
  169. HEAP_INDEX HeapIndex;
  170. ULONG i;
  171. //
  172. // Read the heap structure from the process address space
  173. //
  174. if (!ReadProcessMemory(hProcess, Heap, &CrtHeap, sizeof(CrtHeap), NULL)) {
  175. return FALSE;
  176. }
  177. //
  178. // We won't display data for heaps w/o index.
  179. //
  180. if ((CrtHeap.LargeBlocksIndex == NULL)
  181. &&
  182. (HeapNumber != 0)) {
  183. //
  184. // We are not handling small heaps
  185. //
  186. return FALSE;
  187. }
  188. pHCD->FreeSpace = CrtHeap.TotalFreeSize;
  189. pHCD->FreeListLength = CrtHeap.NonDedicatedListLength;
  190. pHCD->CommittedBytes = 0;
  191. pHCD->ReservedBytes = 0;
  192. pHCD->VirtualBytes = 0;
  193. pHCD->UncommitedRangesLength = 0;
  194. //
  195. // Walking the heap segments and get the virtual address counters
  196. //
  197. for (SegmentIndex = 0; SegmentIndex < HEAP_MAXIMUM_SEGMENTS; SegmentIndex++) {
  198. if ((CrtHeap.Segments[SegmentIndex] == NULL) ||
  199. !ReadProcessMemory(hProcess, CrtHeap.Segments[SegmentIndex], &CrtSegment, sizeof(CrtSegment), NULL)) {
  200. break;
  201. }
  202. pHCD->ReservedBytes += CrtSegment.NumberOfPages * PageSize;
  203. pHCD->CommittedBytes += (CrtSegment.NumberOfPages - CrtSegment.NumberOfUnCommittedPages) * PageSize;
  204. pHCD->VirtualBytes += CrtSegment.NumberOfPages * PageSize - CrtSegment.LargestUnCommittedRange;
  205. pHCD->UncommitedRangesLength += CrtSegment.NumberOfUnCommittedRanges;
  206. }
  207. if (pHCD->CommittedBytes == 0) {
  208. pHCD->CommittedBytes = 1;
  209. }
  210. if (pHCD->VirtualBytes == 0) {
  211. pHCD->VirtualBytes = 1;
  212. }
  213. //
  214. // Compute the heap fragmentation counters
  215. //
  216. pHCD->BlockFragmentation = (ULONG)(pHCD->FreeSpace * 100 / pHCD->CommittedBytes);
  217. pHCD->VAFragmentation =(ULONG)(((pHCD->VirtualBytes - pHCD->CommittedBytes)*100)/pHCD->VirtualBytes);
  218. //
  219. // Read the lock contention
  220. //
  221. pHCD->LockContention = 0;
  222. if (ReadProcessMemory(hProcess, CrtHeap.LockVariable, &CriticalSection, sizeof(CriticalSection), NULL)) {
  223. RTL_CRITICAL_SECTION_DEBUG DebugInfo;
  224. if (ReadProcessMemory(hProcess, CriticalSection.DebugInfo, &DebugInfo, sizeof(DebugInfo), NULL)) {
  225. pHCD->LockContention = DebugInfo.ContentionCount;
  226. }
  227. }
  228. //
  229. // Walk the lookaside to count the blocks
  230. //
  231. pHCD->LookasideAllocs = 0;
  232. pHCD->LookasideFrees = 0;
  233. pHCD->LookasideBlocks = 0;
  234. pHCD->LargestLookasideDepth = 0;
  235. pHCD->SmallAllocs = 0;
  236. pHCD->SmallFrees = 0;
  237. pHCD->MedAllocs = 0;
  238. pHCD->MedFrees = 0;
  239. pHCD->LargeAllocs = 0;
  240. pHCD->LargeFrees = 0;
  241. if (ReadProcessMemory(hProcess, CrtHeap.Lookaside, &LookasideBuffer, sizeof(LookasideBuffer), NULL)) {
  242. for (i = 0; i < HEAP_MAXIMUM_FREELISTS; i++) {
  243. pHCD->SmallAllocs += LookasideBuffer[i].TotalAllocates;
  244. pHCD->SmallFrees += LookasideBuffer[i].TotalFrees;
  245. pHCD->LookasideAllocs += LookasideBuffer[i].TotalAllocates - LookasideBuffer[i].AllocateMisses;
  246. pHCD->LookasideFrees += LookasideBuffer[i].TotalFrees - LookasideBuffer[i].FreeMisses;
  247. if (LookasideBuffer[i].Depth > pHCD->LargestLookasideDepth) {
  248. pHCD->LargestLookasideDepth = LookasideBuffer[i].Depth;
  249. }
  250. if (i == 0) {
  251. } else if (i < 8) {
  252. pHCD->MedAllocs += LookasideBuffer[i].Counters[0];
  253. pHCD->MedFrees += LookasideBuffer[i].Counters[1];
  254. } else {
  255. pHCD->LargeAllocs += LookasideBuffer[i].Counters[0];
  256. pHCD->LargeFrees += LookasideBuffer[i].Counters[1];
  257. }
  258. }
  259. }
  260. pHCD->LookasideBlocks = pHCD->LookasideFrees - pHCD->LookasideAllocs;
  261. //
  262. // Calculate the totals
  263. //
  264. pHCD->TotalAllocs = pHCD->SmallAllocs + pHCD->MedAllocs + pHCD->LargeAllocs;
  265. pHCD->TotalFrees = pHCD->SmallFrees + pHCD->MedFrees + pHCD->LargeFrees;
  266. //
  267. // Set the difference between allocs and frees
  268. //
  269. pHCD->DiffOperations = pHCD->TotalAllocs - pHCD->TotalFrees;
  270. pHCD->AllocTime = 0;
  271. pHCD->AllocTime = 0;
  272. //
  273. // Determine the alloc/free rates
  274. //
  275. if (ReadProcessMemory(hProcess, CrtHeap.LargeBlocksIndex, &HeapIndex, sizeof(HeapIndex), NULL)) {
  276. if (HeapIndex.PerfData.OperationTime[0]) {
  277. pHCD->AllocTime = HeapIndex.PerfData.CountFrequence / HeapIndex.PerfData.OperationTime[0];
  278. }
  279. if (HeapIndex.PerfData.OperationTime[1]) {
  280. pHCD->FreeTime = HeapIndex.PerfData.CountFrequence / HeapIndex.PerfData.OperationTime[1];
  281. }
  282. }
  283. return TRUE;
  284. }
  285. DWORD APIENTRY
  286. CollectHeapObjectData (
  287. IN OUT LPVOID *lppData,
  288. IN OUT LPDWORD lpcbTotalBytes,
  289. IN OUT LPDWORD lpNumObjectTypes
  290. )
  291. /*++
  292. Routine Description:
  293. This routine will return the data for the heap object
  294. Arguments:
  295. IN OUT LPVOID *lppData
  296. IN: pointer to the address of the buffer to receive the completed
  297. PerfDataBlock and subordinate structures. This routine will
  298. append its data to the buffer starting at the point referenced
  299. by *lppData.
  300. OUT: points to the first byte after the data structure added by this
  301. routine. This routine updated the value at lppdata after appending
  302. its data.
  303. IN OUT LPDWORD lpcbTotalBytes
  304. IN: the address of the DWORD that tells the size in bytes of the
  305. buffer referenced by the lppData argument
  306. OUT: the number of bytes added by this routine is writted to the
  307. DWORD pointed to by this argument
  308. IN OUT LPDWORD NumObjectTypes
  309. IN: the address of the DWORD to receive the number of objects added
  310. by this routine
  311. OUT: the number of objects added by this routine is writted to the
  312. DWORD pointed to by this argument
  313. Returns:
  314. 0 if successful, else Win 32 error code of failure
  315. --*/
  316. {
  317. LONG lReturn = ERROR_SUCCESS;
  318. DWORD TotalLen; // Length of the total return block
  319. PHEAP_DATA_DEFINITION pHeapDataDefinition;
  320. PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
  321. PHEAP_COUNTER_DATA pHCD;
  322. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  323. ULONG ProcessNumber;
  324. ULONG NumHeapInstances;
  325. ULONG HeapNumber;
  326. ULONG ProcessBufferOffset;
  327. UNICODE_STRING HeapName;
  328. WCHAR HeapNameBuffer[MAX_THREAD_NAME_LENGTH+1];
  329. BOOL bMoreProcesses = FALSE;
  330. HeapName.Length =
  331. HeapName.MaximumLength = (MAX_THREAD_NAME_LENGTH + 1) * sizeof(WCHAR);
  332. HeapName.Buffer = HeapNameBuffer;
  333. pHeapDataDefinition = (HEAP_DATA_DEFINITION *) *lppData;
  334. //
  335. // Get the page size from the system
  336. //
  337. if (!PageSize) {
  338. SYSTEM_INFO SystemInfo;
  339. GetSystemInfo(&SystemInfo);
  340. PageSize = SystemInfo.dwPageSize;
  341. }
  342. //
  343. // Check for sufficient space for Thread object type definition
  344. //
  345. TotalLen = sizeof(HEAP_DATA_DEFINITION) +
  346. sizeof(PERF_INSTANCE_DEFINITION) +
  347. sizeof(HEAP_COUNTER_DATA);
  348. if ( *lpcbTotalBytes < TotalLen ) {
  349. *lpcbTotalBytes = (DWORD) 0;
  350. *lpNumObjectTypes = (DWORD) 0;
  351. return ERROR_MORE_DATA;
  352. }
  353. //
  354. // Define the heap data block
  355. //
  356. memcpy(pHeapDataDefinition,
  357. &HeapDataDefinition,
  358. sizeof(HEAP_DATA_DEFINITION));
  359. pHeapDataDefinition->HeapObjectType.PerfTime = PerfTime;
  360. ProcessBufferOffset = 0;
  361. //
  362. // Now collect data for each process
  363. //
  364. ProcessNumber = 0;
  365. NumHeapInstances = 0;
  366. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)pProcessBuffer;
  367. pPerfInstanceDefinition =
  368. (PPERF_INSTANCE_DEFINITION)&pHeapDataDefinition[1];
  369. TotalLen = sizeof(HEAP_DATA_DEFINITION);
  370. if (ProcessInfo) {
  371. if (ProcessInfo->NextEntryOffset != 0) {
  372. bMoreProcesses = TRUE;
  373. }
  374. }
  375. while ( bMoreProcesses && (ProcessInfo != NULL)) {
  376. HANDLE hProcess;
  377. NTSTATUS Status;
  378. PROCESS_BASIC_INFORMATION BasicInfo;
  379. //
  380. // Get a handle to the process.
  381. //
  382. hProcess = OpenProcess( PROCESS_QUERY_INFORMATION |
  383. PROCESS_VM_READ,
  384. FALSE, (DWORD)(ULONGLONG)ProcessInfo->UniqueProcessId );
  385. if ( hProcess ) {
  386. //
  387. // Get the process PEB
  388. //
  389. Status = NtQueryInformationProcess(
  390. hProcess,
  391. ProcessBasicInformation,
  392. &BasicInfo,
  393. sizeof(BasicInfo),
  394. NULL
  395. );
  396. if ( NT_SUCCESS(Status) ) {
  397. ULONG NumberOfHeaps;
  398. PVOID ProcessHeaps[MAX_HEAP_COUNT];
  399. PVOID HeapBuffer;
  400. PPEB Peb;
  401. Peb = BasicInfo.PebBaseAddress;
  402. //
  403. // Read the heaps from the process PEB
  404. //
  405. if (!ReadProcessMemory(hProcess, &Peb->NumberOfHeaps, &NumberOfHeaps, sizeof(NumberOfHeaps), NULL)) {
  406. goto READERROR;
  407. }
  408. //
  409. // Limit the number of heaps to be read
  410. //
  411. if (NumberOfHeaps > MAX_HEAP_COUNT) {
  412. NumberOfHeaps = MAX_HEAP_COUNT;
  413. }
  414. if (!ReadProcessMemory(hProcess, &Peb->ProcessHeaps, &HeapBuffer, sizeof(HeapBuffer), NULL)) {
  415. goto READERROR;
  416. }
  417. if (!ReadProcessMemory(hProcess, HeapBuffer, &ProcessHeaps, NumberOfHeaps * sizeof(PVOID), NULL)) {
  418. goto READERROR;
  419. }
  420. //
  421. // Loop through the heaps and retireve the data
  422. //
  423. for (HeapNumber = 0; HeapNumber < NumberOfHeaps; HeapNumber++) {
  424. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  425. (MAX_THREAD_NAME_LENGTH+1+sizeof(DWORD))*
  426. sizeof(WCHAR) +
  427. sizeof (HEAP_COUNTER_DATA);
  428. if ( *lpcbTotalBytes < TotalLen ) {
  429. *lpcbTotalBytes = (DWORD) 0;
  430. *lpNumObjectTypes = (DWORD) 0;
  431. CloseHandle( hProcess );
  432. return ERROR_MORE_DATA;
  433. }
  434. //
  435. // Build the monitor instance based on the process name and
  436. // heap address
  437. //
  438. RtlIntegerToUnicodeString( (ULONG)(ULONGLONG)ProcessHeaps[HeapNumber],
  439. 16,
  440. &HeapName);
  441. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  442. (PVOID *) &pHCD,
  443. PROCESS_OBJECT_TITLE_INDEX,
  444. ProcessNumber,
  445. (DWORD)-1,
  446. HeapName.Buffer);
  447. pHCD->CounterBlock.ByteLength = sizeof(HEAP_COUNTER_DATA);
  448. //
  449. // Get the data from the heap
  450. //
  451. if (ReadHeapData ( hProcess,
  452. HeapNumber,
  453. (PHEAP)ProcessHeaps[HeapNumber],
  454. pHCD) ) {
  455. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pHCD[1];
  456. NumHeapInstances++;
  457. }
  458. }
  459. }
  460. READERROR:
  461. CloseHandle( hProcess );
  462. }
  463. ProcessNumber++;
  464. //
  465. // Move to the next process, if any
  466. //
  467. if (ProcessInfo->NextEntryOffset == 0) {
  468. bMoreProcesses = FALSE;
  469. continue;
  470. }
  471. ProcessBufferOffset += ProcessInfo->NextEntryOffset;
  472. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
  473. &pProcessBuffer[ProcessBufferOffset];
  474. }
  475. // Note number of heap instances
  476. pHeapDataDefinition->HeapObjectType.NumInstances =
  477. NumHeapInstances;
  478. //
  479. // Now we know how large an area we used for the
  480. // heap definition, so we can update the offset
  481. // to the next object definition
  482. //
  483. *lpcbTotalBytes =
  484. pHeapDataDefinition->HeapObjectType.TotalByteLength =
  485. (DWORD)((PCHAR) pPerfInstanceDefinition -
  486. (PCHAR) pHeapDataDefinition);
  487. #if DBG
  488. if (*lpcbTotalBytes > TotalLen ) {
  489. DbgPrint ("\nPERFPROC: Heap Perf Ctr. Instance Size Underestimated:");
  490. DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
  491. }
  492. #endif
  493. *lppData = (LPVOID)pPerfInstanceDefinition;
  494. *lpNumObjectTypes = 1;
  495. return lReturn;
  496. }