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.

562 lines
15 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, pBuffer;
  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(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. pBuffer = pValueInformation;
  111. pValueInformation = REALLOCMEM(RtlProcessHeap(), 0,
  112. pValueInformation,
  113. ResultLength);
  114. if ( pValueInformation == NULL) {
  115. FREEMEM(RtlProcessHeap(), 0, pBuffer);
  116. break;
  117. } else {
  118. ValueBufferLength = ResultLength;
  119. }
  120. }
  121. if (NT_SUCCESS(Status)) {
  122. // check to see if it's the desired type
  123. if (pValueInformation->Type == dwRegType) {
  124. // see if it will fit
  125. if (pValueInformation->DataLength <= dwMaxSize) {
  126. memcpy (pReturnBuffer, &pValueInformation->Data[0],
  127. pValueInformation->DataLength);
  128. bUseDefault = FALSE;
  129. lReturn = STATUS_SUCCESS;
  130. }
  131. }
  132. } else {
  133. // return the default value
  134. lReturn = Status;
  135. }
  136. // release temp buffer
  137. FREEMEM (RtlProcessHeap(), 0, pValueInformation);
  138. } else {
  139. // unable to allocate memory for this operation so
  140. // just return the default value
  141. }
  142. // close the registry key
  143. NtClose(hPerflibKey);
  144. } else {
  145. // return default value
  146. }
  147. if (bUseDefault) {
  148. memcpy (pReturnBuffer, pDefault, dwDefaultSize);
  149. lReturn = STATUS_SUCCESS;
  150. }
  151. return lReturn;
  152. }
  153. HANDLE
  154. MonOpenEventLog (
  155. IN LPWSTR szAppName
  156. )
  157. /*++
  158. Routine Description:
  159. Reads the level of event logging from the registry and opens the
  160. channel to the event logger for subsequent event log entries.
  161. Arguments:
  162. None
  163. Return Value:
  164. Handle to the event log for reporting events.
  165. NULL if open not successful.
  166. --*/
  167. {
  168. HKEY hAppKey;
  169. TCHAR LogLevelKeyName[] = TEXT("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib");
  170. TCHAR LogLevelValueName[] = TEXT("EventLogLevel");
  171. LONG lStatus;
  172. DWORD dwLogLevel;
  173. DWORD dwValueType;
  174. DWORD dwValueSize;
  175. // if global value of the logging level not initialized or is disabled,
  176. // check the registry to see if it should be updated.
  177. if (!MESSAGE_LEVEL) {
  178. lStatus = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
  179. LogLevelKeyName,
  180. 0,
  181. KEY_READ,
  182. &hAppKey);
  183. dwValueSize = sizeof (dwLogLevel);
  184. if (lStatus == ERROR_SUCCESS) {
  185. lStatus = RegQueryValueEx (hAppKey,
  186. LogLevelValueName,
  187. (LPDWORD)NULL,
  188. &dwValueType,
  189. (LPBYTE)&dwLogLevel,
  190. &dwValueSize);
  191. if (lStatus == ERROR_SUCCESS) {
  192. MESSAGE_LEVEL = dwLogLevel;
  193. } else {
  194. MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
  195. }
  196. RegCloseKey (hAppKey);
  197. } else {
  198. MESSAGE_LEVEL = MESSAGE_LEVEL_DEFAULT;
  199. }
  200. }
  201. if (hEventLog == NULL){
  202. hEventLog = RegisterEventSourceW (
  203. (LPTSTR)NULL, // Use Local Machine
  204. szAppName); // event log app name to find in registry
  205. }
  206. if (hEventLog != NULL) {
  207. dwLogUsers++; // increment count of perfctr log users
  208. }
  209. return (hEventLog);
  210. }
  211. VOID
  212. MonCloseEventLog (
  213. )
  214. /*++
  215. Routine Description:
  216. Closes the handle to the event logger if this is the last caller
  217. Arguments:
  218. None
  219. Return Value:
  220. None
  221. --*/
  222. {
  223. if (hEventLog != NULL) {
  224. dwLogUsers--; // decrement usage
  225. if (dwLogUsers <= 0) { // and if we're the last, then close up log
  226. DeregisterEventSource (hEventLog);
  227. }
  228. }
  229. }
  230. DWORD
  231. GetQueryType (
  232. IN LPWSTR lpValue
  233. )
  234. /*++
  235. GetQueryType
  236. returns the type of query described in the lpValue string so that
  237. the appropriate processing method may be used
  238. Arguments
  239. IN lpValue
  240. string passed to PerfRegQuery Value for processing
  241. Return Value
  242. QUERY_GLOBAL
  243. if lpValue == 0 (null pointer)
  244. lpValue == pointer to Null string
  245. lpValue == pointer to "Global" string
  246. QUERY_FOREIGN
  247. if lpValue == pointer to "Foriegn" string
  248. QUERY_COSTLY
  249. if lpValue == pointer to "Costly" string
  250. otherwise:
  251. QUERY_ITEMS
  252. --*/
  253. {
  254. WCHAR *pwcArgChar, *pwcTypeChar;
  255. BOOL bFound;
  256. if (lpValue == 0) {
  257. return QUERY_GLOBAL;
  258. } else if (*lpValue == 0) {
  259. return QUERY_GLOBAL;
  260. }
  261. // check for "Global" request
  262. pwcArgChar = lpValue;
  263. pwcTypeChar = (LPWSTR)GLOBAL_STRING;
  264. bFound = TRUE; // assume found until contradicted
  265. // check to the length of the shortest string
  266. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  267. if (*pwcArgChar++ != *pwcTypeChar++) {
  268. bFound = FALSE; // no match
  269. break; // bail out now
  270. }
  271. }
  272. if (bFound) return QUERY_GLOBAL;
  273. // check for "Foreign" request
  274. pwcArgChar = lpValue;
  275. pwcTypeChar = (LPWSTR)FOREIGN_STRING;
  276. bFound = TRUE; // assume found until contradicted
  277. // check to the length of the shortest string
  278. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  279. if (*pwcArgChar++ != *pwcTypeChar++) {
  280. bFound = FALSE; // no match
  281. break; // bail out now
  282. }
  283. }
  284. if (bFound) return QUERY_FOREIGN;
  285. // check for "Costly" request
  286. pwcArgChar = lpValue;
  287. pwcTypeChar = (LPWSTR)COSTLY_STRING;
  288. bFound = TRUE; // assume found until contradicted
  289. // check to the length of the shortest string
  290. while ((*pwcArgChar != 0) && (*pwcTypeChar != 0)) {
  291. if (*pwcArgChar++ != *pwcTypeChar++) {
  292. bFound = FALSE; // no match
  293. break; // bail out now
  294. }
  295. }
  296. if (bFound) return QUERY_COSTLY;
  297. // if not Global and not Foreign and not Costly,
  298. // then it must be an item list
  299. return QUERY_ITEMS;
  300. }
  301. BOOL
  302. IsNumberInUnicodeList (
  303. IN DWORD dwNumber,
  304. IN LPWSTR lpwszUnicodeList
  305. )
  306. /*++
  307. IsNumberInUnicodeList
  308. Arguments:
  309. IN dwNumber
  310. DWORD number to find in list
  311. IN lpwszUnicodeList
  312. Null terminated, Space delimited list of decimal numbers
  313. Return Value:
  314. TRUE:
  315. dwNumber was found in the list of unicode number strings
  316. FALSE:
  317. dwNumber was not found in the list.
  318. --*/
  319. {
  320. DWORD dwThisNumber;
  321. WCHAR *pwcThisChar;
  322. BOOL bValidNumber;
  323. BOOL bNewItem;
  324. WCHAR wcDelimiter; // could be an argument to be more flexible
  325. BOOL bNotDone;
  326. BOOL bReturnValue = FALSE;
  327. if (lpwszUnicodeList == 0) return FALSE; // null pointer, # not founde
  328. pwcThisChar = lpwszUnicodeList;
  329. dwThisNumber = 0;
  330. wcDelimiter = (WCHAR)' ';
  331. bValidNumber = FALSE;
  332. bNewItem = TRUE;
  333. bNotDone = TRUE;
  334. while ( bNotDone ) {
  335. switch (EvalThisChar (*pwcThisChar, wcDelimiter)) {
  336. case DIGIT:
  337. // if this is the first digit after a delimiter, then
  338. // set flags to start computing the new number
  339. if (bNewItem) {
  340. bNewItem = FALSE;
  341. bValidNumber = TRUE;
  342. }
  343. if (bValidNumber) {
  344. dwThisNumber *= 10;
  345. dwThisNumber += (*pwcThisChar - (WCHAR)'0');
  346. }
  347. break;
  348. case DELIMITER:
  349. // a delimter is either the delimiter character or the
  350. // end of the string ('\0') if when the delimiter has been
  351. // reached a valid number was found, then compare it to the
  352. // number from the argument list. if this is the end of the
  353. // string and no match was found, then return.
  354. //
  355. if (bValidNumber) {
  356. if (dwThisNumber == dwNumber) {
  357. bNotDone = FALSE;
  358. bReturnValue = TRUE;
  359. } else {
  360. bValidNumber = FALSE;
  361. }
  362. }
  363. if (*pwcThisChar == 0) {
  364. bNotDone = FALSE;
  365. } else {
  366. bNewItem = TRUE;
  367. dwThisNumber = 0;
  368. }
  369. break;
  370. case INVALID:
  371. // if an invalid character was encountered, ignore all
  372. // characters up to the next delimiter and then start fresh.
  373. // the invalid number is not compared.
  374. bValidNumber = FALSE;
  375. break;
  376. default:
  377. break;
  378. }
  379. pwcThisChar++;
  380. }
  381. return bReturnValue;
  382. } // IsNumberInUnicodeList
  383. BOOL
  384. MonBuildInstanceDefinition(
  385. PERF_INSTANCE_DEFINITION *pBuffer,
  386. PVOID *pBufferNext,
  387. DWORD ParentObjectTitleIndex,
  388. DWORD ParentObjectInstance,
  389. DWORD UniqueID,
  390. LPWSTR Name
  391. )
  392. /*++
  393. MonBuildInstanceDefinition - Build an instance of an object
  394. Inputs:
  395. pBuffer - pointer to buffer where instance is to
  396. be constructed
  397. pBufferNext - pointer to a pointer which will contain
  398. next available location, DWORD aligned
  399. ParentObjectTitleIndex
  400. - Title Index of parent object type; 0 if
  401. no parent object
  402. ParentObjectInstance
  403. - Index into instances of parent object
  404. type, starting at 0, for this instances
  405. parent object instance
  406. UniqueID - a unique identifier which should be used
  407. instead of the Name for identifying
  408. this instance
  409. Name - Name of this instance
  410. --*/
  411. {
  412. DWORD NameLength;
  413. LPWSTR pName;
  414. //
  415. // Include trailing null in name size
  416. //
  417. NameLength = (lstrlenW(Name) + 1) * sizeof(WCHAR);
  418. pBuffer->ByteLength = sizeof(PERF_INSTANCE_DEFINITION) +
  419. NameLength;
  420. pBuffer->ParentObjectTitleIndex = ParentObjectTitleIndex;
  421. pBuffer->ParentObjectInstance = ParentObjectInstance;
  422. pBuffer->UniqueID = UniqueID;
  423. pBuffer->NameOffset = sizeof(PERF_INSTANCE_DEFINITION);
  424. pBuffer->NameLength = NameLength;
  425. // copy name to name buffer
  426. pName = (LPWSTR)&pBuffer[1];
  427. memcpy(pName,Name,NameLength);
  428. // update "next byte" pointer
  429. *pBufferNext = (PVOID) ((PCHAR) pBuffer + pBuffer->ByteLength);
  430. // round up to put next buffer on a QUADWORD boundry
  431. *pBufferNext = ALIGN_ON_QWORD (*pBufferNext);
  432. // adjust length value to match new length
  433. pBuffer->ByteLength = (ULONG)((ULONG_PTR)*pBufferNext - (ULONG_PTR)pBuffer);
  434. return 0;
  435. }