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.

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