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.

1326 lines
37 KiB

  1. /*++
  2. Copyright (c) 1996 Microsoft Corporation
  3. Module Name:
  4. perfsprc.c
  5. Abstract:
  6. Author:
  7. Bob Watson (a-robw) Aug 95
  8. Revision History:
  9. --*/
  10. #include <nt.h>
  11. #include <ntrtl.h>
  12. #include <nturtl.h>
  13. #include <windows.h>
  14. #include <wchar.h>
  15. #include <winperf.h>
  16. #include <ntprfctr.h>
  17. #include <perfutil.h>
  18. #include "perfsprc.h"
  19. #include "procmsg.h"
  20. #include "dataheap.h"
  21. // bit field definitions for collect function flags
  22. #define POS_READ_SYS_PROCESS_DATA ((DWORD)0x00010000)
  23. #define POS_READ_PROCESS_VM_DATA ((DWORD)0x00020000)
  24. #define POS_READ_JOB_OBJECT_DATA ((DWORD)0x00040000)
  25. #define POS_READ_JOB_DETAIL_DATA ((DWORD)0x00080000)
  26. #define POS_READ_HEAP_DATA ((DWORD)0x00100000)
  27. #define POS_COLLECT_PROCESS_DATA ((DWORD)0x00010001)
  28. #define POS_COLLECT_THREAD_DATA ((DWORD)0x00010003)
  29. #define POS_COLLECT_EXPROCESS_DATA ((DWORD)0x00030004)
  30. #define POS_COLLECT_IMAGE_DATA ((DWORD)0x0003000C)
  31. #define POS_COLLECT_LONG_IMAGE_DATA ((DWORD)0x00030014)
  32. #define POS_COLLECT_THREAD_DETAILS_DATA ((DWORD)0x00030024)
  33. #define POS_COLLECT_JOB_OBJECT_DATA ((DWORD)0x00050040)
  34. #define POS_COLLECT_JOB_DETAIL_DATA ((DWORD)0x000D00C1)
  35. #define POS_COLLECT_HEAP_DATA ((DWORD)0x00110101)
  36. #define POS_COLLECT_FUNCTION_MASK ((DWORD)0x000001FF)
  37. #define POS_COLLECT_GLOBAL_DATA ((DWORD)0x001501C3)
  38. #define POS_COLLECT_GLOBAL_NO_HEAP ((DWORD)0x000500C3)
  39. #define POS_COLLECT_FOREIGN_DATA ((DWORD)0)
  40. #define POS_COLLECT_COSTLY_DATA ((DWORD)0x0003003C)
  41. // global variables to this DLL
  42. HANDLE ThisDLLHandle = NULL;
  43. HANDLE hEventLog = NULL;
  44. LPWSTR wszTotal = NULL;
  45. HANDLE hLibHeap = NULL;
  46. LPBYTE pProcessBuffer = NULL;
  47. PPROCESS_VA_INFO pProcessVaInfo = NULL;
  48. PUNICODE_STRING pusLocalProcessNameBuffer = NULL;
  49. LARGE_INTEGER PerfTime = {0,0};
  50. const WCHAR IDLE_PROCESS[] = L"Idle";
  51. const WCHAR SYSTEM_PROCESS[] = L"System";
  52. const WCHAR szPerflibSubKey[] = L"\\Registry\\Machine\\SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib";
  53. const WCHAR szPerfProcSubKey[] = L"\\Registry\\Machine\\SYSTEM\\CurrentControlSet\\Services\\PerfProc\\Performance";
  54. const WCHAR szDisplayHeapPerfObject[] = L"DisplayHeapPerfObject";
  55. const WCHAR szProcessNameFormat[] = L"ProcessNameFormat";
  56. const WCHAR szThreadNameFormat[] = L"ThreadNameFormat";
  57. BOOL PerfSprc_DisplayHeapPerfObject = FALSE;
  58. DWORD PerfSprc_dwProcessNameFormat = NAME_FORMAT_DEFAULT;
  59. DWORD PerfSprc_dwThreadNameFormat = NAME_FORMAT_DEFAULT;
  60. extern DWORD bOpenJobErrorLogged;
  61. //
  62. // Value to decide if process names should be collected from:
  63. // the SystemProcessInfo structure (fastest)
  64. // -- or --
  65. // the process's image file (slower, but shows Unicode filenames)
  66. //
  67. LONG lProcessNameCollectionMethod = PNCM_NOT_DEFINED;
  68. // variables local to this module
  69. static POS_FUNCTION_INFO posDataFuncInfo[] = {
  70. {PROCESS_OBJECT_TITLE_INDEX, POS_COLLECT_PROCESS_DATA, 0, CollectProcessObjectData},
  71. {THREAD_OBJECT_TITLE_INDEX, POS_COLLECT_THREAD_DATA, 0, CollectThreadObjectData},
  72. {EXPROCESS_OBJECT_TITLE_INDEX, POS_COLLECT_EXPROCESS_DATA, 0, CollectExProcessObjectData},
  73. {IMAGE_OBJECT_TITLE_INDEX, POS_COLLECT_IMAGE_DATA, 0, CollectImageObjectData},
  74. {LONG_IMAGE_OBJECT_TITLE_INDEX, POS_COLLECT_LONG_IMAGE_DATA,0, CollectLongImageObjectData},
  75. {THREAD_DETAILS_OBJECT_TITLE_INDEX, POS_COLLECT_THREAD_DETAILS_DATA, 0, CollectThreadDetailsObjectData},
  76. {JOB_OBJECT_TITLE_INDEX, POS_COLLECT_JOB_OBJECT_DATA, 0, CollectJobObjectData},
  77. {JOB_DETAILS_OBJECT_TITLE_INDEX, POS_COLLECT_JOB_DETAIL_DATA, 0, CollectJobDetailData},
  78. {HEAP_OBJECT_TITLE_INDEX, POS_COLLECT_HEAP_DATA, 0, CollectHeapObjectData}
  79. };
  80. #define POS_NUM_FUNCS (sizeof(posDataFuncInfo) / sizeof(posDataFuncInfo[1]))
  81. static BOOL bInitOk = FALSE;
  82. static DWORD dwOpenCount = 0;
  83. static DWORD ProcessBufSize = LARGE_BUFFER_SIZE;
  84. PM_OPEN_PROC OpenSysProcessObject;
  85. PM_COLLECT_PROC CollecSysProcessObjectData;
  86. PM_CLOSE_PROC CloseSysProcessObject;
  87. __inline
  88. VOID
  89. PerfpQuerySystemTime(
  90. IN PLARGE_INTEGER SystemTime
  91. )
  92. {
  93. do {
  94. SystemTime->HighPart = USER_SHARED_DATA->SystemTime.High1Time;
  95. SystemTime->LowPart = USER_SHARED_DATA->SystemTime.LowPart;
  96. } while (SystemTime->HighPart != USER_SHARED_DATA->SystemTime.High2Time);
  97. }
  98. BOOL
  99. GetProcessExeName(
  100. HANDLE hProcessID,
  101. PUNICODE_STRING pusName
  102. )
  103. {
  104. HANDLE hProcess;
  105. OBJECT_ATTRIBUTES obProcess;
  106. CLIENT_ID ClientId;
  107. PROCESS_BASIC_INFORMATION BasicInfo;
  108. NTSTATUS Status;
  109. PPEB Peb;
  110. PPEB_LDR_DATA Ldr;
  111. PLIST_ENTRY LdrHead;
  112. PLIST_ENTRY LdrNext;
  113. PLDR_DATA_TABLE_ENTRY LdrEntry;
  114. LDR_DATA_TABLE_ENTRY LdrEntryData;
  115. BOOL bReturn;
  116. WCHAR wszDllName[MAX_PATH];
  117. // open process for reading
  118. // get handle to process
  119. ClientId.UniqueThread = (HANDLE)NULL;
  120. ClientId.UniqueProcess = hProcessID;
  121. InitializeObjectAttributes(
  122. &obProcess,
  123. NULL,
  124. 0,
  125. NULL,
  126. NULL
  127. );
  128. Status = NtOpenProcess(
  129. &hProcess,
  130. (ACCESS_MASK)PROCESS_ALL_ACCESS,
  131. &obProcess,
  132. &ClientId);
  133. if (! NT_SUCCESS(Status)){
  134. // unable to open the process,
  135. return FALSE;
  136. }
  137. // Get the process information
  138. Status = NtQueryInformationProcess(
  139. hProcess,
  140. ProcessBasicInformation,
  141. &BasicInfo,
  142. sizeof(BasicInfo),
  143. NULL
  144. );
  145. if ( !NT_SUCCESS(Status) ) {
  146. SetLastError( RtlNtStatusToDosError( Status ) );
  147. bReturn = FALSE;
  148. } else {
  149. Peb = BasicInfo.PebBaseAddress;
  150. //
  151. // get the loader information block
  152. //
  153. // Ldr = Peb->Ldr
  154. //
  155. if (!ReadProcessMemory(hProcess, &Peb->Ldr, &Ldr, sizeof(Ldr), NULL)) {
  156. // unable to read loader information
  157. bReturn = FALSE;
  158. } else {
  159. LdrHead = &Ldr->InMemoryOrderModuleList;
  160. //
  161. // get the first memory block listed. this is the .EXE in NT
  162. //
  163. if (!ReadProcessMemory(hProcess, &LdrHead->Flink, &LdrNext, sizeof(LdrNext), NULL)) {
  164. // unable to read memory link
  165. bReturn = FALSE;
  166. } else {
  167. LdrEntry = CONTAINING_RECORD(LdrNext, LDR_DATA_TABLE_ENTRY, InMemoryOrderLinks);
  168. if (!ReadProcessMemory(hProcess, LdrEntry, &LdrEntryData, sizeof(LdrEntryData), NULL)) {
  169. // unable to read image header
  170. bReturn = FALSE;
  171. } else {
  172. if (!ReadProcessMemory(hProcess,
  173. LdrEntryData.BaseDllName.Buffer,
  174. (LPVOID)&wszDllName[0],
  175. sizeof(wszDllName), NULL)) {
  176. // unable to read DLL buffer
  177. bReturn = FALSE;
  178. } else {
  179. // copy the short name to the caller's buffer
  180. RtlInitUnicodeString (
  181. pusName,
  182. wszDllName);
  183. SetLastError(ERROR_SUCCESS);
  184. }
  185. }
  186. }
  187. }
  188. NtClose (hProcess);
  189. }
  190. return TRUE;
  191. }
  192. LONG
  193. GetProcessNameColMeth (
  194. VOID
  195. )
  196. {
  197. NTSTATUS Status;
  198. HANDLE hPerflibKey;
  199. OBJECT_ATTRIBUTES oaPerflibKey;
  200. UNICODE_STRING PerflibSubKeyString;
  201. UNICODE_STRING NameInfoValueString;
  202. LONG lReturn = PNCM_SYSTEM_INFO;
  203. PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
  204. DWORD dwBufLen;
  205. DWORD dwRetBufLen;
  206. PDWORD pdwValue;
  207. RtlInitUnicodeString (
  208. &PerflibSubKeyString,
  209. szPerflibSubKey);
  210. InitializeObjectAttributes(
  211. &oaPerflibKey,
  212. &PerflibSubKeyString,
  213. OBJ_CASE_INSENSITIVE,
  214. NULL,
  215. NULL
  216. );
  217. Status = NtOpenKey(
  218. &hPerflibKey,
  219. MAXIMUM_ALLOWED,
  220. &oaPerflibKey
  221. );
  222. if (NT_SUCCESS (Status)) {
  223. // registry key opened, now read value.
  224. // allocate enough room for the structure, - the last
  225. // UCHAR in the struct, but + the data buffer (a dword)
  226. dwBufLen = sizeof(KEY_VALUE_PARTIAL_INFORMATION) -
  227. sizeof(UCHAR) + sizeof (DWORD);
  228. pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ALLOCMEM (
  229. hLibHeap,
  230. HEAP_ZERO_MEMORY,
  231. dwBufLen);
  232. if (pKeyInfo != NULL) {
  233. // initialize value name string
  234. RtlInitUnicodeString (
  235. &NameInfoValueString,
  236. (LPCWSTR)L"CollectUnicodeProcessNames");
  237. dwRetBufLen = 0;
  238. Status = NtQueryValueKey (
  239. hPerflibKey,
  240. &NameInfoValueString,
  241. KeyValuePartialInformation,
  242. (PVOID)pKeyInfo,
  243. dwBufLen,
  244. &dwRetBufLen);
  245. if (NT_SUCCESS(Status)) {
  246. // check value of return data buffer
  247. pdwValue = (PDWORD)&pKeyInfo->Data[0];
  248. if (*pdwValue == PNCM_MODULE_FILE) {
  249. lReturn = PNCM_MODULE_FILE;
  250. } else {
  251. // all other values will cause this routine to return
  252. // the default value of PNCM_SYSTEM_INFO;
  253. }
  254. }
  255. FREEMEM (hLibHeap, 0, pKeyInfo);
  256. }
  257. // close handle
  258. NtClose (hPerflibKey);
  259. }
  260. return lReturn;
  261. }
  262. VOID
  263. PerfProcGlobalSettings (
  264. VOID
  265. )
  266. {
  267. NTSTATUS Status;
  268. HANDLE hPerfProcKey;
  269. OBJECT_ATTRIBUTES oaPerfProcKey;
  270. UNICODE_STRING PerfProcSubKeyString;
  271. UNICODE_STRING NameInfoValueString;
  272. PKEY_VALUE_PARTIAL_INFORMATION pKeyInfo;
  273. DWORD dwBufLen;
  274. DWORD dwRetBufLen;
  275. PDWORD pdwValue;
  276. PerfpQuerySystemTime(&PerfTime);
  277. RtlInitUnicodeString (
  278. &PerfProcSubKeyString,
  279. szPerfProcSubKey);
  280. InitializeObjectAttributes(
  281. &oaPerfProcKey,
  282. &PerfProcSubKeyString,
  283. OBJ_CASE_INSENSITIVE,
  284. NULL,
  285. NULL
  286. );
  287. Status = NtOpenKey(
  288. &hPerfProcKey,
  289. MAXIMUM_ALLOWED,
  290. &oaPerfProcKey
  291. );
  292. if (NT_SUCCESS (Status)) {
  293. // registry key opened, now read value.
  294. // allocate enough room for the structure, - the last
  295. // UCHAR in the struct, but + the data buffer (a dword)
  296. dwBufLen = sizeof(KEY_VALUE_PARTIAL_INFORMATION) -
  297. sizeof(UCHAR) + sizeof (DWORD);
  298. pKeyInfo = (PKEY_VALUE_PARTIAL_INFORMATION)ALLOCMEM (
  299. hLibHeap,
  300. HEAP_ZERO_MEMORY,
  301. dwBufLen);
  302. if (pKeyInfo != NULL) {
  303. // initialize value name string
  304. RtlInitUnicodeString (
  305. &NameInfoValueString,
  306. szDisplayHeapPerfObject);
  307. dwRetBufLen = 0;
  308. Status = NtQueryValueKey (
  309. hPerfProcKey,
  310. &NameInfoValueString,
  311. KeyValuePartialInformation,
  312. (PVOID)pKeyInfo,
  313. dwBufLen,
  314. &dwRetBufLen);
  315. if (NT_SUCCESS(Status)) {
  316. // check value of return data buffer
  317. pdwValue = (PDWORD)&pKeyInfo->Data[0];
  318. if (*pdwValue == 1) {
  319. PerfSprc_DisplayHeapPerfObject = TRUE;
  320. } else {
  321. // all other values will cause this routine to return
  322. // the default value of FALSE
  323. }
  324. }
  325. RtlInitUnicodeString(
  326. &NameInfoValueString,
  327. szProcessNameFormat);
  328. dwRetBufLen = 0;
  329. Status = NtQueryValueKey(
  330. hPerfProcKey,
  331. &NameInfoValueString,
  332. KeyValuePartialInformation,
  333. (PVOID)pKeyInfo,
  334. dwBufLen,
  335. &dwRetBufLen);
  336. if (NT_SUCCESS(Status)) {
  337. pdwValue = (PDWORD) &pKeyInfo->Data[0];
  338. PerfSprc_dwProcessNameFormat = *pdwValue;
  339. }
  340. RtlInitUnicodeString(
  341. &NameInfoValueString,
  342. szThreadNameFormat);
  343. dwRetBufLen = 0;
  344. Status = NtQueryValueKey(
  345. hPerfProcKey,
  346. &NameInfoValueString,
  347. KeyValuePartialInformation,
  348. (PVOID)pKeyInfo,
  349. dwBufLen,
  350. &dwRetBufLen);
  351. if (NT_SUCCESS(Status)) {
  352. pdwValue = (PDWORD) &pKeyInfo->Data[0];
  353. PerfSprc_dwThreadNameFormat = *pdwValue;
  354. }
  355. FREEMEM (hLibHeap, 0, pKeyInfo);
  356. }
  357. // close handle
  358. NtClose (hPerfProcKey);
  359. }
  360. if ((PerfSprc_dwProcessNameFormat < NAME_FORMAT_BLANK) ||
  361. (PerfSprc_dwProcessNameFormat > NAME_FORMAT_ID))
  362. PerfSprc_dwProcessNameFormat = NAME_FORMAT_DEFAULT;
  363. if ((PerfSprc_dwThreadNameFormat < NAME_FORMAT_BLANK) ||
  364. (PerfSprc_dwThreadNameFormat > NAME_FORMAT_ID))
  365. PerfSprc_dwThreadNameFormat = NAME_FORMAT_DEFAULT;
  366. }
  367. BOOL
  368. DllProcessAttach (
  369. IN HANDLE DllHandle
  370. )
  371. /*++
  372. Description:
  373. perform any initialization function that apply to all object
  374. modules
  375. --*/
  376. {
  377. BOOL bReturn = TRUE;
  378. WCHAR wszTempBuffer[512];
  379. LONG lStatus;
  380. DWORD dwBufferSize;
  381. UNREFERENCED_PARAMETER (DllHandle);
  382. // open handle to the event log
  383. if (hEventLog == NULL) {
  384. hEventLog = MonOpenEventLog((LPWSTR)L"PerfProc");
  385. // create the local heap
  386. hLibHeap = HeapCreate (0, 1, 0);
  387. if (hLibHeap == NULL) {
  388. return FALSE;
  389. }
  390. if (lProcessNameCollectionMethod == PNCM_NOT_DEFINED) {
  391. // get desired process name collection method as defined in the
  392. // registry
  393. lProcessNameCollectionMethod = GetProcessNameColMeth ();
  394. }
  395. }
  396. lStatus = GetPerflibKeyValue (
  397. szTotalValue,
  398. REG_SZ,
  399. sizeof(wszTempBuffer),
  400. (LPVOID)&wszTempBuffer[0],
  401. DEFAULT_TOTAL_STRING_LEN,
  402. (LPVOID)&szDefaultTotalString[0]);
  403. if (lStatus == ERROR_SUCCESS) {
  404. // then a string was returned in the temp buffer
  405. dwBufferSize = lstrlenW (wszTempBuffer) + 1;
  406. dwBufferSize *= sizeof (WCHAR);
  407. wszTotal = ALLOCMEM (hLibHeap, HEAP_ZERO_MEMORY, dwBufferSize);
  408. if (wszTotal == NULL) {
  409. // unable to allocate buffer so use static buffer
  410. wszTotal = (LPWSTR)&szDefaultTotalString[0];
  411. } else {
  412. memcpy (wszTotal, wszTempBuffer, dwBufferSize);
  413. }
  414. } else {
  415. // unable to get string from registry so just use static buffer
  416. wszTotal = (LPWSTR)&szDefaultTotalString[0];
  417. }
  418. return bReturn;
  419. }
  420. BOOL
  421. DllProcessDetach (
  422. IN HANDLE DllHandle
  423. )
  424. {
  425. UNREFERENCED_PARAMETER (DllHandle);
  426. if (dwOpenCount > 0) {
  427. // the Library is being unloaded before it was
  428. // closed so close it now as this is the last
  429. // chance to do it before the library is tossed.
  430. // if the value of dwOpenCount is > 1, set it to
  431. // one to insure everything will be closed when
  432. // the close function is called.
  433. if (dwOpenCount > 1) dwOpenCount = 1;
  434. CloseSysProcessObject();
  435. }
  436. if ((wszTotal != NULL) && (wszTotal != &szDefaultTotalString[0])) {
  437. FREEMEM (hLibHeap, 0, wszTotal);
  438. wszTotal = NULL;
  439. }
  440. if (HeapDestroy (hLibHeap)) hLibHeap = NULL;
  441. if (hEventLog != NULL) {
  442. MonCloseEventLog ();
  443. }
  444. return TRUE;
  445. }
  446. BOOL
  447. __stdcall
  448. DllInit(
  449. IN HANDLE DLLHandle,
  450. IN DWORD Reason,
  451. IN LPVOID ReservedAndUnused
  452. )
  453. {
  454. ReservedAndUnused;
  455. // this will prevent the DLL from getting
  456. // the DLL_THREAD_* messages
  457. DisableThreadLibraryCalls (DLLHandle);
  458. switch(Reason) {
  459. case DLL_PROCESS_ATTACH:
  460. return DllProcessAttach (DLLHandle);
  461. case DLL_PROCESS_DETACH:
  462. return DllProcessDetach (DLLHandle);
  463. case DLL_THREAD_ATTACH:
  464. case DLL_THREAD_DETACH:
  465. default:
  466. return TRUE;
  467. }
  468. }
  469. PUNICODE_STRING
  470. GetProcessSlowName (
  471. PSYSTEM_PROCESS_INFORMATION pProcess
  472. )
  473. /*++
  474. GetProcessSlowName
  475. Inputs:
  476. PSYSTEM_PROCESS_INFORMATION pProcess
  477. address of System Process Information data structure.
  478. Outputs:
  479. None
  480. Returns:
  481. Pointer to an initialized Unicode string (created by this routine)
  482. that contains the short name of the process image or a numeric ID
  483. if no name is found.
  484. If unable to allocate memory for structure, then NULL is returned.
  485. --*/
  486. {
  487. PWCHAR pPeriod;
  488. PWCHAR pThisChar;
  489. WORD wStringSize;
  490. WORD wThisChar;
  491. WORD wLength;
  492. // this routine assumes that the allocated memory has been zero'd
  493. if (pusLocalProcessNameBuffer == NULL) {
  494. // allocate Unicode String Structure and adjacent buffer first
  495. wLength = MAX_INSTANCE_NAME * sizeof(WCHAR);
  496. if (pProcess->ImageName.Length > 0) {
  497. if (wLength < pProcess->ImageName.Length) {
  498. wLength = pProcess->ImageName.Length;
  499. }
  500. }
  501. wStringSize = sizeof(UNICODE_STRING) + wLength + 64 + (WORD) sizeof(UNICODE_NULL);
  502. pusLocalProcessNameBuffer =
  503. ALLOCMEM (hLibHeap,
  504. HEAP_ZERO_MEMORY, // this will only 0 the buffer the first time!
  505. (DWORD)wStringSize);
  506. if (pusLocalProcessNameBuffer == NULL) {
  507. return NULL;
  508. } else {
  509. pusLocalProcessNameBuffer->MaximumLength = (WORD)(wStringSize - (WORD)(sizeof (UNICODE_STRING)));
  510. }
  511. }
  512. else {
  513. wStringSize = pusLocalProcessNameBuffer->MaximumLength;
  514. }
  515. pusLocalProcessNameBuffer->Length = 0;
  516. pusLocalProcessNameBuffer->Buffer = (PWCHAR)&pusLocalProcessNameBuffer[1];
  517. memset ( // buffer must be zero'd so we'll have a NULL Term
  518. pusLocalProcessNameBuffer->Buffer, 0,
  519. (DWORD)pusLocalProcessNameBuffer->MaximumLength);
  520. // get the process name from the image file
  521. GetProcessExeName (pProcess->UniqueProcessId, pusLocalProcessNameBuffer);
  522. if (pusLocalProcessNameBuffer->Length > 0) { // some name has been defined
  523. pPeriod = (PWCHAR)pusLocalProcessNameBuffer->Buffer;
  524. pThisChar = (PWCHAR)pusLocalProcessNameBuffer->Buffer;
  525. wThisChar = 0;
  526. //
  527. // go from beginning to end and find last backslash and
  528. // last period in name
  529. //
  530. while (*pThisChar != 0) { // go until null
  531. if (*pThisChar == L'.') {
  532. pPeriod = pThisChar;
  533. }
  534. pThisChar++; // point to next char
  535. wThisChar += sizeof(WCHAR);
  536. if (wThisChar >= pusLocalProcessNameBuffer->Length) {
  537. break;
  538. }
  539. }
  540. // if pPeriod is still pointing to the beginning of the
  541. // string, then no period was found
  542. if (pPeriod == (PWCHAR)pusLocalProcessNameBuffer->Buffer) {
  543. pPeriod = pThisChar; // set to end of string;
  544. } else {
  545. // if a period was found, then see if the extension is
  546. // .EXE, if so leave it, if not, then use end of string
  547. // (i.e. include extension in name)
  548. if (lstrcmpiW(pPeriod, (LPCWSTR)L".EXE") != 0) {
  549. pPeriod = pThisChar;
  550. }
  551. }
  552. // copy characters between period (or end of string) and
  553. // slash (or start of string) to make image name
  554. wStringSize = (WORD)((PCHAR)pPeriod - (PCHAR)pusLocalProcessNameBuffer->Buffer);
  555. wLength = pusLocalProcessNameBuffer->MaximumLength - sizeof(UNICODE_NULL);
  556. if (wStringSize >= wLength) {
  557. wStringSize = wLength;
  558. }
  559. *pPeriod = 0; // null terminate buffer
  560. if ((PerfSprc_dwProcessNameFormat == NAME_FORMAT_ID) &&
  561. (wStringSize < (wLength - 10))) {
  562. ULONG Length;
  563. Length = PerfIntegerToWString(
  564. HandleToUlong(pProcess->UniqueProcessId),
  565. 10,
  566. (pusLocalProcessNameBuffer->MaximumLength - wStringSize)
  567. / sizeof(WCHAR),
  568. pPeriod+1);
  569. if (Length > 0)
  570. *pPeriod = L'_';
  571. wStringSize += (WORD) (Length * sizeof(WCHAR));
  572. }
  573. pusLocalProcessNameBuffer->Length = wStringSize; // adjust length
  574. } else { // no name defined so use Process #
  575. // check to see if this is a system process and give it
  576. // a name
  577. switch (HandleToUlong(pProcess->UniqueProcessId)) {
  578. case IDLE_PROCESS_ID:
  579. RtlAppendUnicodeToString (pusLocalProcessNameBuffer,
  580. (LPWSTR)IDLE_PROCESS);
  581. break;
  582. case SYSTEM_PROCESS_ID:
  583. RtlAppendUnicodeToString (pusLocalProcessNameBuffer,
  584. (LPWSTR)SYSTEM_PROCESS);
  585. break;
  586. // if the id is not a system process, then use the id as the name
  587. default:
  588. // try accessing via the "regular" interface
  589. return (GetProcessShortName (pProcess));
  590. break;
  591. }
  592. }
  593. return pusLocalProcessNameBuffer;
  594. }
  595. PUNICODE_STRING
  596. GetProcessShortName (
  597. PSYSTEM_PROCESS_INFORMATION pProcess
  598. )
  599. /*++
  600. GetProcessShortName
  601. Inputs:
  602. PSYSTEM_PROCESS_INFORMATION pProcess
  603. address of System Process Information data structure.
  604. Outputs:
  605. None
  606. Returns:
  607. Pointer to an initialized Unicode string (created by this routine)
  608. that contains the short name of the process image or a numeric ID
  609. if no name is found.
  610. If unable to allocate memory for structure, then NULL is returned.
  611. --*/
  612. {
  613. PWCHAR pSlash;
  614. PWCHAR pPeriod;
  615. PWCHAR pThisChar;
  616. WORD wStringSize;
  617. WORD wThisChar;
  618. ULONG ProcessId;
  619. WORD wLength;
  620. // this routine assumes that the allocated memory has been zero'd
  621. if (pusLocalProcessNameBuffer == NULL) {
  622. // allocate Unicode String Structure and adjacent buffer first
  623. wLength = MAX_INSTANCE_NAME * sizeof(WCHAR);
  624. if (pProcess->ImageName.Length > 0) {
  625. if (wLength < pProcess->ImageName.Length) {
  626. wLength = pProcess->ImageName.Length;
  627. }
  628. }
  629. wStringSize = sizeof(UNICODE_STRING) + wLength + 64 + (WORD) sizeof(UNICODE_NULL);
  630. pusLocalProcessNameBuffer =
  631. ALLOCMEM (hLibHeap,
  632. HEAP_ZERO_MEMORY, // this will only 0 the buffer the first time!
  633. (DWORD)wStringSize);
  634. if (pusLocalProcessNameBuffer == NULL) {
  635. return NULL;
  636. } else {
  637. pusLocalProcessNameBuffer->MaximumLength = (WORD)(wStringSize - (WORD)sizeof (UNICODE_STRING));
  638. }
  639. }
  640. else {
  641. wStringSize = pusLocalProcessNameBuffer->MaximumLength;
  642. }
  643. pusLocalProcessNameBuffer->Length = 0;
  644. pusLocalProcessNameBuffer->Buffer = (PWCHAR)&pusLocalProcessNameBuffer[1];
  645. memset ( // buffer must be zero'd so we'll have a NULL Term
  646. pusLocalProcessNameBuffer->Buffer, 0,
  647. (DWORD)pusLocalProcessNameBuffer->MaximumLength);
  648. ProcessId = HandleToUlong(pProcess->UniqueProcessId);
  649. if (pProcess->ImageName.Buffer) { // some name has been defined
  650. pSlash = (PWCHAR)pProcess->ImageName.Buffer;
  651. pPeriod = (PWCHAR)pProcess->ImageName.Buffer;
  652. pThisChar = (PWCHAR)pProcess->ImageName.Buffer;
  653. wThisChar = 0;
  654. //
  655. // go from beginning to end and find last backslash and
  656. // last period in name
  657. //
  658. while (*pThisChar != 0) { // go until null
  659. if (*pThisChar == L'\\') {
  660. pSlash = pThisChar;
  661. } else if (*pThisChar == L'.') {
  662. pPeriod = pThisChar;
  663. }
  664. pThisChar++; // point to next char
  665. wThisChar += sizeof(WCHAR);
  666. if (wThisChar >= pProcess->ImageName.Length) {
  667. break;
  668. }
  669. }
  670. // if pPeriod is still pointing to the beginning of the
  671. // string, then no period was found
  672. if (pPeriod == (PWCHAR)pProcess->ImageName.Buffer) {
  673. pPeriod = pThisChar; // set to end of string;
  674. } else {
  675. // if a period was found, then see if the extension is
  676. // .EXE, if so leave it, if not, then use end of string
  677. // (i.e. include extension in name)
  678. if (lstrcmpiW(pPeriod, (LPCWSTR)L".EXE") != 0) {
  679. pPeriod = pThisChar;
  680. }
  681. }
  682. if (*pSlash == L'\\') { // if pSlash is pointing to a slash, then
  683. pSlash++; // point to character next to slash
  684. }
  685. // copy characters between period (or end of string) and
  686. // slash (or start of string) to make image name
  687. wStringSize = (WORD)((PCHAR)pPeriod - (PCHAR)pSlash); // size in bytes
  688. wLength = pusLocalProcessNameBuffer->MaximumLength - sizeof(UNICODE_NULL);
  689. if (wStringSize >= wLength) {
  690. wStringSize = wLength;
  691. }
  692. memcpy (pusLocalProcessNameBuffer->Buffer, pSlash, wStringSize);
  693. // null terminate is
  694. // not necessary because allocated memory is zero-init'd
  695. pPeriod = (PWCHAR) ((PCHAR) pusLocalProcessNameBuffer->Buffer + wStringSize);
  696. if (PerfSprc_dwProcessNameFormat == NAME_FORMAT_ID) {
  697. ULONG Length;
  698. Length = PerfIntegerToWString(
  699. ProcessId,
  700. 10,
  701. (pusLocalProcessNameBuffer->MaximumLength - wStringSize)
  702. / sizeof(WCHAR),
  703. pPeriod+1);
  704. if (Length > 0)
  705. *pPeriod = L'_';
  706. wStringSize += (WORD) (Length * sizeof(WCHAR));
  707. }
  708. pusLocalProcessNameBuffer->Length = wStringSize;
  709. } else { // no name defined so use Process #
  710. // check to see if this is a system process and give it
  711. // a name
  712. switch (ProcessId) {
  713. case IDLE_PROCESS_ID:
  714. RtlAppendUnicodeToString (pusLocalProcessNameBuffer,
  715. (LPWSTR)IDLE_PROCESS);
  716. break;
  717. case SYSTEM_PROCESS_ID:
  718. RtlAppendUnicodeToString (pusLocalProcessNameBuffer,
  719. (LPWSTR)SYSTEM_PROCESS);
  720. break;
  721. // if the id is not a system process, then use the id as the name
  722. default:
  723. RtlIntegerToUnicodeString (ProcessId,
  724. 10,
  725. pusLocalProcessNameBuffer);
  726. break;
  727. }
  728. }
  729. return pusLocalProcessNameBuffer;
  730. }
  731. #pragma warning (disable : 4706)
  732. DWORD
  733. GetSystemProcessData (
  734. )
  735. {
  736. DWORD dwReturnedBufferSize;
  737. NTSTATUS Status;
  738. DWORD WinError;
  739. PVOID pBuffer;
  740. //
  741. // Get process data from system.
  742. // if bGotProcessInfo is TRUE, that means we have the process
  743. // info. collected earlier when we are checking for costly
  744. // object types.
  745. //
  746. if (pProcessBuffer == NULL) {
  747. // allocate a new block
  748. pProcessBuffer = ALLOCMEM (hLibHeap, HEAP_ZERO_MEMORY,
  749. ProcessBufSize);
  750. if (pProcessBuffer == NULL) {
  751. return ERROR_OUTOFMEMORY;
  752. }
  753. }
  754. PerfpQuerySystemTime(&PerfTime);
  755. while( (Status = NtQuerySystemInformation(
  756. SystemProcessInformation,
  757. pProcessBuffer,
  758. ProcessBufSize,
  759. &dwReturnedBufferSize)) == STATUS_INFO_LENGTH_MISMATCH ) {
  760. // expand buffer & retry
  761. ProcessBufSize += INCREMENT_BUFFER_SIZE;
  762. pBuffer = pProcessBuffer;
  763. if ( !(pProcessBuffer = REALLOCMEM(hLibHeap, 0,
  764. pProcessBuffer,
  765. ProcessBufSize)) ) {
  766. FREEMEM(hLibHeap, 0, pBuffer);
  767. return (ERROR_OUTOFMEMORY);
  768. }
  769. }
  770. if ( !NT_SUCCESS(Status) ) {
  771. // convert to win32 error
  772. WinError = (DWORD)RtlNtStatusToDosError(Status);
  773. }
  774. else {
  775. WinError = ERROR_SUCCESS;
  776. }
  777. return (WinError);
  778. }
  779. #pragma warning (default : 4706)
  780. DWORD APIENTRY
  781. OpenSysProcessObject (
  782. LPWSTR lpDeviceNames
  783. )
  784. /*++
  785. Routine Description:
  786. This routine will initialize the data structures used to pass
  787. data back to the registry
  788. Arguments:
  789. Pointer to object ID of each device to be opened (PerfGen)
  790. Return Value:
  791. None.
  792. --*/
  793. {
  794. DWORD status, dwSize;
  795. HKEY hKey;
  796. UNREFERENCED_PARAMETER (lpDeviceNames);
  797. if (dwOpenCount == 0) {
  798. // clear the job object open error flag
  799. bOpenJobErrorLogged = FALSE;
  800. PerfProcGlobalSettings();
  801. }
  802. dwOpenCount++;
  803. bInitOk = TRUE;
  804. return ERROR_SUCCESS;
  805. }
  806. DWORD APIENTRY
  807. CollectSysProcessObjectData (
  808. IN LPWSTR lpValueName,
  809. IN OUT LPVOID *lppData,
  810. IN OUT LPDWORD lpcbTotalBytes,
  811. IN OUT LPDWORD lpNumObjectTypes
  812. )
  813. /*++
  814. Routine Description:
  815. This routine will return the data for the processor object
  816. Arguments:
  817. IN LPWSTR lpValueName
  818. pointer to a wide character string passed by registry.
  819. IN OUT LPVOID *lppData
  820. IN: pointer to the address of the buffer to receive the completed
  821. PerfDataBlock and subordinate structures. This routine will
  822. append its data to the buffer starting at the point referenced
  823. by *lppData.
  824. OUT: points to the first byte after the data structure added by this
  825. routine. This routine updated the value at lppdata after appending
  826. its data.
  827. IN OUT LPDWORD lpcbTotalBytes
  828. IN: the address of the DWORD that tells the size in bytes of the
  829. buffer referenced by the lppData argument
  830. OUT: the number of bytes added by this routine is writted to the
  831. DWORD pointed to by this argument
  832. IN OUT LPDWORD NumObjectTypes
  833. IN: the address of the DWORD to receive the number of objects added
  834. by this routine
  835. OUT: the number of objects added by this routine is writted to the
  836. DWORD pointed to by this argument
  837. Returns:
  838. 0 if successful, else Win 32 error code of failure
  839. --*/
  840. {
  841. LONG lReturn = ERROR_SUCCESS;
  842. NTSTATUS status;
  843. // build bit mask of functions to call
  844. DWORD dwQueryType;
  845. DWORD FunctionCallMask = 0;
  846. DWORD FunctionIndex;
  847. DWORD dwNumObjectsFromFunction;
  848. DWORD dwOrigBuffSize;
  849. DWORD dwByteSize;
  850. if (!bInitOk) {
  851. ReportEvent (hEventLog,
  852. EVENTLOG_ERROR_TYPE,
  853. 0,
  854. PERFPROC_NOT_OPEN,
  855. NULL,
  856. 0,
  857. 0,
  858. NULL,
  859. NULL);
  860. *lpcbTotalBytes = (DWORD) 0;
  861. *lpNumObjectTypes = (DWORD) 0;
  862. lReturn = ERROR_SUCCESS;
  863. goto COLLECT_BAIL_OUT;
  864. }
  865. dwQueryType = GetQueryType (lpValueName);
  866. switch (dwQueryType) {
  867. case QUERY_ITEMS:
  868. for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
  869. if (IsNumberInUnicodeList (
  870. posDataFuncInfo[FunctionIndex].dwObjectId, lpValueName)) {
  871. FunctionCallMask |=
  872. posDataFuncInfo[FunctionIndex].dwCollectFunctionBit;
  873. }
  874. }
  875. break;
  876. case QUERY_GLOBAL:
  877. // only return the HEAP data in a global query if it's enabled
  878. // if they ask for it specifically, then it's OK
  879. if (PerfSprc_DisplayHeapPerfObject) {
  880. FunctionCallMask = POS_COLLECT_GLOBAL_DATA;
  881. } else {
  882. // filter out the heap perf object
  883. FunctionCallMask = POS_COLLECT_GLOBAL_NO_HEAP;
  884. }
  885. break;
  886. case QUERY_FOREIGN:
  887. FunctionCallMask = POS_COLLECT_FOREIGN_DATA;
  888. break;
  889. case QUERY_COSTLY:
  890. FunctionCallMask = POS_COLLECT_COSTLY_DATA;
  891. break;
  892. default:
  893. FunctionCallMask = POS_COLLECT_COSTLY_DATA;
  894. break;
  895. }
  896. // collect data from system
  897. if (FunctionCallMask & POS_READ_SYS_PROCESS_DATA) {
  898. status = GetSystemProcessData ();
  899. if (!NT_SUCCESS(status)) {
  900. ReportEvent (hEventLog,
  901. EVENTLOG_ERROR_TYPE,
  902. 0,
  903. PERFPROC_UNABLE_QUERY_PROCESS_INFO,
  904. NULL,
  905. 0,
  906. sizeof(DWORD),
  907. NULL,
  908. (LPVOID)&status);
  909. }
  910. } else {
  911. status = ERROR_SUCCESS;
  912. }
  913. // collect data from system
  914. if ((status == ERROR_SUCCESS) &&
  915. (pProcessBuffer != NULL) &&
  916. (FunctionCallMask & POS_READ_PROCESS_VM_DATA)) {
  917. pProcessVaInfo = GetSystemVaData (
  918. (PSYSTEM_PROCESS_INFORMATION)pProcessBuffer);
  919. // call function
  920. if (pProcessVaInfo == NULL) {
  921. ReportEvent (hEventLog,
  922. EVENTLOG_ERROR_TYPE,
  923. 0,
  924. PERFPROC_UNABLE_QUERY_VM_INFO,
  925. NULL,
  926. 0,
  927. sizeof(DWORD),
  928. NULL,
  929. (LPVOID)&status);
  930. // zero buffer
  931. }
  932. } else {
  933. // zero buffer
  934. }
  935. // collect data
  936. *lpNumObjectTypes = 0;
  937. dwOrigBuffSize = dwByteSize = *lpcbTotalBytes;
  938. *lpcbTotalBytes = 0;
  939. // remove query bits
  940. FunctionCallMask &= POS_COLLECT_FUNCTION_MASK;
  941. for (FunctionIndex = 0; FunctionIndex < POS_NUM_FUNCS; FunctionIndex++) {
  942. if ((posDataFuncInfo[FunctionIndex].dwCollectFunctionBit & FunctionCallMask) ==
  943. (posDataFuncInfo[FunctionIndex].dwCollectFunctionBit & POS_COLLECT_FUNCTION_MASK)) {
  944. dwNumObjectsFromFunction = 0;
  945. lReturn = (*posDataFuncInfo[FunctionIndex].pCollectFunction) (
  946. lppData,
  947. &dwByteSize,
  948. &dwNumObjectsFromFunction);
  949. if (lReturn == ERROR_SUCCESS) {
  950. *lpNumObjectTypes += dwNumObjectsFromFunction;
  951. *lpcbTotalBytes += dwByteSize;
  952. dwOrigBuffSize -= dwByteSize;
  953. dwByteSize = dwOrigBuffSize;
  954. } else {
  955. break;
  956. }
  957. }
  958. }
  959. // this list of data must be freed after use
  960. if (pProcessVaInfo != NULL) {
  961. FreeSystemVaData (pProcessVaInfo);
  962. pProcessVaInfo = NULL;
  963. }
  964. // *lppData is updated by each function
  965. // *lpcbTotalBytes is updated after each successful function
  966. // *lpNumObjects is updated after each successful function
  967. COLLECT_BAIL_OUT:
  968. return lReturn;
  969. }
  970. DWORD APIENTRY
  971. CloseSysProcessObject (
  972. )
  973. /*++
  974. Routine Description:
  975. This routine closes the open handles to the Signal Gen counters.
  976. Arguments:
  977. None.
  978. Return Value:
  979. ERROR_SUCCESS
  980. --*/
  981. {
  982. DWORD status = ERROR_SUCCESS;
  983. PVOID buffer;
  984. if (--dwOpenCount == 0) {
  985. if (hLibHeap != NULL) {
  986. // close
  987. if (pProcessBuffer != NULL) {
  988. buffer = pProcessBuffer;
  989. pProcessBuffer = NULL;
  990. FREEMEM (hLibHeap, 0, buffer);
  991. }
  992. if (pusLocalProcessNameBuffer != NULL) {
  993. buffer = pusLocalProcessNameBuffer;
  994. pusLocalProcessNameBuffer = NULL;
  995. FREEMEM (hLibHeap, 0, buffer);
  996. }
  997. }
  998. }
  999. return status;
  1000. }
  1001. const CHAR PerfpIntegerWChars[] = {L'0', L'1', L'2', L'3', L'4', L'5',
  1002. L'6', L'7', L'8', L'9', L'A', L'B',
  1003. L'C', L'D', L'E', L'F'};
  1004. ULONG
  1005. PerfIntegerToWString(
  1006. IN ULONG Value,
  1007. IN ULONG Base,
  1008. IN LONG OutputLength,
  1009. OUT LPWSTR String
  1010. )
  1011. /*++
  1012. Routine Description:
  1013. Arguments:
  1014. Return Value:
  1015. --*/
  1016. {
  1017. WCHAR Result[ 33 ], *s;
  1018. ULONG Shift, Mask, Digit, Length;
  1019. Shift = 0;
  1020. switch( Base ) {
  1021. case 16: Shift = 4; break;
  1022. case 8: Shift = 3; break;
  1023. case 2: Shift = 1; break;
  1024. case 0: Base = 10;
  1025. case 10: Shift = 0; break;
  1026. default: Base = 10; Shift = 0; // Default to 10
  1027. }
  1028. if (Shift != 0) {
  1029. Mask = 0xF >> (4 - Shift);
  1030. }
  1031. s = &Result[ 32 ];
  1032. *s = L'\0';
  1033. do {
  1034. if (Shift != 0) {
  1035. Digit = Value & Mask;
  1036. Value >>= Shift;
  1037. }
  1038. else {
  1039. Digit = Value % Base;
  1040. Value = Value / Base;
  1041. }
  1042. *--s = PerfpIntegerWChars[ Digit ];
  1043. } while (Value != 0);
  1044. Length = (ULONG) (&Result[ 32 ] - s);
  1045. if (OutputLength < 0) {
  1046. OutputLength = -OutputLength;
  1047. while ((LONG)Length < OutputLength) {
  1048. *--s = L'0';
  1049. Length++;
  1050. }
  1051. }
  1052. if ((LONG)Length > OutputLength) {
  1053. return( 0 );
  1054. }
  1055. else {
  1056. try {
  1057. RtlMoveMemory( String, s, Length*sizeof(WCHAR) );
  1058. if ((LONG)Length < OutputLength) {
  1059. String[ Length ] = L'\0';
  1060. }
  1061. }
  1062. except( EXCEPTION_EXECUTE_HANDLER ) {
  1063. return( 0 );
  1064. }
  1065. return( Length );
  1066. }
  1067. }