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.

1153 lines
39 KiB

  1. /*++
  2. Copyright (C) 1996-1999 Microsoft Corporation
  3. Module Name:
  4. perfutil.c
  5. Abstract:
  6. Performance registry interface functions
  7. --*/
  8. #include <windows.h>
  9. #include <pdh.h>
  10. #include <stdlib.h>
  11. #include <stdio.h>
  12. #include "pdhitype.h"
  13. #include "pdhidef.h"
  14. #include "perfdata.h"
  15. #include "perftype.h"
  16. #include "pdhmsg.h"
  17. #include "strings.h"
  18. DWORD
  19. PdhiMakePerfLangId(
  20. IN LANGID lID,
  21. OUT LPWSTR szBuffer
  22. );
  23. PPERF_MACHINE
  24. PdhiAddNewMachine (
  25. IN PPERF_MACHINE pLastMachine,
  26. IN LPWSTR szMachineName
  27. );
  28. PPERF_MACHINE pFirstMachine = NULL;
  29. PDH_STATUS
  30. ConnectMachine (
  31. PPERF_MACHINE pThisMachine
  32. )
  33. {
  34. PDH_STATUS pdhStatus;
  35. LONG lStatus = ERROR_SUCCESS;
  36. LONGLONG llCurrentTime;
  37. WCHAR szOsVer[8];
  38. HKEY hKeyRemMachine;
  39. HKEY hKeyRemCurrentVersion;
  40. DWORD dwBufSize;
  41. DWORD dwType;
  42. BOOL bUpdateRetryTime = FALSE;
  43. DWORD dwReconnecting;
  44. if (pThisMachine == NULL) {
  45. pdhStatus = PDH_INVALID_ARGUMENT;
  46. } else {
  47. pdhStatus = WAIT_FOR_AND_LOCK_MUTEX(pThisMachine->hMutex);
  48. }
  49. if (pdhStatus == ERROR_SUCCESS) {
  50. // connect to system's performance registry
  51. if (lstrcmpiW(pThisMachine->szName, szStaticLocalMachineName) == 0) {
  52. // only one thread at a time can try to connect to a machine.
  53. pThisMachine->dwRefCount++;
  54. // assign default OS version
  55. // assume NT4 unless found otherwise
  56. lstrcpyW (pThisMachine->szOsVer, (LPCWSTR)L"4.0");
  57. // this is the local machine so use the local reg key
  58. pThisMachine->hKeyPerformanceData = HKEY_PERFORMANCE_DATA;
  59. // look up the OS version and save it
  60. lStatus = RegOpenKeyExW (
  61. HKEY_LOCAL_MACHINE,
  62. cszCurrentVersionKey,
  63. 0L,
  64. KEY_READ,
  65. &hKeyRemCurrentVersion);
  66. if (lStatus == ERROR_SUCCESS) {
  67. dwType=0;
  68. dwBufSize = sizeof (szOsVer);
  69. lStatus = RegQueryValueExW (
  70. hKeyRemCurrentVersion,
  71. cszCurrentVersionValueName,
  72. 0L,
  73. &dwType,
  74. (LPBYTE)&szOsVer[0],
  75. &dwBufSize);
  76. if ((lStatus == ERROR_SUCCESS) && (dwType == REG_SZ)) {
  77. lstrcpyW(pThisMachine->szOsVer, szOsVer);
  78. }
  79. RegCloseKey (hKeyRemCurrentVersion);
  80. }
  81. } else {
  82. // now try to connect if the retry timeout has elapzed
  83. GetLocalFileTime (&llCurrentTime);
  84. dwReconnecting = (DWORD)InterlockedCompareExchange (
  85. (PLONG)&pThisMachine->dwRetryFlags, TRUE, FALSE);
  86. if (!dwReconnecting) {
  87. if ((pThisMachine->llRetryTime == 0) || (pThisMachine->llRetryTime < llCurrentTime)) {
  88. // only one thread at a time can try to connect to a machine.
  89. pThisMachine->dwRefCount++;
  90. bUpdateRetryTime = TRUE; // only update after an attempt has been made
  91. __try {
  92. // close any open keys
  93. if (pThisMachine->hKeyPerformanceData != NULL) {
  94. RegCloseKey (pThisMachine->hKeyPerformanceData);
  95. pThisMachine->hKeyPerformanceData = NULL;
  96. }
  97. } __except (EXCEPTION_EXECUTE_HANDLER) {
  98. lStatus = GetExceptionCode();
  99. }
  100. if (lStatus != ERROR_SUCCESS) {
  101. pThisMachine->hKeyPerformanceData = NULL;
  102. } else {
  103. // get OS version of remote machine
  104. lStatus = RegConnectRegistryW (
  105. pThisMachine->szName,
  106. HKEY_LOCAL_MACHINE,
  107. &hKeyRemMachine);
  108. if (lStatus == ERROR_SUCCESS) {
  109. // look up the OS version and save it
  110. lStatus = RegOpenKeyExW (
  111. hKeyRemMachine,
  112. cszCurrentVersionKey,
  113. 0L,
  114. KEY_READ,
  115. &hKeyRemCurrentVersion);
  116. if (lStatus == ERROR_SUCCESS) {
  117. dwType=0;
  118. dwBufSize = sizeof (szOsVer);
  119. lStatus = RegQueryValueExW (
  120. hKeyRemCurrentVersion,
  121. cszCurrentVersionValueName,
  122. 0L,
  123. &dwType,
  124. (LPBYTE)&szOsVer[0],
  125. &dwBufSize);
  126. if ((lStatus == ERROR_SUCCESS) && (dwType == REG_SZ)) {
  127. lstrcpyW(pThisMachine->szOsVer, szOsVer);
  128. }
  129. RegCloseKey (hKeyRemCurrentVersion);
  130. }
  131. RegCloseKey (hKeyRemMachine);
  132. }
  133. }
  134. if (lStatus == ERROR_SUCCESS) {
  135. __try {
  136. // Connect to remote registry
  137. lStatus = RegConnectRegistryW (
  138. pThisMachine->szName,
  139. HKEY_PERFORMANCE_DATA,
  140. &pThisMachine->hKeyPerformanceData);
  141. } __except (EXCEPTION_EXECUTE_HANDLER) {
  142. lStatus = GetExceptionCode();
  143. }
  144. } // else pass error through
  145. } else {
  146. // not time to reconnect yet so save the old status and
  147. // clear the registry key
  148. pThisMachine->hKeyPerformanceData = NULL;
  149. lStatus = pThisMachine->dwStatus;
  150. }
  151. // clear the reconnecting flag
  152. InterlockedExchange ((LONG *)&pThisMachine->dwRetryFlags, FALSE);
  153. } else {
  154. // some other thread is trying to connect
  155. return (PDH_CANNOT_CONNECT_MACHINE);
  156. }
  157. }
  158. if ((pThisMachine->hKeyPerformanceData != NULL) && (pThisMachine->dwRetryFlags == 0)) {
  159. // successfully connected to computer's registry, so
  160. // get the performance names from that computer and cache them
  161. /*
  162. the shortcut of mapping local strings cannot be used reliably until
  163. more synchronization of the mapped file is implemented. Just Mapping
  164. to the file and not locking it or checking for updates leaves it
  165. vulnerable to the mapped file being changed by an external program
  166. and invalidating the pointer table built by the BuildLocalNameTable
  167. function.
  168. Until this synchronization and locking is implemented, the
  169. BuildLocalNameTable function should not be used.
  170. */
  171. if (pThisMachine->hKeyPerformanceData != HKEY_PERFORMANCE_DATA) {
  172. if (pThisMachine->szPerfStrings != NULL) {
  173. // reload the perf strings, incase new ones have been
  174. // installed
  175. if ( pThisMachine->sz009PerfStrings != NULL
  176. && pThisMachine->sz009PerfStrings != pThisMachine->szPerfStrings) {
  177. G_FREE(pThisMachine->sz009PerfStrings);
  178. }
  179. if (pThisMachine->typePerfStrings) {
  180. G_FREE(pThisMachine->typePerfStrings);
  181. }
  182. G_FREE (pThisMachine->szPerfStrings);
  183. pThisMachine->sz009PerfStrings = NULL;
  184. pThisMachine->typePerfStrings = NULL;
  185. pThisMachine->szPerfStrings = NULL;
  186. }
  187. BuildNameTable(pThisMachine->szName,
  188. GetUserDefaultUILanguage(),
  189. pThisMachine);
  190. if (pThisMachine->szPerfStrings == NULL) {
  191. BuildNameTable(pThisMachine->szName,
  192. MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  193. pThisMachine);
  194. }
  195. } else {
  196. if (pThisMachine->szPerfStrings != NULL) {
  197. // reload the perf strings, incase new ones have been
  198. // installed
  199. if ( pThisMachine->sz009PerfStrings != NULL
  200. && pThisMachine->sz009PerfStrings != pThisMachine->szPerfStrings) {
  201. G_FREE(pThisMachine->sz009PerfStrings);
  202. }
  203. if (pThisMachine->typePerfStrings) {
  204. G_FREE(pThisMachine->typePerfStrings);
  205. }
  206. G_FREE (pThisMachine->szPerfStrings);
  207. pThisMachine->sz009PerfStrings = NULL;
  208. pThisMachine->typePerfStrings = NULL;
  209. pThisMachine->szPerfStrings = NULL;
  210. }
  211. BuildNameTable(NULL,
  212. GetUserDefaultUILanguage(),
  213. pThisMachine);
  214. if (pThisMachine->szPerfStrings == NULL) {
  215. BuildNameTable(NULL,
  216. MAKELANGID(LANG_ENGLISH, SUBLANG_ENGLISH_US),
  217. pThisMachine);
  218. }
  219. pThisMachine->pLocalNameInfo = NULL;
  220. }
  221. if (pThisMachine->szPerfStrings != NULL) {
  222. pdhStatus = ERROR_SUCCESS;
  223. pThisMachine->dwStatus = ERROR_SUCCESS;
  224. } else {
  225. // unable to read system counter name strings
  226. pdhStatus = PDH_CANNOT_READ_NAME_STRINGS;
  227. memset (&pThisMachine->LastStringUpdateTime, 0, sizeof(pThisMachine->LastStringUpdateTime));
  228. pThisMachine->dwLastPerfString = 0;
  229. pThisMachine->dwStatus = PDH_CSTATUS_NO_MACHINE;
  230. }
  231. } else {
  232. // unable to connect to the specified machine
  233. pdhStatus = PDH_CANNOT_CONNECT_MACHINE;
  234. // Set error to either
  235. // "PDH_CSTATUS_NO_MACHINE" if no connection could be made
  236. // or
  237. // PDH_ACCESS_DENIED if ERROR_ACCESS_DENIED status is returned
  238. // since if ERROR_ACCESS_DENIED is returned, then reconnection will
  239. // probably be futile.
  240. if ((lStatus == ERROR_ACCESS_DENIED) || (lStatus == PDH_ACCESS_DENIED)) {
  241. pThisMachine->dwStatus = PDH_ACCESS_DENIED;
  242. } else {
  243. pThisMachine->dwStatus = PDH_CSTATUS_NO_MACHINE;
  244. }
  245. }
  246. if (pdhStatus != ERROR_SUCCESS) {
  247. if (bUpdateRetryTime) {
  248. // this attempt didn't work so reset retry counter to
  249. // wait some more for the machine to come back up.
  250. GetLocalFileTime (&llCurrentTime);
  251. pThisMachine->llRetryTime = llCurrentTime + llRemoteRetryTime;
  252. }
  253. } else {
  254. // clear the retry counter to allow function calls
  255. pThisMachine->llRetryTime = 0;
  256. }
  257. pThisMachine->dwRefCount--;
  258. RELEASE_MUTEX(pThisMachine->hMutex);
  259. }
  260. return pdhStatus;
  261. }
  262. PPERF_MACHINE
  263. PdhiAddNewMachine (
  264. IN PPERF_MACHINE pLastMachine,
  265. IN LPWSTR szMachineName
  266. )
  267. {
  268. PPERF_MACHINE pNewMachine = NULL;
  269. LPWSTR szNameBuffer = NULL;
  270. PERF_DATA_BLOCK *pdbBuffer = NULL;
  271. LPWSTR szIdList = NULL;
  272. DWORD dwNameSize = 0;
  273. BOOL bUseLocalName = TRUE;
  274. // reset the last error value
  275. SetLastError (ERROR_SUCCESS);
  276. if (szMachineName != NULL) {
  277. if (*szMachineName != 0) {
  278. bUseLocalName = FALSE;
  279. }
  280. }
  281. if (bUseLocalName) {
  282. dwNameSize = lstrlenW (szStaticLocalMachineName);
  283. } else {
  284. dwNameSize = lstrlenW (szMachineName);
  285. }
  286. dwNameSize += 1;
  287. dwNameSize *= sizeof (WCHAR);
  288. pNewMachine = (PPERF_MACHINE) G_ALLOC(sizeof(PERF_MACHINE)
  289. + SMALL_BUFFER_SIZE + (sizeof(WCHAR) * (dwNameSize + 1)));
  290. if (pNewMachine != NULL) {
  291. szIdList = (LPWSTR) ((LPBYTE) pNewMachine + sizeof(PERF_MACHINE));
  292. szNameBuffer = (LPWSTR) ((LPBYTE) szIdList + SMALL_BUFFER_SIZE);
  293. pdbBuffer = G_ALLOC (LARGE_BUFFER_SIZE);
  294. if (pdbBuffer != NULL) {
  295. // initialize the new buffer
  296. pNewMachine->hKeyPerformanceData = NULL;
  297. pNewMachine->pLocalNameInfo = NULL;
  298. pNewMachine->szName = szNameBuffer;
  299. if (bUseLocalName) {
  300. lstrcpyW (pNewMachine->szName, szStaticLocalMachineName);
  301. } else {
  302. lstrcpyW (pNewMachine->szName, szMachineName);
  303. }
  304. pNewMachine->pSystemPerfData = pdbBuffer;
  305. pNewMachine->szPerfStrings = NULL;
  306. pNewMachine->sz009PerfStrings = NULL;
  307. pNewMachine->typePerfStrings = NULL;
  308. pNewMachine->dwLastPerfString = 0;
  309. pNewMachine->dwRefCount = 0;
  310. pNewMachine->szQueryObjects = szIdList;
  311. pNewMachine->dwStatus = PDH_CSTATUS_NO_MACHINE; // not connected yet
  312. pNewMachine->llRetryTime = 1; // retry connection immediately
  313. pNewMachine->dwRetryFlags = 0; // Not attempting a connection.
  314. pNewMachine->dwMachineFlags = 0;
  315. pNewMachine->hMutex = CreateMutex (NULL, FALSE, NULL);
  316. // everything went OK so far so add this entry to the list
  317. if (pLastMachine != NULL) {
  318. pNewMachine->pNext = pLastMachine->pNext;
  319. pLastMachine->pNext = pNewMachine;
  320. pNewMachine->pPrev = pLastMachine;
  321. pNewMachine->pNext->pPrev = pNewMachine;
  322. } else {
  323. // this is the first item in the list so it
  324. // points to itself
  325. pNewMachine->pNext = pNewMachine;
  326. pNewMachine->pPrev = pNewMachine;
  327. }
  328. return pNewMachine;
  329. } else {
  330. // unable to allocate perf data buffer
  331. SetLastError (PDH_MEMORY_ALLOCATION_FAILURE);
  332. // clean up and bail out.
  333. if (pNewMachine != NULL) {
  334. G_FREE (pNewMachine);
  335. }
  336. return NULL;
  337. }
  338. } else {
  339. // unable to allocate machine data memory
  340. SetLastError (PDH_MEMORY_ALLOCATION_FAILURE);
  341. // clean up and bail out.
  342. if (pNewMachine != NULL) {
  343. G_FREE (pNewMachine);
  344. }
  345. return NULL;
  346. }
  347. }
  348. PPERF_MACHINE
  349. GetMachineW (
  350. IN LPWSTR szMachineName,
  351. IN DWORD dwFlags
  352. )
  353. {
  354. PPERF_MACHINE pThisMachine = NULL;
  355. PPERF_MACHINE pLastMachine;
  356. BOOL bFound = FALSE;
  357. LPWSTR szFnMachineName;
  358. BOOL bNew = FALSE; // true if this is a new machine to the list
  359. BOOL bUseLocalName = TRUE;
  360. DWORD dwLocalStatus;
  361. // reset the last error value
  362. SetLastError (ERROR_SUCCESS);
  363. if (WAIT_FOR_AND_LOCK_MUTEX (hPdhDataMutex) == ERROR_SUCCESS) {
  364. if (szMachineName != NULL) {
  365. if (*szMachineName != 0) {
  366. bUseLocalName = FALSE;
  367. }
  368. }
  369. if (bUseLocalName) {
  370. szFnMachineName = szStaticLocalMachineName;
  371. } else {
  372. szFnMachineName = szMachineName;
  373. }
  374. // walk down list to find this machine
  375. pThisMachine = pFirstMachine;
  376. pLastMachine = NULL;
  377. // walk around entire list
  378. if (pThisMachine != NULL) {
  379. do {
  380. // walk down the list and look for a match
  381. if (lstrcmpiW(szFnMachineName, pThisMachine->szName) != 0) {
  382. pLastMachine = pThisMachine;
  383. pThisMachine = pThisMachine->pNext;
  384. } else {
  385. if (dwFlags & PDH_GM_UPDATE_NAME) {
  386. if (szMachineName != NULL) {
  387. // match found so update name string if a real string was passed in
  388. lstrcpyW (szMachineName, pThisMachine->szName);
  389. }
  390. }
  391. // and break now
  392. bFound = TRUE;
  393. break;
  394. }
  395. } while (pThisMachine != pFirstMachine);
  396. }
  397. // if thismachine == the first machine, then we couldn't find a match in
  398. // the list, if this machine is NULL, then there is no list
  399. if (!bFound) {
  400. // then this machine was not found so add it.
  401. pThisMachine = PdhiAddNewMachine (
  402. pLastMachine,
  403. szFnMachineName);
  404. if (pThisMachine != NULL) {
  405. bNew = TRUE;
  406. if (pFirstMachine == NULL) {
  407. // then update the first pointer
  408. pFirstMachine = pThisMachine;
  409. }
  410. }
  411. }
  412. if (pThisMachine->dwThreadId != GetCurrentThreadId()) {
  413. dwFlags |= PDH_GM_UPDATE_PERFDATA;
  414. pThisMachine->dwThreadId = GetCurrentThreadId();
  415. }
  416. if ((pThisMachine != NULL) &&
  417. (((!bFound) || (dwFlags & PDH_GM_UPDATE_PERFDATA)) ||
  418. (pThisMachine->dwStatus != ERROR_SUCCESS))) {
  419. // then this is a new machine
  420. // or
  421. // the caller wants the data refreshed
  422. // or
  423. // the machine has an entry, but is not yet on line
  424. // first try to connect to the machine
  425. // the call to ConnectMachine updates the machine status
  426. // so there's no need to keep it here.
  427. if (ConnectMachine (pThisMachine) == ERROR_SUCCESS) {
  428. // connected to the machine so
  429. // then lock access to it
  430. // the caller of this function must release the mutex
  431. dwLocalStatus = WAIT_FOR_AND_LOCK_MUTEX (pThisMachine->hMutex);
  432. if (dwLocalStatus == ERROR_SUCCESS) {
  433. // get the current system counter info
  434. pThisMachine->dwStatus = GetSystemPerfData (
  435. pThisMachine->hKeyPerformanceData,
  436. &pThisMachine->pSystemPerfData,
  437. (LPWSTR)cszGlobal,
  438. (BOOL)(dwFlags & PDH_GM_READ_COSTLY_DATA)
  439. );
  440. if ((dwFlags & PDH_GM_READ_COSTLY_DATA) &&
  441. (pThisMachine->dwStatus == ERROR_SUCCESS)) {
  442. pThisMachine->dwMachineFlags |= PDHIPM_FLAGS_HAVE_COSTLY;
  443. } else {
  444. pThisMachine->dwMachineFlags &= ~PDHIPM_FLAGS_HAVE_COSTLY;
  445. }
  446. SetLastError (pThisMachine->dwStatus);
  447. } else {
  448. pThisMachine = NULL;
  449. SetLastError (WAIT_TIMEOUT);
  450. }
  451. } else {
  452. SetLastError (pThisMachine->dwStatus);
  453. }
  454. }
  455. if (pThisMachine != NULL) {
  456. // machine found so bump the ref count
  457. // NOTE!!! the caller must release this!!!
  458. pThisMachine->dwRefCount++;
  459. }
  460. // at this point if pThisMachine is NULL then it was not found, nor
  461. // could it be added otherwise it is pointing to the matching machine
  462. // structure
  463. RELEASE_MUTEX (hPdhDataMutex);
  464. } else {
  465. SetLastError (WAIT_TIMEOUT);
  466. }
  467. return pThisMachine;
  468. }
  469. BOOL
  470. FreeMachine (
  471. PPERF_MACHINE pMachine,
  472. BOOL bForceRelease,
  473. BOOL bProcessExit
  474. )
  475. {
  476. PPERF_MACHINE pPrev;
  477. PPERF_MACHINE pNext;
  478. HANDLE hMutex;
  479. // unlink if this isn't the only one in the list
  480. if ((!bForceRelease) && (pMachine->dwRefCount)) return FALSE;
  481. hMutex = pMachine->hMutex;
  482. if (WAIT_FOR_AND_LOCK_MUTEX (hMutex) != ERROR_SUCCESS) {
  483. SetLastError(WAIT_TIMEOUT);
  484. return FALSE;
  485. }
  486. pPrev = pMachine->pPrev;
  487. pNext = pMachine->pNext;
  488. if ((pPrev != pMachine) && (pNext != pMachine)) {
  489. // this is not the only entry in the list
  490. pPrev->pNext = pNext;
  491. pNext->pPrev = pPrev;
  492. if (pMachine == pFirstMachine) {
  493. // then we are deleting the first one in the list so
  494. // update the list head to point to the next one in line
  495. pFirstMachine = pNext;
  496. }
  497. } else {
  498. // this is the only entry so clear the head pointer
  499. pFirstMachine = NULL;
  500. }
  501. // now free all allocated memory
  502. if (pMachine->pSystemPerfData != NULL) {
  503. G_FREE (pMachine->pSystemPerfData);
  504. }
  505. if (pMachine->typePerfStrings != NULL) {
  506. G_FREE (pMachine->typePerfStrings);
  507. }
  508. if ( pMachine->sz009PerfStrings != NULL
  509. && pMachine->sz009PerfStrings != pMachine->szPerfStrings) {
  510. G_FREE (pMachine->sz009PerfStrings);
  511. }
  512. if (pMachine->szPerfStrings != NULL) {
  513. G_FREE (pMachine->szPerfStrings);
  514. }
  515. // close key
  516. if (pMachine->hKeyPerformanceData != NULL) {
  517. if ( (! bProcessExit)
  518. || pMachine->hKeyPerformanceData != HKEY_PERFORMANCE_DATA) {
  519. RegCloseKey (pMachine->hKeyPerformanceData);
  520. }
  521. pMachine->hKeyPerformanceData = NULL;
  522. }
  523. // free memory block
  524. G_FREE (pMachine);
  525. // release and close mutex
  526. RELEASE_MUTEX (hMutex);
  527. if (hMutex != NULL) {
  528. CloseHandle (hMutex);
  529. }
  530. return TRUE;
  531. }
  532. BOOL
  533. FreeAllMachines (
  534. BOOL bProcessExit
  535. )
  536. {
  537. PPERF_MACHINE pThisMachine;
  538. // free any machines in the machine list
  539. if (pFirstMachine != NULL) {
  540. if (WAIT_FOR_AND_LOCK_MUTEX (hPdhDataMutex) == ERROR_SUCCESS) {
  541. pThisMachine = pFirstMachine;
  542. while (pFirstMachine != pFirstMachine->pNext) {
  543. // delete from list
  544. // the deletion routine updates the prev pointer as it
  545. // removes the specified entry.
  546. FreeMachine (pThisMachine->pPrev, TRUE, bProcessExit);
  547. if (pFirstMachine == NULL)
  548. break;
  549. }
  550. // remove last query
  551. if (pFirstMachine)
  552. FreeMachine (pFirstMachine, TRUE, bProcessExit);
  553. pFirstMachine = NULL;
  554. RELEASE_MUTEX (hPdhDataMutex);
  555. } else {
  556. SetLastError (WAIT_TIMEOUT);
  557. return FALSE;
  558. }
  559. }
  560. return TRUE;
  561. }
  562. DWORD
  563. GetObjectId (
  564. IN PPERF_MACHINE pMachine,
  565. IN LPWSTR szObjectName,
  566. IN BOOL *bInstances
  567. )
  568. {
  569. PERF_OBJECT_TYPE * pObject;
  570. pObject = GetObjectDefByName (
  571. pMachine->pSystemPerfData,
  572. pMachine->dwLastPerfString,
  573. pMachine->szPerfStrings,
  574. szObjectName);
  575. if (pObject != NULL) {
  576. // copy name string
  577. LPCWSTR szTmpObjectName = PdhiLookupPerfNameByIndex(
  578. pMachine, pObject->ObjectNameTitleIndex);
  579. if (szObjectName != NULL && szTmpObjectName != NULL) {
  580. lstrcpyW (szObjectName, szTmpObjectName);
  581. }
  582. if (bInstances != NULL) {
  583. *bInstances = (pObject->NumInstances != PERF_NO_INSTANCES ? TRUE : FALSE);
  584. }
  585. return pObject->ObjectNameTitleIndex;
  586. } else {
  587. return (DWORD)-1;
  588. }
  589. }
  590. DWORD
  591. GetCounterId (
  592. PPERF_MACHINE pMachine,
  593. DWORD dwObjectId,
  594. LPWSTR szCounterName
  595. )
  596. {
  597. PERF_OBJECT_TYPE *pObject;
  598. PERF_COUNTER_DEFINITION *pCounter;
  599. pObject = GetObjectDefByTitleIndex(
  600. pMachine->pSystemPerfData,
  601. dwObjectId);
  602. if (pObject != NULL) {
  603. pCounter = GetCounterDefByName (
  604. pObject,
  605. pMachine->dwLastPerfString,
  606. pMachine->szPerfStrings,
  607. szCounterName);
  608. if (pCounter != NULL) {
  609. // update counter name string
  610. LPCWSTR szTmpCounterName = PdhiLookupPerfNameByIndex(
  611. pMachine, pCounter->CounterNameTitleIndex);
  612. if (szCounterName != NULL && szTmpCounterName != NULL) {
  613. lstrcpyW (szCounterName, szTmpCounterName);
  614. }
  615. return pCounter->CounterNameTitleIndex;
  616. } else {
  617. return (DWORD)-1;
  618. }
  619. } else {
  620. return (DWORD)-1;
  621. }
  622. }
  623. BOOL
  624. InitPerflibCounterInfo (
  625. IN PPDHI_COUNTER pCounter
  626. )
  627. /*++
  628. Routine Description:
  629. Initializes the perflib related fields of the counter structure
  630. Arguments:
  631. IN PPDHI_COUNTER pCounter
  632. pointer to the counter structure to initialize
  633. Return Value:
  634. TRUE
  635. --*/
  636. {
  637. PERF_OBJECT_TYPE *pPerfObject = NULL;
  638. PERF_COUNTER_DEFINITION *pPerfCounter = NULL;
  639. if (pCounter->pQMachine->pMachine == NULL) {
  640. pCounter->ThisValue.CStatus = PDH_CSTATUS_NO_MACHINE;
  641. return FALSE;
  642. } else if (pCounter->pQMachine->pMachine->dwStatus != ERROR_SUCCESS) {
  643. // machine not initialized
  644. return FALSE;
  645. }
  646. // get perf object definition from system data structure
  647. pPerfObject = GetObjectDefByTitleIndex (
  648. pCounter->pQMachine->pMachine->pSystemPerfData,
  649. pCounter->plCounterInfo.dwObjectId);
  650. if (pPerfObject != NULL) {
  651. // object was found now look up counter definition
  652. pPerfCounter = GetCounterDefByTitleIndex (pPerfObject, 0,
  653. pCounter->plCounterInfo.dwCounterId);
  654. if (pPerfCounter != NULL) {
  655. // get system perf data info
  656. // (pack into a DWORD)
  657. pCounter->CVersion = pCounter->pQMachine->pMachine->pSystemPerfData->Version;
  658. pCounter->CVersion &= 0x0000FFFF;
  659. pCounter->CVersion <<= 16;
  660. pCounter->CVersion &= 0xFFFF0000;
  661. pCounter->CVersion |= (pCounter->pQMachine->pMachine->pSystemPerfData->Revision & 0x0000FFFF);
  662. // get the counter's time base
  663. if (pPerfCounter->CounterType & PERF_TIMER_100NS) {
  664. pCounter->TimeBase = (LONGLONG)10000000;
  665. } else if (pPerfCounter->CounterType & PERF_OBJECT_TIMER) {
  666. // then get the time base freq from the object
  667. pCounter->TimeBase = pPerfObject->PerfFreq.QuadPart;
  668. } else { // if (pPerfCounter->CounterType & PERF_TIMER_TICK or other)
  669. pCounter->TimeBase = pCounter->pQMachine->pMachine->pSystemPerfData->PerfFreq.QuadPart;
  670. }
  671. // look up info from counter definition
  672. pCounter->plCounterInfo.dwCounterType =
  673. pPerfCounter->CounterType;
  674. pCounter->plCounterInfo.dwCounterSize =
  675. pPerfCounter->CounterSize;
  676. pCounter->plCounterInfo.lDefaultScale =
  677. pPerfCounter->DefaultScale;
  678. //
  679. // get explain text pointer
  680. pCounter->szExplainText =
  681. (LPWSTR)PdhiLookupPerfNameByIndex (
  682. pCounter->pQMachine->pMachine,
  683. pPerfCounter->CounterHelpTitleIndex);
  684. //
  685. // now clear/initialize the raw counter info
  686. //
  687. pCounter->ThisValue.TimeStamp.dwLowDateTime = 0;
  688. pCounter->ThisValue.TimeStamp.dwHighDateTime = 0;
  689. pCounter->ThisValue.MultiCount = 1;
  690. pCounter->ThisValue.FirstValue = 0;
  691. pCounter->ThisValue.SecondValue = 0;
  692. //
  693. pCounter->LastValue.TimeStamp.dwLowDateTime = 0;
  694. pCounter->LastValue.TimeStamp.dwHighDateTime = 0;
  695. pCounter->LastValue.MultiCount = 1;
  696. pCounter->LastValue.FirstValue = 0;
  697. pCounter->LastValue.SecondValue = 0;
  698. //
  699. // clear data array pointers
  700. //
  701. pCounter->pThisRawItemList = NULL;
  702. pCounter->pLastRawItemList = NULL;
  703. //
  704. // lastly update status
  705. //
  706. if (pCounter->ThisValue.CStatus == 0) {
  707. // don't overwrite any other status values
  708. pCounter->ThisValue.CStatus = PDH_CSTATUS_VALID_DATA;
  709. }
  710. return TRUE;
  711. } else {
  712. // unable to find counter
  713. pCounter->ThisValue.CStatus = PDH_CSTATUS_NO_COUNTER;
  714. return FALSE;
  715. }
  716. } else {
  717. // unable to find object
  718. pCounter->ThisValue.CStatus = PDH_CSTATUS_NO_OBJECT;
  719. return FALSE;
  720. }
  721. }
  722. #pragma warning ( disable : 4127 )
  723. STATIC_BOOL
  724. IsNumberInUnicodeList (
  725. IN DWORD dwNumber,
  726. IN LPWSTR lpwszUnicodeList
  727. )
  728. /*++
  729. IsNumberInUnicodeList
  730. Arguments:
  731. IN dwNumber
  732. DWORD number to find in list
  733. IN lpwszUnicodeList
  734. Null terminated, Space delimited list of decimal numbers
  735. Return Value:
  736. TRUE:
  737. dwNumber was found in the list of unicode number strings
  738. FALSE:
  739. dwNumber was not found in the list.
  740. --*/
  741. {
  742. DWORD dwThisNumber;
  743. WCHAR *pwcThisChar;
  744. BOOL bValidNumber;
  745. BOOL bNewItem;
  746. WCHAR wcDelimiter; // could be an argument to be more flexible
  747. if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not founde
  748. pwcThisChar = lpwszUnicodeList;
  749. dwThisNumber = 0;
  750. wcDelimiter = SPACE_L;
  751. bValidNumber = FALSE;
  752. bNewItem = TRUE;
  753. while (TRUE) {
  754. switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
  755. case DIGIT:
  756. // if this is the first digit after a delimiter, then
  757. // set flags to start computing the new number
  758. if (bNewItem) {
  759. bNewItem = FALSE;
  760. bValidNumber = TRUE;
  761. }
  762. if (bValidNumber) {
  763. dwThisNumber *= 10;
  764. dwThisNumber += (*pwcThisChar - (WCHAR)'0');
  765. }
  766. break;
  767. case DELIMITER:
  768. // a delimter is either the delimiter character or the
  769. // end of the string ('\0') if when the delimiter has been
  770. // reached a valid number was found, then compare it to the
  771. // number from the argument list. if this is the end of the
  772. // string and no match was found, then return.
  773. //
  774. if (bValidNumber) {
  775. if (dwThisNumber == dwNumber) return TRUE;
  776. bValidNumber = FALSE;
  777. }
  778. if (*pwcThisChar == 0) {
  779. return FALSE;
  780. } else {
  781. bNewItem = TRUE;
  782. dwThisNumber = 0;
  783. }
  784. break;
  785. case INVALID:
  786. // if an invalid character was encountered, ignore all
  787. // characters up to the next delimiter and then start fresh.
  788. // the invalid number is not compared.
  789. bValidNumber = FALSE;
  790. break;
  791. default:
  792. break;
  793. }
  794. pwcThisChar++;
  795. }
  796. } // IsNumberInUnicodeList
  797. #pragma warning ( default : 4127 )
  798. BOOL
  799. AppendObjectToValueList (
  800. DWORD dwObjectId,
  801. PWSTR pwszValueList
  802. )
  803. /*++
  804. AppendObjectToValueList
  805. Arguments:
  806. IN dwNumber
  807. DWORD number to insert in list
  808. IN PWSTR
  809. pointer to wide char string that contains buffer that is
  810. Null terminated, Space delimited list of decimal numbers that
  811. may have this number appended to.
  812. Return Value:
  813. TRUE:
  814. dwNumber was added to list
  815. FALSE:
  816. dwNumber was not added. (because it's already there or
  817. an error occured)
  818. --*/
  819. {
  820. WCHAR tempString [16] ;
  821. BOOL bReturn = FALSE;
  822. LPWSTR szFormatString;
  823. if (!pwszValueList) {
  824. bReturn = FALSE;
  825. } else if (IsNumberInUnicodeList(dwObjectId, pwszValueList)) {
  826. bReturn = FALSE; // object already in list
  827. } else {
  828. __try {
  829. if (*pwszValueList == 0) {
  830. // then this is the first string so no delimiter
  831. szFormatString = (LPWSTR)fmtDecimal;
  832. } else {
  833. // this is being added to the end so include the delimiter
  834. szFormatString = (LPWSTR)fmtSpaceDecimal;
  835. }
  836. // format number and append the new object id the value list
  837. swprintf (tempString, szFormatString, dwObjectId) ;
  838. lstrcatW (pwszValueList, tempString);
  839. bReturn = TRUE;
  840. } __except (EXCEPTION_EXECUTE_HANDLER) {
  841. bReturn = FALSE;
  842. }
  843. }
  844. return bReturn;
  845. }
  846. BOOL
  847. GetInstanceByNameMatch (
  848. IN PPERF_MACHINE pMachine,
  849. IN PPDHI_COUNTER pCounter
  850. )
  851. {
  852. PPERF_INSTANCE_DEFINITION pInstanceDef;
  853. PPERF_OBJECT_TYPE pObjectDef;
  854. LONG lInstanceId = PERF_NO_UNIQUE_ID;
  855. // get the instances object
  856. pObjectDef = GetObjectDefByTitleIndex(
  857. pMachine->pSystemPerfData,
  858. pCounter->plCounterInfo.dwObjectId);
  859. if (pObjectDef != NULL) {
  860. pInstanceDef = FirstInstance (pObjectDef);
  861. if (pInstanceDef->UniqueID == PERF_NO_UNIQUE_ID) {
  862. // get instance in that object by comparing names
  863. // if there is no parent specified, then just look it up by name
  864. pInstanceDef = GetInstanceByName (
  865. pMachine->pSystemPerfData,
  866. pObjectDef,
  867. pCounter->pCounterPath->szInstanceName,
  868. pCounter->pCounterPath->szParentName,
  869. pCounter->pCounterPath->dwIndex);
  870. } else {
  871. // get numeric equivalent of Instance ID
  872. if (pCounter->pCounterPath->szInstanceName != NULL) {
  873. lInstanceId = wcstol (
  874. pCounter->pCounterPath->szInstanceName,
  875. NULL, 10);
  876. }
  877. pInstanceDef = GetInstanceByUniqueId (
  878. pObjectDef, lInstanceId);
  879. }
  880. // update counter fields
  881. pCounter->plCounterInfo.lInstanceId = lInstanceId;
  882. if (lInstanceId == -1) {
  883. // use instance NAME
  884. // GetInstanceNameStr (pInstanceDef,
  885. // pCounter->pCounterPath->szInstanceName,
  886. // pObjectDef->CodePage);
  887. pCounter->plCounterInfo.szInstanceName =
  888. pCounter->pCounterPath->szInstanceName;
  889. pCounter->plCounterInfo.szParentInstanceName =
  890. pCounter->pCounterPath->szParentName;
  891. } else {
  892. // use instance ID number
  893. pCounter->plCounterInfo.szInstanceName = NULL;
  894. pCounter->plCounterInfo.szParentInstanceName = NULL;
  895. }
  896. if (pInstanceDef != NULL) {
  897. // instance found
  898. return TRUE;
  899. } else {
  900. // unable to find instance
  901. return FALSE;
  902. }
  903. } else {
  904. return FALSE;
  905. }
  906. }
  907. BOOL
  908. GetObjectPerfInfo (
  909. IN PPERF_DATA_BLOCK pPerfData,
  910. IN DWORD dwObjectId,
  911. IN LONGLONG *pPerfTime,
  912. IN LONGLONG *pPerfFreq
  913. )
  914. {
  915. PERF_OBJECT_TYPE * pObject;
  916. BOOL bReturn = FALSE;
  917. pObject = GetObjectDefByTitleIndex (pPerfData, dwObjectId);
  918. if (pObject != NULL) {
  919. __try {
  920. *pPerfTime = pObject->PerfTime.QuadPart;
  921. *pPerfFreq = pObject->PerfFreq.QuadPart;
  922. bReturn = TRUE;
  923. } __except (EXCEPTION_EXECUTE_HANDLER) {
  924. bReturn = FALSE;
  925. }
  926. }
  927. return bReturn;
  928. }
  929. PDH_STATUS
  930. ValidateMachineConnection (
  931. IN PPERF_MACHINE pMachine
  932. )
  933. {
  934. PDH_STATUS pdhStatus;
  935. HANDLE hThread;
  936. DWORD ThreadId;
  937. DWORD dwWaitStatus;
  938. DWORD dwReconnecting;
  939. LONGLONG llCurrentTime;
  940. // if a connection or request has failed, this will be
  941. // set to an error status
  942. if (pMachine != NULL) {
  943. if (pMachine->dwStatus != ERROR_SUCCESS) {
  944. // get the current time
  945. GetLocalFileTime (&llCurrentTime);
  946. if (pMachine->llRetryTime < llCurrentTime) {
  947. if (pMachine->llRetryTime != 0) {
  948. // see what's up by trying to reconnect
  949. // dwReconnecting = (DWORD)InterlockedCompareExchange (
  950. // (PLONG)&pMachine->dwRetryFlags, TRUE, FALSE);
  951. dwReconnecting = pMachine->dwRetryFlags;
  952. if (!dwReconnecting) {
  953. // start another thread to connect to the machine then
  954. // wait for the thread to return. If it returns in time, then
  955. // use it, otherwise indicate it's not available and continue
  956. hThread = CreateThread (
  957. NULL,
  958. 0,
  959. (LPTHREAD_START_ROUTINE)ConnectMachine,
  960. (LPVOID)pMachine,
  961. 0,
  962. &ThreadId);
  963. if (hThread != NULL) {
  964. // wait for the thread to complete or the timeout to expire
  965. dwWaitStatus = WaitForSingleObject (hThread, 500);
  966. if (dwWaitStatus == WAIT_TIMEOUT) {
  967. // then this is taking too long so set an error and
  968. // continue. If the machine is off line, then the
  969. // thread will eventually complete and the machine
  970. // status will indicate that it's offline. If the
  971. // machine connects after the timeout, it'll be
  972. // picked up on the next scan.
  973. pdhStatus = PDH_CSTATUS_NO_MACHINE;
  974. } else {
  975. // get the thread's exit code
  976. GetExitCodeThread (hThread, (LPDWORD)&pdhStatus);
  977. }
  978. CloseHandle (hThread);
  979. } else {
  980. // unable to creat thread to connect to machine
  981. // so return not available status
  982. pdhStatus = PDH_CANNOT_CONNECT_MACHINE;
  983. }
  984. } else {
  985. // a connection attempt is in process so do nothing here
  986. pdhStatus = PDH_CANNOT_CONNECT_MACHINE;
  987. }
  988. } else {
  989. // everything's fine
  990. pdhStatus = ERROR_SUCCESS;
  991. }
  992. } else {
  993. // it's not retry time, yet so machine is off line still
  994. pdhStatus = PDH_CSTATUS_NO_MACHINE;
  995. }
  996. } else {
  997. // everything's fine
  998. pdhStatus = ERROR_SUCCESS;
  999. }
  1000. } else {
  1001. pdhStatus = PDH_CSTATUS_NO_MACHINE;
  1002. }
  1003. return pdhStatus;
  1004. }