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.

466 lines
18 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. physdisk.c
  5. Abstract:
  6. This file implements a Performance Object that presents
  7. Physical Disk 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. #pragma warning ( disable : 4201 )
  19. #include <ntdddisk.h>
  20. #include <windows.h>
  21. #include <ole2.h>
  22. #include <wmium.h>
  23. #pragma warning ( default : 4201 )
  24. #include <assert.h>
  25. #include <winperf.h>
  26. #include <ntprfctr.h>
  27. #include <perfutil.h>
  28. #include "perfdisk.h"
  29. #if _DBG_PRINT_INSTANCES
  30. #include <wtypes.h>
  31. #include <stdio.h>
  32. #include <stdlib.h>
  33. #endif
  34. #include "diskmsg.h"
  35. #include "dataphys.h"
  36. DWORD APIENTRY
  37. CollectPDiskObjectData(
  38. IN OUT LPVOID *lppData,
  39. IN OUT LPDWORD lpcbTotalBytes,
  40. IN OUT LPDWORD lpNumObjectTypes
  41. )
  42. /*++
  43. Routine Description:
  44. This routine will return the data for the logical disk object
  45. Arguments:
  46. IN OUT LPVOID *lppData
  47. IN: pointer to the address of the buffer to receive the completed
  48. PerfDataBlock and subordinate structures. This routine will
  49. append its data to the buffer starting at the point referenced
  50. by *lppData.
  51. OUT: points to the first byte after the data structure added by this
  52. routine. This routine updated the value at lppdata after appending
  53. its data.
  54. IN OUT LPDWORD lpcbTotalBytes
  55. IN: the address of the DWORD that tells the size in bytes of the
  56. buffer referenced by the lppData argument
  57. OUT: the number of bytes added by this routine is writted to the
  58. DWORD pointed to by this argument
  59. IN OUT LPDWORD NumObjectTypes
  60. IN: the address of the DWORD to receive the number of objects added
  61. by this routine
  62. OUT: the number of objects added by this routine is writted to the
  63. DWORD pointed to by this argument
  64. Returns:
  65. 0 if successful, else Win 32 error code of failure
  66. --*/
  67. {
  68. PPDISK_DATA_DEFINITION pPhysicalDiskDataDefinition;
  69. DWORD TotalLen; // Length of the total return block
  70. PDISK_COUNTER_DATA pcdTotal;
  71. DWORD dwStatus = ERROR_SUCCESS;
  72. PPERF_INSTANCE_DEFINITION pPerfInstanceDefinition = NULL;
  73. PWNODE_ALL_DATA WmiDiskInfo;
  74. DISK_PERFORMANCE *pDiskPerformance; // Disk driver returns counters here
  75. PWCHAR wszWmiInstanceName;
  76. WCHAR wszInstanceName[MAX_PATH]; // the numbers shouldn't ever get this big
  77. DWORD dwInstanceNameOffset;
  78. DWORD dwNumPhysicalDisks = 0;
  79. PPDISK_COUNTER_DATA pPCD;
  80. BOOL bMoreEntries;
  81. LONGLONG llTemp;
  82. DWORD dwTemp;
  83. DWORD dwReturn = ERROR_SUCCESS;
  84. WORD wNameLength;
  85. BOOL bSkip;
  86. DWORD dwCurrentWmiObjCount = 0;
  87. DWORD dwRemapCount = 10;
  88. DOUBLE dReadTime, dWriteTime, dTransferTime;
  89. //
  90. // Check for sufficient space for Physical Disk object
  91. // type definition
  92. //
  93. do {
  94. dwNumPhysicalDisks = 0;
  95. // make sure the drive letter map is up-to-date
  96. if (bRemapDriveLetters) {
  97. dwStatus = MapDriveLetters();
  98. // MapDriveLetters clears the remap flag when successful
  99. if (dwStatus != ERROR_SUCCESS) {
  100. *lpcbTotalBytes = (DWORD) 0;
  101. *lpNumObjectTypes = (DWORD) 0;
  102. return dwStatus;
  103. }
  104. }
  105. pPhysicalDiskDataDefinition = (PDISK_DATA_DEFINITION *) *lppData;
  106. // clear the accumulator structure
  107. memset (&pcdTotal, 0, sizeof(pcdTotal));
  108. //
  109. // Define Logical Disk data block
  110. //
  111. TotalLen = sizeof (PDISK_DATA_DEFINITION);
  112. if ( *lpcbTotalBytes < TotalLen ) {
  113. *lpcbTotalBytes = (DWORD) 0;
  114. *lpNumObjectTypes = (DWORD) 0;
  115. return ERROR_MORE_DATA;
  116. }
  117. memmove(pPhysicalDiskDataDefinition,
  118. &PhysicalDiskDataDefinition,
  119. sizeof(PDISK_DATA_DEFINITION));
  120. // read the data from the diskperf driver
  121. if (dwStatus == ERROR_SUCCESS) {
  122. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  123. &pPhysicalDiskDataDefinition[1];
  124. WmiDiskInfo = (PWNODE_ALL_DATA)WmiBuffer;
  125. // make sure the structure is valid
  126. if (WmiDiskInfo->WnodeHeader.BufferSize < sizeof(WNODE_ALL_DATA)) {
  127. bMoreEntries = FALSE;
  128. // just to make sure someone notices on a checked build
  129. assert (WmiDiskInfo->WnodeHeader.BufferSize >= sizeof(WNODE_ALL_DATA));
  130. } else {
  131. // make sure there are some entries to return
  132. bMoreEntries =
  133. (WmiDiskInfo->InstanceCount > 0) ? TRUE : FALSE;
  134. }
  135. while (bMoreEntries) {
  136. pDiskPerformance = (PDISK_PERFORMANCE)(
  137. (PUCHAR)WmiDiskInfo + WmiDiskInfo->DataBlockOffset);
  138. dwInstanceNameOffset = *((LPDWORD)(
  139. (LPBYTE)WmiDiskInfo + WmiDiskInfo->OffsetInstanceNameOffsets));
  140. wNameLength = *(WORD *)((LPBYTE)WmiDiskInfo + dwInstanceNameOffset);
  141. if (wNameLength > 0) {
  142. wszWmiInstanceName = (LPWSTR)((LPBYTE)WmiDiskInfo + dwInstanceNameOffset + sizeof(WORD));
  143. if (IsPhysicalDrive(pDiskPerformance)) {
  144. #if _DBG_PRINT_INSTANCES
  145. WCHAR szOutputBuffer[512];
  146. #endif
  147. // then the format is correct AND this is a physical
  148. // partition so set the name string pointer and
  149. // length for use in creating the instance.
  150. memset (wszInstanceName, 0, sizeof(wszInstanceName));
  151. GetPhysicalDriveNameString (
  152. pDiskPerformance->StorageDeviceNumber,
  153. pPhysDiskList,
  154. dwNumPhysDiskListEntries,
  155. wszInstanceName);
  156. #if _DBG_PRINT_INSTANCES
  157. swprintf (szOutputBuffer, (LPCWSTR)L"\nPERFDISK: [%d] PhysDrive [%8.8s,%d] is mapped as: ",
  158. dwNumPhysDiskListEntries,
  159. pDiskPerformance->StorageManagerName,
  160. pDiskPerformance->StorageDeviceNumber);
  161. OutputDebugStringW (szOutputBuffer);
  162. OutputDebugStringW (wszInstanceName);
  163. #endif
  164. bSkip = FALSE;
  165. } else {
  166. bSkip = TRUE;
  167. }
  168. if (!bSkip) {
  169. // first see if there's room for this entry....
  170. TotalLen =
  171. // space already used
  172. (DWORD)((PCHAR) pPerfInstanceDefinition -
  173. (PCHAR) pPhysicalDiskDataDefinition)
  174. // + estimate of this instance
  175. + sizeof(PERF_INSTANCE_DEFINITION)
  176. + (lstrlenW(wszInstanceName) + 1) * sizeof(WCHAR) ;
  177. TotalLen = QWORD_MULTIPLE (TotalLen);
  178. TotalLen += sizeof(PDISK_COUNTER_DATA);
  179. TotalLen = QWORD_MULTIPLE (TotalLen);
  180. if ( *lpcbTotalBytes < TotalLen ) {
  181. *lpcbTotalBytes = (DWORD) 0;
  182. *lpNumObjectTypes = (DWORD) 0;
  183. dwReturn = ERROR_MORE_DATA;
  184. break;
  185. }
  186. MonBuildInstanceDefinition(
  187. pPerfInstanceDefinition,
  188. (PVOID *) &pPCD,
  189. 0, 0, // no parent
  190. (DWORD)-1,// no unique ID
  191. wszInstanceName);
  192. // clear counter data block
  193. memset (pPCD, 0, sizeof(PDISK_COUNTER_DATA));
  194. pPCD->CounterBlock.ByteLength = sizeof(PDISK_COUNTER_DATA);
  195. // KdPrint (("PERFDISK: (P) Entry %8.8x for: %ws\n", (DWORD)pPCD, wszWmiInstanceName));
  196. // insure quadword alignment of the data structure
  197. assert (((DWORD)(pPCD) & 0x00000007) == 0);
  198. // Set up pointer for data collection
  199. // the QueueDepth counter is only a byte so clear the unused bytes
  200. pDiskPerformance->QueueDepth &= 0x000000FF;
  201. //
  202. // Format and collect Physical data
  203. //
  204. pcdTotal.DiskCurrentQueueLength += pDiskPerformance->QueueDepth;
  205. pPCD->DiskCurrentQueueLength = pDiskPerformance->QueueDepth;
  206. llTemp = pDiskPerformance->ReadTime.QuadPart +
  207. pDiskPerformance->WriteTime.QuadPart;
  208. // these values are read in 100 NS units but are expected
  209. // to be in sys perf freq (tick) units for the Sec/op ctrs
  210. // so convert them here
  211. dReadTime = (DOUBLE)(pDiskPerformance->ReadTime.QuadPart);
  212. dWriteTime = (DOUBLE)(pDiskPerformance->WriteTime.QuadPart);
  213. dTransferTime = (DOUBLE)(llTemp);
  214. dReadTime *= dSysTickTo100Ns;
  215. dWriteTime *= dSysTickTo100Ns;
  216. dTransferTime *= dSysTickTo100Ns;
  217. pPCD->DiskTime = llTemp;
  218. pPCD->DiskAvgQueueLength = llTemp;
  219. pcdTotal.DiskAvgQueueLength += llTemp;
  220. pcdTotal.DiskTime += llTemp;
  221. pPCD->DiskReadTime = pDiskPerformance->ReadTime.QuadPart;
  222. pPCD->DiskReadQueueLength = pDiskPerformance->ReadTime.QuadPart;
  223. pcdTotal.DiskReadTime += pDiskPerformance->ReadTime.QuadPart;
  224. pcdTotal.DiskReadQueueLength += pDiskPerformance->ReadTime.QuadPart;
  225. pPCD->DiskWriteTime = pDiskPerformance->WriteTime.QuadPart;
  226. pPCD->DiskWriteQueueLength = pDiskPerformance->WriteTime.QuadPart;
  227. pcdTotal.DiskWriteTime += pDiskPerformance->WriteTime.QuadPart;
  228. pcdTotal.DiskWriteQueueLength += pDiskPerformance->WriteTime.QuadPart;
  229. pPCD->DiskAvgTime = (LONGLONG)dTransferTime;
  230. pcdTotal.DiskAvgTime += (LONGLONG)dTransferTime;
  231. dwTemp = pDiskPerformance->ReadCount +
  232. pDiskPerformance->WriteCount;
  233. pcdTotal.DiskTransfersBase1 += dwTemp;
  234. pPCD->DiskTransfersBase1 = dwTemp;
  235. pcdTotal.DiskAvgReadTime += (LONGLONG)dReadTime;
  236. pPCD->DiskAvgReadTime = (LONGLONG)dReadTime;
  237. pcdTotal.DiskReadsBase1 += pDiskPerformance->ReadCount;
  238. pPCD->DiskReadsBase1 = pDiskPerformance->ReadCount;
  239. pcdTotal.DiskAvgWriteTime += (LONGLONG)dWriteTime;
  240. pPCD->DiskAvgWriteTime = (LONGLONG)dWriteTime;
  241. pcdTotal.DiskWritesBase1 += pDiskPerformance->WriteCount;
  242. pPCD->DiskWritesBase1 = pDiskPerformance->WriteCount;
  243. pcdTotal.DiskTransfers += dwTemp;
  244. pPCD->DiskTransfers = dwTemp;
  245. pcdTotal.DiskReads += pDiskPerformance->ReadCount;
  246. pPCD->DiskReads = pDiskPerformance->ReadCount;
  247. pcdTotal.DiskWrites += pDiskPerformance->WriteCount;
  248. pPCD->DiskWrites = pDiskPerformance->WriteCount;
  249. llTemp = pDiskPerformance->BytesRead.QuadPart +
  250. pDiskPerformance->BytesWritten.QuadPart;
  251. pcdTotal.DiskBytes += llTemp;
  252. pPCD->DiskBytes = llTemp;
  253. pcdTotal.DiskReadBytes += pDiskPerformance->BytesRead.QuadPart;
  254. pPCD->DiskReadBytes = pDiskPerformance->BytesRead.QuadPart;
  255. pcdTotal.DiskWriteBytes += pDiskPerformance->BytesWritten.QuadPart;
  256. pPCD->DiskWriteBytes = pDiskPerformance->BytesWritten.QuadPart;
  257. pcdTotal.DiskAvgBytes += llTemp;
  258. pPCD->DiskAvgBytes = llTemp;
  259. pcdTotal.DiskTransfersBase2 += dwTemp;
  260. pPCD->DiskTransfersBase2 = dwTemp;
  261. pcdTotal.DiskAvgReadBytes += pDiskPerformance->BytesRead.QuadPart;
  262. pPCD->DiskAvgReadBytes = pDiskPerformance->BytesRead.QuadPart;
  263. pcdTotal.DiskReadsBase2 += pDiskPerformance->ReadCount;
  264. pPCD->DiskReadsBase2 = pDiskPerformance->ReadCount;
  265. pcdTotal.DiskAvgWriteBytes += pDiskPerformance->BytesWritten.QuadPart;
  266. pPCD->DiskAvgWriteBytes = pDiskPerformance->BytesWritten.QuadPart;
  267. pcdTotal.DiskWritesBase2 += pDiskPerformance->WriteCount;
  268. pPCD->DiskWritesBase2 = pDiskPerformance->WriteCount;
  269. pPCD->IdleTime = pDiskPerformance->IdleTime.QuadPart;
  270. pcdTotal.IdleTime += pDiskPerformance->IdleTime.QuadPart;
  271. pPCD->SplitCount = pDiskPerformance->SplitCount;
  272. pcdTotal.SplitCount += pDiskPerformance->SplitCount;
  273. pPCD->DiskTimeTimeStamp = pDiskPerformance->QueryTime.QuadPart;
  274. pcdTotal.DiskTimeTimeStamp += pDiskPerformance->QueryTime.QuadPart;
  275. // move to the end of the buffer for the next instance
  276. pPerfInstanceDefinition = (PPERF_INSTANCE_DEFINITION)&pPCD[1];
  277. dwNumPhysicalDisks++;
  278. } else {
  279. // KdPrint (("PERFDISK: (P) Skipping Instance: %ws\n", wszWmiInstanceName));
  280. }
  281. // count the number of items returned by WMI
  282. dwCurrentWmiObjCount++;
  283. } else {
  284. // the name has 0 length so skip
  285. }
  286. // bump pointers inside WMI data block
  287. if (WmiDiskInfo->WnodeHeader.Linkage != 0) {
  288. // continue
  289. WmiDiskInfo = (PWNODE_ALL_DATA) (
  290. (LPBYTE)WmiDiskInfo + WmiDiskInfo->WnodeHeader.Linkage);
  291. } else {
  292. // this is the end of the line
  293. bMoreEntries = FALSE;
  294. }
  295. } // end for each volume
  296. // see if number of WMI objects returned is different from
  297. // the last time the instance table was built, if so then
  298. // remap the letters and redo the instances
  299. if (dwCurrentWmiObjCount != dwWmiDriveCount) {
  300. DebugPrint((1, "CollectPDisk: Remap Current %d Drive %d\n",
  301. dwCurrentWmiObjCount, dwWmiDriveCount));
  302. bRemapDriveLetters = TRUE;
  303. dwRemapCount--;
  304. }
  305. } // end if mem init was successful
  306. } while (bRemapDriveLetters && dwRemapCount);
  307. if ((dwNumPhysicalDisks > 0) && (dwStatus == ERROR_SUCCESS)) {
  308. // see if there's room for this entry....
  309. TotalLen =
  310. // space already used
  311. (DWORD)((PCHAR) pPerfInstanceDefinition -
  312. (PCHAR) pPhysicalDiskDataDefinition)
  313. // + estimate of this instance
  314. + sizeof(PERF_INSTANCE_DEFINITION)
  315. + (lstrlenW(wszTotal) + 1) * sizeof(WCHAR) ;
  316. TotalLen = QWORD_MULTIPLE (TotalLen);
  317. TotalLen += sizeof(PDISK_COUNTER_DATA);
  318. TotalLen = QWORD_MULTIPLE (TotalLen);
  319. if ( *lpcbTotalBytes < TotalLen ) {
  320. *lpcbTotalBytes = (DWORD) 0;
  321. *lpNumObjectTypes = (DWORD) 0;
  322. dwReturn = ERROR_MORE_DATA;
  323. } else {
  324. // normalize the total times
  325. pcdTotal.DiskTime /= dwNumPhysicalDisks;
  326. pcdTotal.DiskReadTime /= dwNumPhysicalDisks;
  327. pcdTotal.DiskWriteTime /= dwNumPhysicalDisks;
  328. pcdTotal.IdleTime /= dwNumPhysicalDisks;
  329. pcdTotal.DiskTimeTimeStamp /= dwNumPhysicalDisks;
  330. MonBuildInstanceDefinition(
  331. pPerfInstanceDefinition,
  332. (PVOID *) &pPCD,
  333. 0,
  334. 0,
  335. (DWORD)-1,
  336. wszTotal);
  337. // update the total counters
  338. // insure quadword alignment of the data structure
  339. assert (((DWORD)(pPCD) & 0x00000007) == 0);
  340. memcpy (pPCD, &pcdTotal, sizeof (pcdTotal));
  341. pPCD->CounterBlock.ByteLength = sizeof(PDISK_COUNTER_DATA);
  342. // and update the "next byte" pointer
  343. pPerfInstanceDefinition = (PPERF_INSTANCE_DEFINITION)&pPCD[1];
  344. // update pointer to next available buffer...
  345. pPhysicalDiskDataDefinition->DiskObjectType.NumInstances =
  346. dwNumPhysicalDisks + 1; // add 1 for "Total" disk
  347. }
  348. } else {
  349. // If we are diskless, then return no instances
  350. pPerfInstanceDefinition = (PERF_INSTANCE_DEFINITION *)
  351. &pPhysicalDiskDataDefinition[1];
  352. pPhysicalDiskDataDefinition->DiskObjectType.NumInstances = 0;
  353. }
  354. if (dwReturn == ERROR_SUCCESS) {
  355. *lpcbTotalBytes =
  356. pPhysicalDiskDataDefinition->DiskObjectType.TotalByteLength =
  357. (DWORD)((PCHAR) pPerfInstanceDefinition -
  358. (PCHAR) pPhysicalDiskDataDefinition);
  359. #if DBG
  360. // sanity check on buffer size estimates
  361. if (*lpcbTotalBytes > TotalLen ) {
  362. DbgPrint ("\nPERFDISK: Physical Disk Perf Ctr. Instance Size Underestimated:");
  363. DbgPrint ("\nPERFDISK: Estimated size: %d, Actual Size: %d", TotalLen, *lpcbTotalBytes);
  364. }
  365. #endif
  366. *lppData = (LPVOID) pPerfInstanceDefinition;
  367. *lpNumObjectTypes = 1;
  368. }
  369. return dwReturn;
  370. }