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.

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