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.

626 lines
20 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. perfsrv.c
  5. Abstract:
  6. This file implements a Performance Object that presents
  7. Server 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 <ntddnfs.h>
  19. #include <windows.h>
  20. #include <assert.h>
  21. #include <lmerr.h>
  22. #include <lmapibuf.h>
  23. #include <lmwksta.h>
  24. #include <srvfsctl.h>
  25. #include <winperf.h>
  26. #include <ntprfctr.h>
  27. #include <assert.h>
  28. #include <perfutil.h>
  29. #include "perfnet.h"
  30. #include "netsvcmc.h"
  31. #include "datasrv.h"
  32. #include "datasrvq.h"
  33. #define MAX_SRVQ_NAME_LENGTH 16
  34. static HANDLE hSrv = NULL;
  35. static SRV_QUEUE_STATISTICS *pSrvQueueStatistics = NULL;
  36. static DWORD dwDataBufferLength = 0L;
  37. static SYSTEM_BASIC_INFORMATION BasicInfo;
  38. static BOOL bSrvQOk = TRUE;
  39. DWORD APIENTRY
  40. OpenServerObject (
  41. IN LPWSTR lpValueName
  42. )
  43. {
  44. STRING DeviceName;
  45. UNICODE_STRING DeviceNameU;
  46. OBJECT_ATTRIBUTES ObjectAttributes;
  47. IO_STATUS_BLOCK IoStatusBlock;
  48. NTSTATUS status;
  49. UNREFERENCED_PARAMETER (lpValueName);
  50. // open the handle to the server for data collection
  51. //
  52. // Get access to the Server for it's data
  53. //
  54. RtlInitString(&DeviceName, SERVER_DEVICE_NAME);
  55. DeviceNameU.Buffer = NULL;
  56. status = RtlAnsiStringToUnicodeString(&DeviceNameU, &DeviceName, TRUE);
  57. if (NT_SUCCESS(status)) {
  58. InitializeObjectAttributes(&ObjectAttributes,
  59. &DeviceNameU,
  60. OBJ_CASE_INSENSITIVE,
  61. NULL,
  62. NULL
  63. );
  64. status = NtOpenFile(&hSrv,
  65. SYNCHRONIZE,
  66. &ObjectAttributes,
  67. &IoStatusBlock,
  68. 0,
  69. FILE_SYNCHRONOUS_IO_NONALERT
  70. );
  71. }
  72. if (!NT_SUCCESS(status)) {
  73. hSrv = NULL;
  74. bSrvQOk = FALSE;
  75. ReportEvent (hEventLog,
  76. EVENTLOG_ERROR_TYPE,
  77. 0,
  78. PERFNET_UNABLE_OPEN_SERVER,
  79. NULL,
  80. 0,
  81. sizeof(DWORD),
  82. NULL,
  83. (LPVOID)&status);
  84. }
  85. if (DeviceNameU.Buffer) {
  86. RtlFreeUnicodeString(&DeviceNameU);
  87. }
  88. return (DWORD)RtlNtStatusToDosError(status);
  89. }
  90. DWORD APIENTRY
  91. OpenServerQueueObject (
  92. IN LPWSTR szValueName
  93. )
  94. {
  95. NTSTATUS status;
  96. UNREFERENCED_PARAMETER (szValueName);
  97. //
  98. // collect basic and static processor data
  99. //
  100. status = NtQuerySystemInformation(
  101. SystemBasicInformation,
  102. &BasicInfo,
  103. sizeof(SYSTEM_BASIC_INFORMATION),
  104. NULL
  105. );
  106. assert (NT_SUCCESS(status));
  107. if (!NT_SUCCESS(status)) {
  108. // all we really want is the number of processors so
  109. // if we can't get that from the system, then we'll
  110. // substitute 32 for the number
  111. BasicInfo.NumberOfProcessors = 32;
  112. status = ERROR_SUCCESS;
  113. }
  114. // compute the various buffer sizes required
  115. dwDataBufferLength = sizeof(SRV_QUEUE_STATISTICS) *
  116. (BasicInfo.NumberOfProcessors + 1);
  117. pSrvQueueStatistics = (SRV_QUEUE_STATISTICS *)ALLOCMEM (
  118. hLibHeap, HEAP_ZERO_MEMORY, dwDataBufferLength);
  119. // if memory allocation failed, then no server queue stats will
  120. // be returned.
  121. assert (pSrvQueueStatistics != NULL);
  122. if (pSrvQueueStatistics == NULL) {
  123. bSrvQOk = FALSE;
  124. }
  125. return ERROR_SUCCESS;
  126. }
  127. DWORD APIENTRY
  128. CollectServerObjectData(
  129. IN OUT LPVOID *lppData,
  130. IN OUT LPDWORD lpcbTotalBytes,
  131. IN OUT LPDWORD lpNumObjectTypes
  132. )
  133. /*++
  134. Routine Description:
  135. This routine will return the data for the Physical Disk object
  136. Arguments:
  137. IN OUT LPVOID *lppData
  138. IN: pointer to the address of the buffer to receive the completed
  139. PerfDataBlock and subordinate structures. This routine will
  140. append its data to the buffer starting at the point referenced
  141. by *lppData.
  142. OUT: points to the first byte after the data structure added by this
  143. routine. This routine updated the value at lppdata after appending
  144. its data.
  145. IN OUT LPDWORD lpcbTotalBytes
  146. IN: the address of the DWORD that tells the size in bytes of the
  147. buffer referenced by the lppData argument
  148. OUT: the number of bytes added by this routine is writted to the
  149. DWORD pointed to by this argument
  150. IN OUT LPDWORD NumObjectTypes
  151. IN: the address of the DWORD to receive the number of objects added
  152. by this routine
  153. OUT: the number of objects added by this routine is writted to the
  154. DWORD pointed to by this argument
  155. Returns:
  156. 0 if successful, else Win 32 error code of failure
  157. --*/
  158. {
  159. DWORD TotalLen; // Length of the total return block
  160. NTSTATUS Status = ERROR_SUCCESS;
  161. IO_STATUS_BLOCK IoStatusBlock;
  162. SRV_DATA_DEFINITION *pSrvDataDefinition;
  163. SRV_COUNTER_DATA *pSCD;
  164. SRV_STATISTICS SrvStatistics;
  165. if (hSrv == NULL) {
  166. // bail out if the server didn't get opened.
  167. *lpcbTotalBytes = (DWORD) 0;
  168. *lpNumObjectTypes = (DWORD) 0;
  169. return ERROR_SUCCESS;
  170. }
  171. //
  172. // Check for sufficient space for server data
  173. //
  174. TotalLen = sizeof(SRV_DATA_DEFINITION) +
  175. sizeof(SRV_COUNTER_DATA);
  176. if ( *lpcbTotalBytes < TotalLen ) {
  177. // bail out if the data won't fit in the caller's buffer
  178. // or the server didn't get opened.
  179. *lpcbTotalBytes = (DWORD) 0;
  180. *lpNumObjectTypes = (DWORD) 0;
  181. return ERROR_MORE_DATA;
  182. }
  183. //
  184. // Define objects data block
  185. //
  186. pSrvDataDefinition = (SRV_DATA_DEFINITION *) *lppData;
  187. memcpy (pSrvDataDefinition,
  188. &SrvDataDefinition,
  189. sizeof(SRV_DATA_DEFINITION));
  190. //
  191. // Format and collect server data
  192. //
  193. pSCD = (PSRV_COUNTER_DATA)&pSrvDataDefinition[1];
  194. // test for quadword alignment of the structure
  195. assert (((DWORD)(pSCD) & 0x00000007) == 0);
  196. pSCD->CounterBlock.ByteLength = sizeof(SRV_COUNTER_DATA);
  197. Status = NtFsControlFile(hSrv,
  198. NULL,
  199. NULL,
  200. NULL,
  201. &IoStatusBlock,
  202. FSCTL_SRV_GET_STATISTICS,
  203. NULL,
  204. 0,
  205. &SrvStatistics,
  206. sizeof(SrvStatistics)
  207. );
  208. if ( NT_SUCCESS(Status) ) {
  209. pSCD->TotalBytes = SrvStatistics.TotalBytesSent.QuadPart +
  210. SrvStatistics.TotalBytesReceived.QuadPart;
  211. pSCD->TotalBytesReceived = SrvStatistics.TotalBytesReceived.QuadPart;
  212. pSCD->TotalBytesSent = SrvStatistics.TotalBytesSent.QuadPart;
  213. pSCD->SessionsTimedOut = SrvStatistics.SessionsTimedOut;
  214. pSCD->SessionsErroredOut = SrvStatistics.SessionsErroredOut;
  215. pSCD->SessionsLoggedOff = SrvStatistics.SessionsLoggedOff;
  216. pSCD->SessionsForcedLogOff = SrvStatistics.SessionsForcedLogOff;
  217. pSCD->LogonErrors = SrvStatistics.LogonErrors;
  218. pSCD->AccessPermissionErrors = SrvStatistics.AccessPermissionErrors;
  219. pSCD->GrantedAccessErrors = SrvStatistics.GrantedAccessErrors;
  220. pSCD->SystemErrors = SrvStatistics.SystemErrors;
  221. pSCD->BlockingSmbsRejected = SrvStatistics.BlockingSmbsRejected;
  222. pSCD->WorkItemShortages = SrvStatistics.WorkItemShortages;
  223. pSCD->TotalFilesOpened = SrvStatistics.TotalFilesOpened;
  224. pSCD->CurrentOpenFiles = SrvStatistics.CurrentNumberOfOpenFiles;
  225. pSCD->CurrentSessions = SrvStatistics.CurrentNumberOfSessions;
  226. pSCD->CurrentOpenSearches = SrvStatistics.CurrentNumberOfOpenSearches;
  227. pSCD->CurrentNonPagedPoolUsage = SrvStatistics.CurrentNonPagedPoolUsage;
  228. pSCD->NonPagedPoolFailures = SrvStatistics.NonPagedPoolFailures;
  229. pSCD->PeakNonPagedPoolUsage = SrvStatistics.PeakNonPagedPoolUsage;
  230. pSCD->CurrentPagedPoolUsage = SrvStatistics.CurrentPagedPoolUsage;
  231. pSCD->PagedPoolFailures = SrvStatistics.PagedPoolFailures;
  232. pSCD->PeakPagedPoolUsage = SrvStatistics.PeakPagedPoolUsage;
  233. pSCD->ContextBlockQueueRate = SrvStatistics.TotalWorkContextBlocksQueued.Count;
  234. pSCD->NetLogon =
  235. pSCD->NetLogonTotal = SrvStatistics.SessionLogonAttempts;
  236. } else {
  237. // log an event describing the error
  238. DWORD dwData[4];
  239. DWORD dwDataIndex = 0;
  240. dwData[dwDataIndex++] = Status;
  241. dwData[dwDataIndex++] = IoStatusBlock.Status;
  242. dwData[dwDataIndex++] = (DWORD)IoStatusBlock.Information;
  243. ReportEvent (hEventLog,
  244. EVENTLOG_ERROR_TYPE, // error type
  245. 0, // category (not used)
  246. PERFNET_UNABLE_READ_SERVER, // error code
  247. NULL, // SID (not used),
  248. 0, // number of strings
  249. dwDataIndex * sizeof(DWORD), // sizeof raw data
  250. NULL, // message text array
  251. (LPVOID)&dwData[0]); // raw data
  252. //
  253. // Failure to access Server: clear counters to 0
  254. //
  255. memset(pSCD, 0, sizeof(SRV_COUNTER_DATA));
  256. pSCD->CounterBlock.ByteLength = sizeof(SRV_COUNTER_DATA);
  257. }
  258. *lppData = (LPVOID)&pSCD[1];
  259. *lpcbTotalBytes = (DWORD)((LPBYTE)&pSCD[1] - (LPBYTE)pSrvDataDefinition);
  260. *lpNumObjectTypes = 1;
  261. return ERROR_SUCCESS;
  262. }
  263. DWORD APIENTRY
  264. CollectServerQueueObjectData(
  265. IN OUT LPVOID *lppData,
  266. IN OUT LPDWORD lpcbTotalBytes,
  267. IN OUT LPDWORD lpNumObjectTypes
  268. )
  269. /*++
  270. Routine Description:
  271. This routine will return the data for the Physical Disk object
  272. Arguments:
  273. IN OUT LPVOID *lppData
  274. IN: pointer to the address of the buffer to receive the completed
  275. PerfDataBlock and subordinate structures. This routine will
  276. append its data to the buffer starting at the point referenced
  277. by *lppData.
  278. OUT: points to the first byte after the data structure added by this
  279. routine. This routine updated the value at lppdata after appending
  280. its data.
  281. IN OUT LPDWORD lpcbTotalBytes
  282. IN: the address of the DWORD that tells the size in bytes of the
  283. buffer referenced by the lppData argument
  284. OUT: the number of bytes added by this routine is writted to the
  285. DWORD pointed to by this argument
  286. IN OUT LPDWORD NumObjectTypes
  287. IN: the address of the DWORD to receive the number of objects added
  288. by this routine
  289. OUT: the number of objects added by this routine is writted to the
  290. DWORD pointed to by this argument
  291. Returns:
  292. 0 if successful, else Win 32 error code of failure
  293. --*/
  294. {
  295. DWORD TotalLen; // Length of the total return block
  296. LONG nQueue;
  297. NTSTATUS Status = ERROR_SUCCESS;
  298. IO_STATUS_BLOCK IoStatusBlock;
  299. SRVQ_DATA_DEFINITION *pSrvQDataDefinition;
  300. PERF_INSTANCE_DEFINITION *pPerfInstanceDefinition;
  301. SRVQ_COUNTER_DATA *pSQCD;
  302. SRV_QUEUE_STATISTICS *pThisQueueStatistics;
  303. UNICODE_STRING QueueName;
  304. WCHAR QueueNameBuffer[MAX_SRVQ_NAME_LENGTH];
  305. if (!bSrvQOk) {
  306. *lpcbTotalBytes = (DWORD) 0;
  307. *lpNumObjectTypes = (DWORD) 0;
  308. return ERROR_SUCCESS;
  309. }
  310. //
  311. // Check for sufficient space for server data
  312. //
  313. TotalLen = sizeof(SRVQ_DATA_DEFINITION) +
  314. sizeof(PERF_INSTANCE_DEFINITION) +
  315. sizeof(SRVQ_COUNTER_DATA);
  316. if ( *lpcbTotalBytes < TotalLen ) {
  317. *lpcbTotalBytes = (DWORD) 0;
  318. *lpNumObjectTypes = (DWORD) 0;
  319. return ERROR_MORE_DATA;
  320. }
  321. // assign local pointer to current position in buffer
  322. pSrvQDataDefinition = (SRVQ_DATA_DEFINITION *) *lppData;
  323. //
  324. // Define perf object data block
  325. //
  326. memcpy (pSrvQDataDefinition,
  327. &SrvQDataDefinition,
  328. sizeof(SRVQ_DATA_DEFINITION));
  329. //
  330. // Format and collect server Queue data
  331. //
  332. QueueName.Length = 0;
  333. QueueName.MaximumLength = sizeof(QueueNameBuffer);
  334. QueueName.Buffer = QueueNameBuffer;
  335. Status = NtFsControlFile(hSrv,
  336. NULL,
  337. NULL,
  338. NULL,
  339. &IoStatusBlock,
  340. FSCTL_SRV_GET_QUEUE_STATISTICS,
  341. NULL,
  342. 0,
  343. pSrvQueueStatistics,
  344. dwDataBufferLength
  345. );
  346. if (NT_SUCCESS(Status)) {
  347. // server data was collected successfully so...
  348. // process each processor queue instance.
  349. nQueue = 0;
  350. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  351. &pSrvQDataDefinition[1];
  352. TotalLen = sizeof(SRVQ_DATA_DEFINITION);
  353. for (nQueue = 0; nQueue < BasicInfo.NumberOfProcessors; nQueue++) {
  354. // see if this instance will fit
  355. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  356. 8 + // size of 3 (unicode) digit queuelength name
  357. sizeof(SRVQ_COUNTER_DATA);
  358. if ( *lpcbTotalBytes < TotalLen ) {
  359. *lpcbTotalBytes = (DWORD) 0;
  360. *lpNumObjectTypes = (DWORD) 0;
  361. return ERROR_MORE_DATA;
  362. }
  363. RtlIntegerToUnicodeString(nQueue,
  364. 10,
  365. &QueueName);
  366. // there should be enough room for this instance so initialize it
  367. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  368. (PVOID *) &pSQCD,
  369. 0,
  370. 0,
  371. (DWORD)-1,
  372. QueueName.Buffer);
  373. pSQCD->CounterBlock.ByteLength = sizeof (SRVQ_COUNTER_DATA);
  374. // initialize pointers for this instance
  375. pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
  376. pSQCD->QueueLength = pThisQueueStatistics->QueueLength;
  377. pSQCD->ActiveThreads = pThisQueueStatistics->ActiveThreads;
  378. pSQCD->AvailableThreads = pThisQueueStatistics->AvailableThreads;
  379. pSQCD->AvailableWorkItems = pThisQueueStatistics->FreeWorkItems;
  380. pSQCD->BorrowedWorkItems = pThisQueueStatistics->StolenWorkItems;
  381. pSQCD->WorkItemShortages = pThisQueueStatistics->NeedWorkItem;
  382. pSQCD->CurrentClients = pThisQueueStatistics->CurrentClients;
  383. pSQCD->TotalBytesTransfered =
  384. pSQCD->BytesReceived = pThisQueueStatistics->BytesReceived.QuadPart;
  385. pSQCD->TotalBytesTransfered +=
  386. pSQCD->BytesSent = pThisQueueStatistics->BytesSent.QuadPart;
  387. pSQCD->TotalOperations =
  388. pSQCD->ReadOperations = pThisQueueStatistics->ReadOperations.QuadPart;
  389. pSQCD->TotalBytes =
  390. pSQCD->BytesRead = pThisQueueStatistics->BytesRead.QuadPart;
  391. pSQCD->TotalOperations +=
  392. pSQCD->WriteOperations = pThisQueueStatistics->WriteOperations.QuadPart;
  393. pSQCD->TotalBytes +=
  394. pSQCD->BytesWritten = pThisQueueStatistics->BytesWritten.QuadPart;
  395. pSQCD->TotalContextBlocksQueued = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
  396. // update the current pointer
  397. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSQCD[1];
  398. }
  399. RtlInitUnicodeString (&QueueName, (LPCWSTR)L"Blocking Queue");
  400. // now load the "blocking" queue data
  401. // see if this instance will fit
  402. TotalLen += sizeof(PERF_INSTANCE_DEFINITION) +
  403. QWORD_MULTIPLE(QueueName.Length + sizeof(WCHAR)) +
  404. sizeof (SRVQ_COUNTER_DATA);
  405. if ( *lpcbTotalBytes < TotalLen ) {
  406. // this instance won't fit so bail out
  407. *lpcbTotalBytes = (DWORD) 0;
  408. *lpNumObjectTypes = (DWORD) 0;
  409. return ERROR_MORE_DATA;
  410. }
  411. // there should be enough room for this instance so initialize it
  412. MonBuildInstanceDefinition(pPerfInstanceDefinition,
  413. (PVOID *) &pSQCD,
  414. 0,
  415. 0,
  416. (DWORD)-1,
  417. QueueName.Buffer);
  418. pSQCD->CounterBlock.ByteLength = sizeof(SRVQ_COUNTER_DATA);
  419. // initialize pointers for this instance
  420. pThisQueueStatistics = &pSrvQueueStatistics[nQueue];
  421. pSQCD->QueueLength = pThisQueueStatistics->QueueLength;
  422. pSQCD->ActiveThreads = pThisQueueStatistics->ActiveThreads;
  423. pSQCD->AvailableThreads = pThisQueueStatistics->AvailableThreads;
  424. pSQCD->AvailableWorkItems = 0;
  425. pSQCD->BorrowedWorkItems = 0;
  426. pSQCD->WorkItemShortages = 0;
  427. pSQCD->CurrentClients = 0;
  428. pSQCD->TotalBytesTransfered =
  429. pSQCD->BytesReceived = pThisQueueStatistics->BytesReceived.QuadPart;
  430. pSQCD->TotalBytesTransfered +=
  431. pSQCD->BytesSent = pThisQueueStatistics->BytesSent.QuadPart;
  432. pSQCD->ReadOperations = 0;
  433. pSQCD->TotalBytes =
  434. pSQCD->BytesRead = pThisQueueStatistics->BytesRead.QuadPart;
  435. pSQCD->WriteOperations = 0;
  436. pSQCD->TotalBytes +=
  437. pSQCD->BytesWritten = pThisQueueStatistics->BytesWritten.QuadPart;
  438. pSQCD->TotalOperations = 0;
  439. pSQCD->TotalContextBlocksQueued = pThisQueueStatistics->TotalWorkContextBlocksQueued.Count;
  440. nQueue++; // to include the Blocking Queue statistics entry
  441. // update the current pointer
  442. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)&pSQCD[1];
  443. // update queue (instance) count in object data block
  444. pSrvQDataDefinition->SrvQueueObjectType.NumInstances = nQueue;
  445. // update available length
  446. *lpcbTotalBytes =
  447. pSrvQDataDefinition->SrvQueueObjectType.TotalByteLength =
  448. (DWORD)((PCHAR) pPerfInstanceDefinition -
  449. (PCHAR) pSrvQDataDefinition);
  450. #if DBG
  451. if (*lpcbTotalBytes > TotalLen ) {
  452. DbgPrint ("\nPERFNET: Server Queue Perf Ctr. Instance Size Underestimated:");
  453. DbgPrint ("\nPERFNET: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
  454. }
  455. #endif
  456. *lppData = (LPVOID)pPerfInstanceDefinition;
  457. *lpNumObjectTypes = 1;
  458. return ERROR_SUCCESS;
  459. } else {
  460. // unable to read server queue data for some reason so don't return this
  461. // object
  462. // log an event describing the error
  463. DWORD dwData[4];
  464. DWORD dwDataIndex = 0;
  465. dwData[dwDataIndex++] = Status;
  466. dwData[dwDataIndex++] = IoStatusBlock.Status;
  467. dwData[dwDataIndex++] = (DWORD)IoStatusBlock.Information;
  468. ReportEvent (hEventLog,
  469. EVENTLOG_ERROR_TYPE, // error type
  470. 0, // category (not used)
  471. PERFNET_UNABLE_READ_SERVER_QUEUE, // error code
  472. NULL, // SID (not used),
  473. 0, // number of strings
  474. dwDataIndex * sizeof(DWORD), // sizeof raw data
  475. NULL, // message text array
  476. (LPVOID)&dwData[0]); // raw data
  477. *lpcbTotalBytes = (DWORD) 0;
  478. *lpNumObjectTypes = (DWORD) 0;
  479. return ERROR_SUCCESS;
  480. }
  481. }
  482. DWORD APIENTRY
  483. CloseServerObject ()
  484. {
  485. if (hSrv != NULL) {
  486. NtClose(hSrv);
  487. hSrv = NULL;
  488. }
  489. return ERROR_SUCCESS;
  490. }
  491. DWORD APIENTRY
  492. CloseServerQueueObject ()
  493. {
  494. if (hLibHeap != NULL) {
  495. if (pSrvQueueStatistics != NULL) {
  496. FREEMEM (hLibHeap, 0, pSrvQueueStatistics);
  497. pSrvQueueStatistics = NULL;
  498. }
  499. }
  500. return ERROR_SUCCESS;
  501. }