Leaked source code of windows server 2003
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.

620 lines
17 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 = ERROR_SUCCESS;
  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. (LPCWSTR)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(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. FREEMEM(pValueInformation);
  111. pValueInformation = ALLOCMEM(ResultLength);
  112. if ( pValueInformation == NULL) {
  113. ValueBufferLength = 0;
  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
  135. FREEMEM (pValueInformation);
  136. } else {
  137. // unable to allocate memory for this operation so
  138. // just return the default value
  139. }
  140. // close the registry key
  141. NtClose(hPerflibKey);
  142. } else {
  143. // return default value
  144. }
  145. if (bUseDefault) {
  146. memcpy (pReturnBuffer, pDefault, dwDefaultSize);
  147. lReturn = STATUS_SUCCESS;
  148. }
  149. return lReturn;
  150. }
  151. HANDLE
  152. MonOpenEventLog (
  153. IN LPWSTR szAppName
  154. )
  155. /*++
  156. Routine Description:
  157. Reads the level of event logging from the registry and opens the
  158. channel to the event logger for subsequent event log entries.
  159. Arguments:
  160. None
  161. Return Value:
  162. Handle to the event log for reporting events.
  163. NULL if open not successful.
  164. --*/
  165. {
  166. HKEY hAppKey;
  167. TCHAR LogLevelKeyName[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
  168. TCHAR LogLevelValueName[] = TEXT("EventLogLevel");
  169. LONG lStatus;
  170. DWORD dwLogLevel;
  171. DWORD dwValueType;
  172. DWORD dwValueSize;
  173. // if global value of the logging level not initialized or is disabled,
  174. // check the registry to see if it should be updated.
  175. if (!MESSAGE_LEVEL) {
  176. lStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  177. LogLevelKeyName,
  178. 0,
  179. KEY_READ,
  180. &hAppKey);
  181. dwValueSize = sizeof (dwLogLevel);
  182. if (lStatus == ERROR_SUCCESS) {
  183. lStatus = RegQueryValueEx (hAppKey,
  184. LogLevelValueName,
  185. (LPDWORD)NULL,
  186. &dwValueType,
  187. (LPBYTE)&dwLogLevel,
  188. &dwValueSize);
  189. if (lStatus == ERROR_SUCCESS) {
  190. MESSAGE_LEVEL = dwLogLevel;
  191. } else {
  192. MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
  193. }
  194. RegCloseKey (hAppKey);
  195. } else {
  196. MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
  197. }
  198. }
  199. if (hEventLog == NULL){
  200. hEventLog = RegisterEventSourceW (
  201. (LPTSTR)NULL, // Use Local Machine
  202. szAppName); // event log app name to find in registry
  203. }
  204. if (hEventLog != NULL) {
  205. dwLogUsers++; // increment count of perfctr log users
  206. }
  207. return (hEventLog);
  208. }
  209. VOID
  210. MonCloseEventLog (
  211. )
  212. /*++
  213. Routine Description:
  214. Closes the handle to the event logger if this is the last caller
  215. Arguments:
  216. None
  217. Return Value:
  218. None
  219. --*/
  220. {
  221. if (hEventLog != NULL) {
  222. dwLogUsers--; // decrement usage
  223. if (dwLogUsers <= 0) { // and if we're the last, then close up log
  224. DeregisterEventSource (hEventLog);
  225. }
  226. }
  227. }
  228. DWORD
  229. GetQueryType (
  230. IN LPWSTR lpValue
  231. )
  232. /*++
  233. GetQueryType
  234. returns the type of query described in the lpValue string so that
  235. the appropriate processing method may be used
  236. Arguments
  237. IN lpValue
  238. string passed to PerfRegQuery Value for processing
  239. Return Value
  240. QUERY_GLOBAL
  241. if lpValue == 0 (null pointer)
  242. lpValue == pointer to Null string
  243. lpValue == pointer to "Global" string
  244. QUERY_FOREIGN
  245. if lpValue == pointer to "Foriegn" string
  246. QUERY_COSTLY
  247. if lpValue == pointer to "Costly" string
  248. otherwise:
  249. QUERY_ITEMS
  250. --*/
  251. {
  252. WCHAR *pwcArgChar, *pwcTypeChar;
  253. BOOL bFound;
  254. if (lpValue == 0) {
  255. return QUERY_GLOBAL;
  256. } else if (*lpValue == 0) {
  257. return QUERY_GLOBAL;
  258. }
  259. // check for "Global" request
  260. pwcArgChar = lpValue;
  261. pwcTypeChar = (LPWSTR)GLOBAL_STRING;
  262. bFound = TRUE; // assume found until contradicted
  263. // check to the length of the shortest string
  264. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  265. if (*pwcArgChar++ != *pwcTypeChar++) {
  266. bFound = FALSE; // no match
  267. break; // bail out now
  268. }
  269. }
  270. if (bFound) return QUERY_GLOBAL;
  271. // check for "Foreign" request
  272. pwcArgChar = lpValue;
  273. pwcTypeChar = (LPWSTR)FOREIGN_STRING;
  274. bFound = TRUE; // assume found until contradicted
  275. // check to the length of the shortest string
  276. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  277. if (*pwcArgChar++ != *pwcTypeChar++) {
  278. bFound = FALSE; // no match
  279. break; // bail out now
  280. }
  281. }
  282. if (bFound) return QUERY_FOREIGN;
  283. // check for "Costly" request
  284. pwcArgChar = lpValue;
  285. pwcTypeChar = (LPWSTR)COSTLY_STRING;
  286. bFound = TRUE; // assume found until contradicted
  287. // check to the length of the shortest string
  288. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  289. if (*pwcArgChar++ != *pwcTypeChar++) {
  290. bFound = FALSE; // no match
  291. break; // bail out now
  292. }
  293. }
  294. if (bFound) return QUERY_COSTLY;
  295. // if not Global and not Foreign and not Costly,
  296. // then it must be an item list
  297. return QUERY_ITEMS;
  298. }
  299. BOOL
  300. IsNumberInUnicodeList (
  301. IN DWORD dwNumber,
  302. IN LPWSTR lpwszUnicodeList
  303. )
  304. /*++
  305. IsNumberInUnicodeList
  306. Arguments:
  307. IN dwNumber
  308. DWORD number to find in list
  309. IN lpwszUnicodeList
  310. Null terminated, Space delimited list of decimal numbers
  311. Return Value:
  312. TRUE:
  313. dwNumber was found in the list of unicode number strings
  314. FALSE:
  315. dwNumber was not found in the list.
  316. --*/
  317. {
  318. DWORD dwThisNumber;
  319. WCHAR *pwcThisChar;
  320. BOOL bValidNumber;
  321. BOOL bNewItem;
  322. WCHAR wcDelimiter; // could be an argument to be more flexible
  323. BOOL bNotDone;
  324. BOOL bReturnValue = FALSE;
  325. if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not founde
  326. pwcThisChar = lpwszUnicodeList;
  327. dwThisNumber = 0;
  328. wcDelimiter = (WCHAR)' ';
  329. bValidNumber = FALSE;
  330. bNewItem = TRUE;
  331. bNotDone = TRUE;
  332. while ( bNotDone ) {
  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) {
  355. bNotDone = FALSE;
  356. bReturnValue = TRUE;
  357. } else {
  358. bValidNumber = FALSE;
  359. }
  360. }
  361. if (*pwcThisChar == 0) {
  362. bNotDone = FALSE;
  363. } else {
  364. bNewItem = TRUE;
  365. dwThisNumber = 0;
  366. }
  367. break;
  368. case INVALID:
  369. // if an invalid character was encountered, ignore all
  370. // characters up to the next delimiter and then start fresh.
  371. // the invalid number is not compared.
  372. bValidNumber = FALSE;
  373. break;
  374. default:
  375. break;
  376. }
  377. pwcThisChar++;
  378. }
  379. return bReturnValue;
  380. } // IsNumberInUnicodeList
  381. BOOL
  382. MonBuildInstanceDefinitionByUnicodeString(
  383. PERF_INSTANCE_DEFINITION *pBuffer,
  384. PVOID *pBufferNext,
  385. DWORD ParentObjectTitleIndex,
  386. DWORD ParentObjectInstance,
  387. DWORD UniqueID,
  388. PUNICODE_STRING Name
  389. )
  390. /*++
  391. MonBuildInstanceDefinition - Build an instance of an object
  392. Inputs:
  393. pBuffer - pointer to buffer where instance is to
  394. be constructed
  395. pBufferNext - pointer to a pointer which will contain
  396. next available location, DWORD aligned
  397. ParentObjectTitleIndex
  398. - Title Index of parent object type; 0 if
  399. no parent object
  400. ParentObjectInstance
  401. - Index into instances of parent object
  402. type, starting at 0, for this instances
  403. parent object instance
  404. UniqueID - a unique identifier which should be used
  405. instead of the Name for identifying
  406. this instance
  407. Name - Name of this instance
  408. --*/
  409. {
  410. DWORD NameLength;
  411. WCHAR *pName;
  412. //
  413. // Include trailing null in name size
  414. //
  415. //ASSERT( (((ULONG_PTR) pBuffer) & 0x07) == 0 );
  416. NameLength = Name->Length;
  417. if ( !NameLength ||
  418. Name->Buffer[(NameLength/sizeof(WCHAR))-1] != UNICODE_NULL ) {
  419. NameLength += sizeof(WCHAR);
  420. }
  421. pBuffer->ByteLength = QWORD_MULTIPLE(sizeof(PERF_INSTANCE_DEFINITION) + NameLength);
  422. pBuffer->ParentObjectTitleIndex = ParentObjectTitleIndex;
  423. pBuffer->ParentObjectInstance = ParentObjectInstance;
  424. pBuffer->UniqueID = UniqueID;
  425. pBuffer->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
  426. pBuffer->NameLength = NameLength;
  427. pName = (PWCHAR)&pBuffer[1];
  428. RtlMoveMemory(pName,Name->Buffer,Name->Length);
  429. // Always null terminated. Space for this reserved above.
  430. pName[(NameLength/sizeof(WCHAR))-1] = UNICODE_NULL;
  431. *pBufferNext = (PVOID) ((PCHAR) pBuffer + pBuffer->ByteLength);
  432. return 0;
  433. }
  434. BOOL
  435. MonBuildInstanceDefinition(
  436. PERF_INSTANCE_DEFINITION *pBuffer,
  437. PVOID *pBufferNext,
  438. DWORD ParentObjectTitleIndex,
  439. DWORD ParentObjectInstance,
  440. DWORD UniqueID,
  441. LPWSTR Name
  442. )
  443. /*++
  444. MonBuildInstanceDefinition - Build an instance of an object
  445. Inputs:
  446. pBuffer - pointer to buffer where instance is to
  447. be constructed
  448. pBufferNext - pointer to a pointer which will contain
  449. next available location, DWORD aligned
  450. ParentObjectTitleIndex
  451. - Title Index of parent object type; 0 if
  452. no parent object
  453. ParentObjectInstance
  454. - Index into instances of parent object
  455. type, starting at 0, for this instances
  456. parent object instance
  457. UniqueID - a unique identifier which should be used
  458. instead of the Name for identifying
  459. this instance
  460. Name - Name of this instance
  461. --*/
  462. {
  463. DWORD NameLength;
  464. UNICODE_STRING uName;
  465. //
  466. // Include trailing null in name size
  467. //
  468. NameLength = lstrlenW(Name) * sizeof(WCHAR);
  469. uName.Buffer = Name;
  470. uName.Length = (USHORT) NameLength;
  471. uName.MaximumLength = uName.Length + sizeof(WCHAR);
  472. return
  473. MonBuildInstanceDefinitionByUnicodeString(
  474. pBuffer,
  475. pBufferNext,
  476. ParentObjectTitleIndex,
  477. ParentObjectInstance,
  478. UniqueID,
  479. &uName);
  480. }