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.

559 lines
14 KiB

  1. /*++
  2. Copyright (c) 1995-6 Microsoft Corporation
  3. Module Name:
  4. perfutil.c
  5. Abstract:
  6. This file implements the utility routines used to construct the
  7. common parts of a PERF_INSTANCE_DEFINITION (see winperf.h) and
  8. perform event logging functions.
  9. Created:
  10. Bob Watson 28-Jul-1995
  11. Revision History:
  12. --*/
  13. //
  14. // include files
  15. //
  16. #include <nt.h>
  17. #include <ntrtl.h>
  18. #include <nturtl.h>
  19. #include <windows.h>
  20. #include <string.h>
  21. #include <winperf.h>
  22. #include "perfutil.h"
  23. #include "perfmsg.h"
  24. //
  25. // Global data definitions.
  26. //
  27. ULONG ulInfoBufferSize = 0;
  28. extern HANDLE hEventLog; // event log handle for reporting events
  29. // initialized in Open... routines
  30. DWORD dwLogUsers = 0; // count of functions using event log
  31. DWORD MESSAGE_LEVEL = 0;
  32. const WCHAR GLOBAL_STRING[] = L"Global";
  33. const WCHAR FOREIGN_STRING[] = L"Foreign";
  34. const WCHAR COSTLY_STRING[] = L"Costly";
  35. const WCHAR NULL_STRING[] = L"\0"; // pointer to null string
  36. const WCHAR szTotalValue[] = L"TotalInstanceName";
  37. const WCHAR szDefaultTotalString[] = L"_Total";
  38. // test for delimiter, end of line and non-digit characters
  39. // used by IsNumberInUnicodeList routine
  40. //
  41. #define DIGIT 1
  42. #define DELIMITER 2
  43. #define INVALID 3
  44. #define EvalThisChar(c,d) ( \
  45. (c == d) ? DELIMITER : \
  46. (c == 0) ? DELIMITER : \
  47. (c < (WCHAR)'0') ? INVALID : \
  48. (c > (WCHAR)'9') ? INVALID : \
  49. DIGIT)
  50. LONG
  51. GetPerflibKeyValue (
  52. LPCWSTR szItem,
  53. DWORD dwRegType,
  54. DWORD dwMaxSize, // ... of pReturnBuffer in bytes
  55. LPVOID pReturnBuffer,
  56. DWORD dwDefaultSize, // ... of pDefault in bytes
  57. LPVOID pDefault
  58. )
  59. /*++
  60. read and return the current value of the specified value
  61. under the Perflib registry key. If unable to read the value
  62. return the default value from the argument list.
  63. the value is returned in the pReturnBuffer.
  64. --*/
  65. {
  66. HKEY hPerflibKey;
  67. OBJECT_ATTRIBUTES Obja;
  68. NTSTATUS Status;
  69. UNICODE_STRING PerflibSubKeyString;
  70. UNICODE_STRING ValueNameString;
  71. LONG lReturn;
  72. PKEY_VALUE_PARTIAL_INFORMATION pValueInformation;
  73. DWORD ValueBufferLength;
  74. DWORD ResultLength;
  75. BOOL bUseDefault = TRUE;
  76. // initialize UNICODE_STRING structures used in this function
  77. RtlInitUnicodeString (
  78. &PerflibSubKeyString,
  79. L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
  80. RtlInitUnicodeString (
  81. &ValueNameString,
  82. (LPWSTR)szItem);
  83. //
  84. // Initialize the OBJECT_ATTRIBUTES structure and open the key.
  85. //
  86. InitializeObjectAttributes(
  87. &Obja,
  88. &PerflibSubKeyString,
  89. OBJ_CASE_INSENSITIVE,
  90. NULL,
  91. NULL
  92. );
  93. Status = NtOpenKey(
  94. &hPerflibKey,
  95. KEY_READ,
  96. &Obja
  97. );
  98. if (NT_SUCCESS( Status )) {
  99. // read value of desired entry
  100. ValueBufferLength = ResultLength = 1024;
  101. pValueInformation = ALLOCMEM(RtlProcessHeap(), 0, ResultLength);
  102. if (pValueInformation != NULL) {
  103. while ( (Status = NtQueryValueKey(hPerflibKey,
  104. &ValueNameString,
  105. KeyValuePartialInformation,
  106. pValueInformation,
  107. ValueBufferLength,
  108. &ResultLength))
  109. == STATUS_BUFFER_OVERFLOW ) {
  110. pValueInformation = REALLOCMEM(RtlProcessHeap(), 0,
  111. pValueInformation,
  112. ResultLength);
  113. if ( pValueInformation == NULL) {
  114. break;
  115. } else {
  116. ValueBufferLength = ResultLength;
  117. }
  118. }
  119. if (NT_SUCCESS(Status)) {
  120. // check to see if it's the desired type
  121. if (pValueInformation->Type == dwRegType) {
  122. // see if it will fit
  123. if (pValueInformation->DataLength <= dwMaxSize) {
  124. memcpy (pReturnBuffer, &pValueInformation->Data[0],
  125. pValueInformation->DataLength);
  126. bUseDefault = FALSE;
  127. lReturn = STATUS_SUCCESS;
  128. }
  129. }
  130. } else {
  131. // return the default value
  132. lReturn = Status;
  133. }
  134. // Release temp buffer. Check pValueInfo again since
  135. // the realloc may have failed above.
  136. if (pValueInformation != NULL) {
  137. FREEMEM (RtlProcessHeap(), 0, pValueInformation);
  138. }
  139. } else {
  140. // unable to allocate memory for this operation so
  141. // just return the default value
  142. }
  143. // close the registry key
  144. NtClose(hPerflibKey);
  145. } else {
  146. // return default value
  147. }
  148. if (bUseDefault) {
  149. memcpy (pReturnBuffer, pDefault, dwDefaultSize);
  150. lReturn = STATUS_SUCCESS;
  151. }
  152. return lReturn;
  153. }
  154. HANDLE
  155. MonOpenEventLog (
  156. IN LPWSTR szAppName
  157. )
  158. /*++
  159. Routine Description:
  160. Reads the level of event logging from the registry and opens the
  161. channel to the event logger for subsequent event log entries.
  162. Arguments:
  163. None
  164. Return Value:
  165. Handle to the event log for reporting events.
  166. NULL if open not successful.
  167. --*/
  168. {
  169. HKEY hAppKey;
  170. TCHAR LogLevelKeyName[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
  171. TCHAR LogLevelValueName[] = TEXT("EventLogLevel");
  172. LONG lStatus;
  173. DWORD dwLogLevel;
  174. DWORD dwValueType;
  175. DWORD dwValueSize;
  176. // if global value of the logging level not initialized or is disabled,
  177. // check the registry to see if it should be updated.
  178. if (!MESSAGE_LEVEL) {
  179. lStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  180. LogLevelKeyName,
  181. 0,
  182. KEY_READ,
  183. &hAppKey);
  184. dwValueSize = sizeof (dwLogLevel);
  185. if (lStatus == ERROR_SUCCESS) {
  186. lStatus = RegQueryValueEx (hAppKey,
  187. LogLevelValueName,
  188. (LPDWORD)NULL,
  189. &dwValueType,
  190. (LPBYTE)&dwLogLevel,
  191. &dwValueSize);
  192. if (lStatus == ERROR_SUCCESS) {
  193. MESSAGE_LEVEL = dwLogLevel;
  194. } else {
  195. MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
  196. }
  197. RegCloseKey (hAppKey);
  198. } else {
  199. MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
  200. }
  201. }
  202. if (hEventLog == NULL){
  203. hEventLog = RegisterEventSourceW (
  204. (LPWSTR)NULL, // Use Local Machine
  205. szAppName); // event log app name to find in registry
  206. }
  207. if (hEventLog != NULL) {
  208. dwLogUsers++; // increment count of perfctr log users
  209. }
  210. return (hEventLog);
  211. }
  212. VOID
  213. MonCloseEventLog (
  214. )
  215. /*++
  216. Routine Description:
  217. Closes the handle to the event logger if this is the last caller
  218. Arguments:
  219. None
  220. Return Value:
  221. None
  222. --*/
  223. {
  224. if (hEventLog != NULL) {
  225. dwLogUsers--; // decrement usage
  226. if (dwLogUsers <= 0) { // and if we're the last, then close up log
  227. DeregisterEventSource (hEventLog);
  228. }
  229. }
  230. }
  231. DWORD
  232. GetQueryType (
  233. IN LPWSTR lpValue
  234. )
  235. /*++
  236. GetQueryType
  237. returns the type of query described in the lpValue string so that
  238. the appropriate processing method may be used
  239. Arguments
  240. IN lpValue
  241. string passed to PerfRegQuery Value for processing
  242. Return Value
  243. QUERY_GLOBAL
  244. if lpValue == 0 (null pointer)
  245. lpValue == pointer to Null string
  246. lpValue == pointer to "Global" string
  247. QUERY_FOREIGN
  248. if lpValue == pointer to "Foriegn" string
  249. QUERY_COSTLY
  250. if lpValue == pointer to "Costly" string
  251. otherwise:
  252. QUERY_ITEMS
  253. --*/
  254. {
  255. WCHAR *pwcArgChar, *pwcTypeChar;
  256. BOOL bFound;
  257. if (lpValue == 0) {
  258. return QUERY_GLOBAL;
  259. } else if (*lpValue == 0) {
  260. return QUERY_GLOBAL;
  261. }
  262. // check for "Global" request
  263. pwcArgChar = lpValue;
  264. pwcTypeChar = (LPWSTR)GLOBAL_STRING;
  265. bFound = TRUE; // assume found until contradicted
  266. // check to the length of the shortest string
  267. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  268. if (*pwcArgChar++ != *pwcTypeChar++) {
  269. bFound = FALSE; // no match
  270. break; // bail out now
  271. }
  272. }
  273. if (bFound) return QUERY_GLOBAL;
  274. // check for "Foreign" request
  275. pwcArgChar = lpValue;
  276. pwcTypeChar = (LPWSTR)FOREIGN_STRING;
  277. bFound = TRUE; // assume found until contradicted
  278. // check to the length of the shortest string
  279. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  280. if (*pwcArgChar++ != *pwcTypeChar++) {
  281. bFound = FALSE; // no match
  282. break; // bail out now
  283. }
  284. }
  285. if (bFound) return QUERY_FOREIGN;
  286. // check for "Costly" request
  287. pwcArgChar = lpValue;
  288. pwcTypeChar = (LPWSTR)COSTLY_STRING;
  289. bFound = TRUE; // assume found until contradicted
  290. // check to the length of the shortest string
  291. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  292. if (*pwcArgChar++ != *pwcTypeChar++) {
  293. bFound = FALSE; // no match
  294. break; // bail out now
  295. }
  296. }
  297. if (bFound) return QUERY_COSTLY;
  298. // if not Global and not Foreign and not Costly,
  299. // then it must be an item list
  300. return QUERY_ITEMS;
  301. }
  302. BOOL
  303. IsNumberInUnicodeList (
  304. IN DWORD dwNumber,
  305. IN LPWSTR lpwszUnicodeList
  306. )
  307. /*++
  308. IsNumberInUnicodeList
  309. Arguments:
  310. IN dwNumber
  311. DWORD number to find in list
  312. IN lpwszUnicodeList
  313. Null terminated, Space delimited list of decimal numbers
  314. Return Value:
  315. TRUE:
  316. dwNumber was found in the list of unicode number strings
  317. FALSE:
  318. dwNumber was not found in the list.
  319. --*/
  320. {
  321. DWORD dwThisNumber;
  322. WCHAR *pwcThisChar;
  323. BOOL bValidNumber;
  324. BOOL bNewItem;
  325. WCHAR wcDelimiter; // could be an argument to be more flexible
  326. if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not founde
  327. pwcThisChar = lpwszUnicodeList;
  328. dwThisNumber = 0;
  329. wcDelimiter = (WCHAR)' ';
  330. bValidNumber = FALSE;
  331. bNewItem = TRUE;
  332. while (TRUE) {
  333. switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
  334. case DIGIT:
  335. // if this is the first digit after a delimiter, then
  336. // set flags to start computing the new number
  337. if (bNewItem) {
  338. bNewItem = FALSE;
  339. bValidNumber = TRUE;
  340. }
  341. if (bValidNumber) {
  342. dwThisNumber *= 10;
  343. dwThisNumber += (*pwcThisChar - (WCHAR)'0');
  344. }
  345. break;
  346. case DELIMITER:
  347. // a delimter is either the delimiter character or the
  348. // end of the string ('\0') if when the delimiter has been
  349. // reached a valid number was found, then compare it to the
  350. // number from the argument list. if this is the end of the
  351. // string and no match was found, then return.
  352. //
  353. if (bValidNumber) {
  354. if (dwThisNumber == dwNumber) return TRUE;
  355. bValidNumber = FALSE;
  356. }
  357. if (*pwcThisChar == 0) {
  358. return FALSE;
  359. } else {
  360. bNewItem = TRUE;
  361. dwThisNumber = 0;
  362. }
  363. break;
  364. case INVALID:
  365. // if an invalid character was encountered, ignore all
  366. // characters up to the next delimiter and then start fresh.
  367. // the invalid number is not compared.
  368. bValidNumber = FALSE;
  369. break;
  370. default:
  371. break;
  372. }
  373. pwcThisChar++;
  374. }
  375. return FALSE;
  376. } // IsNumberInUnicodeList
  377. BOOL
  378. MonBuildInstanceDefinition(
  379. PERF_INSTANCE_DEFINITION *pBuffer,
  380. PVOID *pBufferNext,
  381. DWORD ParentObjectTitleIndex,
  382. DWORD ParentObjectInstance,
  383. DWORD UniqueID,
  384. LPWSTR Name
  385. )
  386. /*++
  387. MonBuildInstanceDefinition - Build an instance of an object
  388. Inputs:
  389. pBuffer - pointer to buffer where instance is to
  390. be constructed
  391. pBufferNext - pointer to a pointer which will contain
  392. next available location, DWORD aligned
  393. ParentObjectTitleIndex
  394. - Title Index of parent object type; 0 if
  395. no parent object
  396. ParentObjectInstance
  397. - Index into instances of parent object
  398. type, starting at 0, for this instances
  399. parent object instance
  400. UniqueID - a unique identifier which should be used
  401. instead of the Name for identifying
  402. this instance
  403. Name - Name of this instance
  404. --*/
  405. {
  406. DWORD NameLength;
  407. LPWSTR pName;
  408. //
  409. // Include trailing null in name size
  410. //
  411. NameLength = (lstrlenW(Name) + 1) * sizeof(WCHAR);
  412. pBuffer->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) +
  413. NameLength;
  414. pBuffer->ParentObjectTitleIndex = ParentObjectTitleIndex;
  415. pBuffer->ParentObjectInstance = ParentObjectInstance;
  416. pBuffer->UniqueID = UniqueID;
  417. pBuffer->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
  418. pBuffer->NameLength = NameLength;
  419. // copy name to name buffer
  420. pName = (LPWSTR)&pBuffer[1];
  421. memcpy(pName,Name,NameLength);
  422. // update "next byte" pointer
  423. *pBufferNext = (PVOID) ((PCHAR) pBuffer + pBuffer->ByteLength);
  424. // round up to put next buffer on a QUADWORD boundry
  425. *pBufferNext = ALIGN_ON_QWORD (*pBufferNext);
  426. // adjust length value to match new length
  427. pBuffer->ByteLength = (ULONG)((ULONG_PTR)*pBufferNext - (ULONG_PTR)pBuffer);
  428. return 0;
  429. }