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.

382 lines
13 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. perfproc.c
  5. Abstract:
  6. This file implements an Performance Object that presents
  7. Image details performance object data
  8. Created:
  9. Bob Watson 22-Oct-1996
  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 "dataproc.h"
  27. static BOOL bOldestProcessTime = FALSE;
  28. static LARGE_INTEGER OldestProcessTime = {0,0};
  29. DWORD APIENTRY
  30. CollectProcessObjectData (
  31. IN OUT LPVOID *lppData,
  32. IN OUT LPDWORD lpcbTotalBytes,
  33. IN OUT LPDWORD lpNumObjectTypes
  34. )
  35. /*++
  36. Routine Description:
  37. This routine will return the data for the processor object
  38. Arguments:
  39. IN OUT LPVOID *lppData
  40. IN: pointer to the address of the buffer to receive the completed
  41. PerfDataBlock and subordinate structures. This routine will
  42. append its data to the buffer starting at the point referenced
  43. by *lppData.
  44. OUT: points to the first byte after the data structure added by this
  45. routine. This routine updated the value at lppdata after appending
  46. its data.
  47. IN OUT LPDWORD lpcbTotalBytes
  48. IN: the address of the DWORD that tells the size in bytes of the
  49. buffer referenced by the lppData argument
  50. OUT: the number of bytes added by this routine is writted to the
  51. DWORD pointed to by this argument
  52. IN OUT LPDWORD NumObjectTypes
  53. IN: the address of the DWORD to receive the number of objects added
  54. by this routine
  55. OUT: the number of objects added by this routine is writted to the
  56. DWORD pointed to by this argument
  57. Returns:
  58. 0 if successful, else Win 32 error code of failure
  59. --*/
  60. {
  61. DWORD TotalLen; // Length of the total return block
  62. PSYSTEM_PROCESS_INFORMATION ProcessInfo;
  63. PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition;
  64. PPROCESS_DATA_DEFINITION pProcessDataDefinition;
  65. PPROCESS_COUNTER_DATA pPCD;
  66. PROCESS_COUNTER_DATA pcdTotal;
  67. ULONG NumProcessInstances;
  68. BOOLEAN NullProcess;
  69. PUNICODE_STRING pProcessName;
  70. ULONG ProcessBufferOffset;
  71. pProcessDataDefinition = (PROCESS_DATA_DEFINITION *) *lppData;
  72. //
  73. // Check for sufficient space for Process object type definition
  74. //
  75. TotalLen = sizeof(PROCESS_DATA_DEFINITION) +
  76. sizeof (PERF_INSTANCE_DEFINITION) +
  77. MAX_VALUE_NAME_LENGTH +
  78. sizeof(PROCESS_COUNTER_DATA);
  79. if ( *lpcbTotalBytes < TotalLen ) {
  80. *lpcbTotalBytes = 0;
  81. *lpNumObjectTypes = 0;
  82. return ERROR_MORE_DATA;
  83. }
  84. //
  85. // Define Process data block
  86. //
  87. memcpy(pProcessDataDefinition,
  88. &ProcessDataDefinition,
  89. sizeof(PROCESS_DATA_DEFINITION));
  90. pProcessDataDefinition->ProcessObjectType.PerfTime = PerfTime;
  91. ProcessBufferOffset = 0;
  92. // Now collect data for each process
  93. NumProcessInstances = 0;
  94. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION) pProcessBuffer;
  95. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  96. &pProcessDataDefinition[1];
  97. // adjust TotalLen to be the size of the buffer already in use
  98. TotalLen = sizeof (PROCESS_DATA_DEFINITION);
  99. // zero the total instance buffer
  100. memset (&pcdTotal, 0, sizeof (pcdTotal));
  101. while ( ProcessInfo != NULL ) {
  102. // see if this instance will fit
  103. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  104. ((MAX_PROCESS_NAME_LENGTH+1+sizeof(DWORD)) * sizeof(WCHAR)) +
  105. sizeof (PROCESS_COUNTER_DATA);
  106. if ( *lpcbTotalBytes < TotalLen ) {
  107. *lpcbTotalBytes = 0;
  108. *lpNumObjectTypes = 0;
  109. return ERROR_MORE_DATA;
  110. }
  111. // check for Live processes
  112. // (i.e. name or threads)
  113. pProcessName = NULL;
  114. if ((ProcessInfo->ImageName.Buffer != NULL) ||
  115. (ProcessInfo->NumberOfThreads > 0)){
  116. // thread is not Dead
  117. // get process name
  118. pProcessName = GetProcessShortName (ProcessInfo);
  119. NullProcess = FALSE;
  120. } else {
  121. // thread is dead
  122. NullProcess = TRUE;
  123. }
  124. if ( !NullProcess ) {
  125. // get the old process creation time the first time we are in
  126. // this routine
  127. if (!bOldestProcessTime) {
  128. if (OldestProcessTime.QuadPart <= 0) {
  129. OldestProcessTime = ProcessInfo->CreateTime;
  130. } else if (ProcessInfo->CreateTime.QuadPart > 0) {
  131. // both time values are not zero, see which one is smaller
  132. if (OldestProcessTime.QuadPart >
  133. ProcessInfo->CreateTime.QuadPart) {
  134. OldestProcessTime = ProcessInfo->CreateTime;
  135. }
  136. }
  137. }
  138. // get Pool usage for this process
  139. NumProcessInstances++;
  140. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  141. (PVOID *) &pPCD,
  142. 0,
  143. 0,
  144. (DWORD)-1,
  145. (pProcessName ? pProcessName->Buffer : L"")
  146. );
  147. // test structure for Quadword Alignment
  148. assert (((DWORD)(pPCD) & 0x00000007) == 0);
  149. //
  150. // Format and collect Process data
  151. //
  152. pPCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (PROCESS_COUNTER_DATA));
  153. //
  154. // Convert User time from 100 nsec units to counter frequency.
  155. //
  156. pcdTotal.ProcessorTime +=
  157. pPCD->ProcessorTime = ProcessInfo->KernelTime.QuadPart +
  158. ProcessInfo->UserTime.QuadPart;
  159. pcdTotal.UserTime +=
  160. pPCD->UserTime = ProcessInfo->UserTime.QuadPart;
  161. pcdTotal.KernelTime +=
  162. pPCD->KernelTime = ProcessInfo->KernelTime.QuadPart;
  163. pcdTotal.PeakVirtualSize +=
  164. pPCD->PeakVirtualSize = ProcessInfo->PeakVirtualSize;
  165. pcdTotal.VirtualSize +=
  166. pPCD->VirtualSize = ProcessInfo->VirtualSize;
  167. pcdTotal.PageFaults +=
  168. pPCD->PageFaults = ProcessInfo->PageFaultCount;
  169. pcdTotal.PeakWorkingSet +=
  170. pPCD->PeakWorkingSet = ProcessInfo->PeakWorkingSetSize;
  171. pcdTotal.TotalWorkingSet +=
  172. pPCD->TotalWorkingSet = ProcessInfo->WorkingSetSize;
  173. #ifdef _DATAPROC_PRIVATE_WS_
  174. pcdTotal.PrivateWorkingSet +=
  175. pPCD->PrivateWorkingSet = ProcessInfo->PrivateWorkingSetSize;
  176. pcdTotal.SharedWorkingSet +=
  177. pPCD->SharedWorkingSet =
  178. ProcessInfo->WorkingSetSize -
  179. ProcessInfo->PrivateWorkingSetSize;
  180. #endif //_DATAPROC_PRIVATE_WS_
  181. pcdTotal.PeakPageFile +=
  182. pPCD->PeakPageFile = ProcessInfo->PeakPagefileUsage;
  183. pcdTotal.PageFile +=
  184. pPCD->PageFile = ProcessInfo->PagefileUsage;
  185. pcdTotal.PrivatePages +=
  186. pPCD->PrivatePages = ProcessInfo->PrivatePageCount;
  187. pcdTotal.ThreadCount +=
  188. pPCD->ThreadCount = ProcessInfo->NumberOfThreads;
  189. // base priority is not totaled
  190. pPCD->BasePriority = ProcessInfo->BasePriority;
  191. // elpased time is not totaled
  192. if (bOldestProcessTime &&
  193. (ProcessInfo->CreateTime.QuadPart <= 0)) {
  194. pPCD->ElapsedTime = OldestProcessTime.QuadPart;
  195. } else {
  196. pPCD->ElapsedTime = ProcessInfo->CreateTime.QuadPart;
  197. }
  198. pPCD->ProcessId = HandleToUlong(ProcessInfo->UniqueProcessId);
  199. pPCD->CreatorProcessId = HandleToUlong(ProcessInfo->InheritedFromUniqueProcessId);
  200. pcdTotal.PagedPool +=
  201. pPCD->PagedPool = (DWORD)ProcessInfo->QuotaPagedPoolUsage;
  202. pcdTotal.NonPagedPool +=
  203. pPCD->NonPagedPool = (DWORD)ProcessInfo->QuotaNonPagedPoolUsage;
  204. pcdTotal.HandleCount +=
  205. pPCD->HandleCount = (DWORD)ProcessInfo->HandleCount;
  206. // update I/O counters
  207. pcdTotal.ReadOperationCount +=
  208. pPCD->ReadOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
  209. pcdTotal.DataOperationCount +=
  210. pPCD->DataOperationCount = ProcessInfo->ReadOperationCount.QuadPart;
  211. pcdTotal.WriteOperationCount +=
  212. pPCD->WriteOperationCount = ProcessInfo->WriteOperationCount.QuadPart;
  213. pcdTotal.DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
  214. pPCD->DataOperationCount += ProcessInfo->WriteOperationCount.QuadPart;
  215. pcdTotal.OtherOperationCount +=
  216. pPCD->OtherOperationCount = ProcessInfo->OtherOperationCount.QuadPart;
  217. pcdTotal.ReadTransferCount +=
  218. pPCD->ReadTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
  219. pcdTotal.DataTransferCount +=
  220. pPCD->DataTransferCount = ProcessInfo->ReadTransferCount.QuadPart;
  221. pcdTotal.WriteTransferCount +=
  222. pPCD->WriteTransferCount = ProcessInfo->WriteTransferCount.QuadPart;
  223. pcdTotal.DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
  224. pPCD->DataTransferCount += ProcessInfo->WriteTransferCount.QuadPart;
  225. pcdTotal.OtherTransferCount +=
  226. pPCD->OtherTransferCount = ProcessInfo->OtherTransferCount.QuadPart;
  227. // set perfdata pointer to next byte
  228. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
  229. }
  230. // exit if this was the last process in list
  231. if (ProcessInfo->NextEntryOffset == 0) {
  232. break;
  233. }
  234. // point to next buffer in list
  235. ProcessBufferOffset += ProcessInfo->NextEntryOffset;
  236. ProcessInfo = (PSYSTEM_PROCESS_INFORMATION)
  237. &pProcessBuffer[ProcessBufferOffset];
  238. }
  239. if (NumProcessInstances > 0) {
  240. // see if the total instance will fit
  241. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  242. (MAX_PROCESS_NAME_LENGTH+1+sizeof(DWORD))*
  243. sizeof(WCHAR) +
  244. sizeof (PROCESS_COUNTER_DATA);
  245. if ( *lpcbTotalBytes < TotalLen ) {
  246. *lpcbTotalBytes = 0;
  247. *lpNumObjectTypes = 0;
  248. return ERROR_MORE_DATA;
  249. }
  250. // it looks like it will fit so create "total" instance
  251. NumProcessInstances++;
  252. // set the Total Elapsed Time to be the current time so that it will
  253. // show up as 0 when displayed.
  254. pcdTotal.ElapsedTime = pProcessDataDefinition->ProcessObjectType.PerfTime.QuadPart;
  255. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  256. (PVOID *) &pPCD,
  257. 0,
  258. 0,
  259. (DWORD)-1,
  260. wszTotal);
  261. // test structure for Quadword Alignment
  262. assert (((DWORD)(pPCD) & 0x00000007) == 0);
  263. //
  264. // Format and collect Process data
  265. //
  266. memcpy (pPCD, &pcdTotal, sizeof (pcdTotal));
  267. pPCD->CounterBlock.ByteLength = QWORD_MULTIPLE(sizeof (PROCESS_COUNTER_DATA));
  268. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pPCD[1];
  269. }
  270. // flag so we don't have to get the oldest Process Creation time again.
  271. bOldestProcessTime = TRUE;
  272. // Note number of process instances
  273. pProcessDataDefinition->ProcessObjectType.NumInstances =
  274. NumProcessInstances;
  275. //
  276. // Now we know how large an area we used for the
  277. // Process definition, so we can update the offset
  278. // to the next object definition
  279. //
  280. *lpcbTotalBytes =
  281. pProcessDataDefinition->ProcessObjectType.TotalByteLength =
  282. QWORD_MULTIPLE(
  283. (DWORD)((PCHAR) pPerfInstanceDefinition -
  284. (PCHAR) pProcessDataDefinition));
  285. #if DBG
  286. if (*lpcbTotalBytes > TotalLen ) {
  287. DbgPrint ("\nPERFPROC: Process Perf Ctr. Instance Size Underestimated:");
  288. DbgPrint ("\nPERFPROC: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
  289. }
  290. #endif
  291. *lppData = (LPVOID) ((PCHAR) pProcessDataDefinition + *lpcbTotalBytes);
  292. *lpNumObjectTypes = 1;
  293. return ERROR_SUCCESS;
  294. }