1 /*
2 * PROJECT: ReactOS Task Manager
3 * LICENSE: LGPL-2.1-or-later (https://spdx.org/licenses/LGPL-2.1-or-later)
4 * PURPOSE: Performance Counters
5 * COPYRIGHT: Copyright 1999-2001 Brian Palmer <brianp@reactos.org>
6 * Copyright 2014 Ismael Ferreras Morezuelas <swyterzone+ros@gmail.com>
7 */
8
9 #include "precomp.h"
10
11 #define WIN32_LEAN_AND_MEAN
12 #include <aclapi.h>
13
14 #define NTOS_MODE_USER
15 #include <ndk/psfuncs.h>
16 #include <ndk/exfuncs.h>
17
18 CRITICAL_SECTION PerfDataCriticalSection;
19 PPERFDATA pPerfDataOld = NULL; /* Older perf data (saved to establish delta values) */
20 PPERFDATA pPerfData = NULL; /* Most recent copy of perf data */
21 ULONG ProcessCountOld = 0;
22 ULONG ProcessCount = 0;
23 double dbIdleTime;
24 double dbKernelTime;
25 double dbSystemTime;
26 LARGE_INTEGER liOldIdleTime = {{0,0}};
27 double OldKernelTime = 0;
28 LARGE_INTEGER liOldSystemTime = {{0,0}};
29 SYSTEM_PERFORMANCE_INFORMATION SystemPerfInfo;
30 SYSTEM_BASIC_INFORMATION SystemBasicInfo;
31 SYSTEM_FILECACHE_INFORMATION SystemCacheInfo;
32 ULONG SystemNumberOfHandles;
33 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SystemProcessorTimeInfo = NULL;
34 PSID SystemUserSid = NULL;
35
36 PCMD_LINE_CACHE global_cache = NULL;
37
38 #define CMD_LINE_MIN(a, b) (a < b ? a - sizeof(WCHAR) : b)
39
40 typedef struct _SIDTOUSERNAME
41 {
42 LIST_ENTRY List;
43 LPWSTR pszName;
44 BYTE Data[0];
45 } SIDTOUSERNAME, *PSIDTOUSERNAME;
46
47 static LIST_ENTRY SidToUserNameHead = {&SidToUserNameHead, &SidToUserNameHead};
48
PerfDataInitialize(void)49 BOOL PerfDataInitialize(void)
50 {
51 SID_IDENTIFIER_AUTHORITY NtSidAuthority = {SECURITY_NT_AUTHORITY};
52 NTSTATUS status;
53
54 InitializeCriticalSection(&PerfDataCriticalSection);
55
56 /*
57 * Get number of processors in the system
58 */
59 status = NtQuerySystemInformation(SystemBasicInformation, &SystemBasicInfo, sizeof(SystemBasicInfo), NULL);
60 if (!NT_SUCCESS(status))
61 return FALSE;
62
63 /*
64 * Create the SYSTEM Sid
65 */
66 AllocateAndInitializeSid(&NtSidAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &SystemUserSid);
67 return TRUE;
68 }
69
PerfDataUninitialize(void)70 void PerfDataUninitialize(void)
71 {
72 PLIST_ENTRY pCur;
73 PSIDTOUSERNAME pEntry;
74
75 if (pPerfData != NULL)
76 HeapFree(GetProcessHeap(), 0, pPerfData);
77
78 DeleteCriticalSection(&PerfDataCriticalSection);
79
80 if (SystemUserSid != NULL)
81 {
82 FreeSid(SystemUserSid);
83 SystemUserSid = NULL;
84 }
85
86 /* Free user names cache list */
87 pCur = SidToUserNameHead.Flink;
88 while (pCur != &SidToUserNameHead)
89 {
90 pEntry = CONTAINING_RECORD(pCur, SIDTOUSERNAME, List);
91 pCur = pCur->Flink;
92 HeapFree(GetProcessHeap(), 0, pEntry);
93 }
94
95 if (SystemProcessorTimeInfo) {
96 HeapFree(GetProcessHeap(), 0, SystemProcessorTimeInfo);
97 }
98 }
99
SidToUserName(PSID Sid,LPWSTR szBuffer,DWORD BufferSize)100 static void SidToUserName(PSID Sid, LPWSTR szBuffer, DWORD BufferSize)
101 {
102 static WCHAR szDomainNameUnused[255];
103 DWORD DomainNameLen = _countof(szDomainNameUnused);
104 SID_NAME_USE Use;
105
106 if (Sid != NULL)
107 LookupAccountSidW(NULL, Sid, szBuffer, &BufferSize, szDomainNameUnused, &DomainNameLen, &Use);
108 }
109
110 VOID
111 WINAPI
CachedGetUserFromSid(PSID pSid,LPWSTR pUserName,PULONG pcwcUserName)112 CachedGetUserFromSid(
113 PSID pSid,
114 LPWSTR pUserName,
115 PULONG pcwcUserName)
116 {
117 PLIST_ENTRY pCur;
118 PSIDTOUSERNAME pEntry;
119 ULONG cbSid, cwcUserName;
120
121 cwcUserName = *pcwcUserName;
122
123 /* Walk through the list */
124 for(pCur = SidToUserNameHead.Flink;
125 pCur != &SidToUserNameHead;
126 pCur = pCur->Flink)
127 {
128 pEntry = CONTAINING_RECORD(pCur, SIDTOUSERNAME, List);
129 if (EqualSid((PSID)&pEntry->Data, pSid))
130 {
131 wcsncpy(pUserName, pEntry->pszName, cwcUserName);
132 *pcwcUserName = wcslen(pUserName);
133 return;
134 }
135 }
136
137 /* We didn't find the SID in the list, get the name conventional */
138 SidToUserName(pSid, pUserName, cwcUserName);
139 *pcwcUserName = wcslen(pUserName);
140
141 /* Allocate a new entry */
142 cwcUserName = *pcwcUserName + 1;
143 cbSid = GetLengthSid(pSid);
144 pEntry = HeapAlloc(GetProcessHeap(), 0, sizeof(SIDTOUSERNAME) + cbSid + cwcUserName * sizeof(WCHAR));
145
146 /* Copy the Sid and name to our entry */
147 CopySid(cbSid, (PSID)&pEntry->Data, pSid);
148 pEntry->pszName = (LPWSTR)(pEntry->Data + cbSid);
149 wcsncpy(pEntry->pszName, pUserName, cwcUserName);
150
151 /* Insert the new entry */
152 pEntry->List.Flink = &SidToUserNameHead;
153 pEntry->List.Blink = SidToUserNameHead.Blink;
154 SidToUserNameHead.Blink->Flink = &pEntry->List;
155 SidToUserNameHead.Blink = &pEntry->List;
156 }
157
PerfDataRefresh(void)158 void PerfDataRefresh(void)
159 {
160 ULONG ulSize;
161 NTSTATUS status;
162 LPBYTE pBuffer;
163 ULONG BufferSize;
164 PSYSTEM_PROCESS_INFORMATION pSPI;
165 PPERFDATA pPDOld;
166 ULONG Idx, Idx2;
167 HANDLE hProcess;
168 HANDLE hProcessToken;
169 SYSTEM_PERFORMANCE_INFORMATION SysPerfInfo;
170 SYSTEM_TIMEOFDAY_INFORMATION SysTimeInfo;
171 SYSTEM_FILECACHE_INFORMATION SysCacheInfo;
172 SYSTEM_HANDLE_INFORMATION SysHandleInfoData;
173 PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION SysProcessorTimeInfo;
174 double CurrentKernelTime;
175 PSECURITY_DESCRIPTOR ProcessSD;
176 PSID ProcessUser;
177 ULONG Buffer[64]; /* must be 4 bytes aligned! */
178 ULONG cwcUserName;
179
180 /* Get new system time */
181 status = NtQuerySystemInformation(SystemTimeOfDayInformation, &SysTimeInfo, sizeof(SysTimeInfo), NULL);
182 if (!NT_SUCCESS(status))
183 return;
184
185 /* Get new CPU's idle time */
186 status = NtQuerySystemInformation(SystemPerformanceInformation, &SysPerfInfo, sizeof(SysPerfInfo), NULL);
187 if (!NT_SUCCESS(status))
188 return;
189
190 /* Get system cache information */
191 status = NtQuerySystemInformation(SystemFileCacheInformation, &SysCacheInfo, sizeof(SysCacheInfo), NULL);
192 if (!NT_SUCCESS(status))
193 return;
194
195 /* Get processor time information */
196 SysProcessorTimeInfo = (PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)HeapAlloc(GetProcessHeap(), 0, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * SystemBasicInfo.NumberOfProcessors);
197 status = NtQuerySystemInformation(SystemProcessorPerformanceInformation, SysProcessorTimeInfo, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * SystemBasicInfo.NumberOfProcessors, &ulSize);
198
199 if (!NT_SUCCESS(status))
200 {
201 if (SysProcessorTimeInfo != NULL)
202 HeapFree(GetProcessHeap(), 0, SysProcessorTimeInfo);
203 return;
204 }
205
206 /* Get handle information
207 * Number of handles is enough, no need for data array.
208 */
209 status = NtQuerySystemInformation(SystemHandleInformation, &SysHandleInfoData, sizeof(SysHandleInfoData), NULL);
210 /* On unexpected error, reuse previous value.
211 * STATUS_SUCCESS (0-1 handle) should never happen.
212 */
213 if (status != STATUS_INFO_LENGTH_MISMATCH)
214 SysHandleInfoData.NumberOfHandles = SystemNumberOfHandles;
215
216 /* Get process information
217 * We don't know how much data there is so just keep
218 * increasing the buffer size until the call succeeds
219 */
220 BufferSize = 0;
221 do
222 {
223 BufferSize += 0x10000;
224 pBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BufferSize);
225
226 status = NtQuerySystemInformation(SystemProcessInformation, pBuffer, BufferSize, &ulSize);
227
228 if (status == STATUS_INFO_LENGTH_MISMATCH) {
229 HeapFree(GetProcessHeap(), 0, pBuffer);
230 }
231
232 } while (status == STATUS_INFO_LENGTH_MISMATCH);
233
234 EnterCriticalSection(&PerfDataCriticalSection);
235
236 /*
237 * Save system performance info
238 */
239 memcpy(&SystemPerfInfo, &SysPerfInfo, sizeof(SYSTEM_PERFORMANCE_INFORMATION));
240
241 /*
242 * Save system cache info
243 */
244 memcpy(&SystemCacheInfo, &SysCacheInfo, sizeof(SYSTEM_FILECACHE_INFORMATION));
245
246 /*
247 * Save system processor time info
248 */
249 if (SystemProcessorTimeInfo) {
250 HeapFree(GetProcessHeap(), 0, SystemProcessorTimeInfo);
251 }
252 SystemProcessorTimeInfo = SysProcessorTimeInfo;
253
254 /*
255 * Save system handle info
256 */
257 SystemNumberOfHandles = SysHandleInfoData.NumberOfHandles;
258
259 for (CurrentKernelTime=0, Idx=0; Idx<(ULONG)SystemBasicInfo.NumberOfProcessors; Idx++) {
260 CurrentKernelTime += Li2Double(SystemProcessorTimeInfo[Idx].KernelTime);
261 CurrentKernelTime += Li2Double(SystemProcessorTimeInfo[Idx].DpcTime);
262 CurrentKernelTime += Li2Double(SystemProcessorTimeInfo[Idx].InterruptTime);
263 }
264
265 /* If it's a first call - skip idle time calcs */
266 if (liOldIdleTime.QuadPart != 0) {
267 /* CurrentValue = NewValue - OldValue */
268 dbIdleTime = Li2Double(SysPerfInfo.IdleProcessTime) - Li2Double(liOldIdleTime);
269 dbKernelTime = CurrentKernelTime - OldKernelTime;
270 dbSystemTime = Li2Double(SysTimeInfo.CurrentTime) - Li2Double(liOldSystemTime);
271
272 /* CurrentCpuIdle = IdleTime / SystemTime */
273 dbIdleTime = dbIdleTime / dbSystemTime;
274 dbKernelTime = dbKernelTime / dbSystemTime;
275
276 /* CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors */
277 dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors; /* + 0.5; */
278 dbKernelTime = 100.0 - dbKernelTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors; /* + 0.5; */
279 }
280
281 /* Store new CPU's idle and system time */
282 liOldIdleTime = SysPerfInfo.IdleProcessTime;
283 liOldSystemTime = SysTimeInfo.CurrentTime;
284 OldKernelTime = CurrentKernelTime;
285
286 /* Determine the process count
287 * We loop through the data we got from NtQuerySystemInformation
288 * and count how many structures there are (until RelativeOffset is 0)
289 */
290 ProcessCountOld = ProcessCount;
291 ProcessCount = 0;
292 pSPI = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
293 while (pSPI) {
294 ProcessCount++;
295 if (pSPI->NextEntryOffset == 0)
296 break;
297 pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->NextEntryOffset);
298 }
299
300 /* Now alloc a new PERFDATA array and fill in the data */
301 pPerfData = (PPERFDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PERFDATA) * ProcessCount);
302
303 pSPI = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
304 for (Idx=0; Idx<ProcessCount; Idx++) {
305 /* Get the old perf data for this process (if any) */
306 /* so that we can establish delta values */
307 pPDOld = NULL;
308 if (pPerfDataOld) {
309 for (Idx2=0; Idx2<ProcessCountOld; Idx2++) {
310 if (pPerfDataOld[Idx2].ProcessId == pSPI->UniqueProcessId) {
311 pPDOld = &pPerfDataOld[Idx2];
312 break;
313 }
314 }
315 }
316
317 if (pSPI->ImageName.Buffer) {
318 /* Don't assume a UNICODE_STRING Buffer is zero terminated: */
319 int len = pSPI->ImageName.Length / 2;
320 /* Check against max size and allow for terminating zero (already zeroed): */
321 if(len >= MAX_PATH)len=MAX_PATH - 1;
322 wcsncpy(pPerfData[Idx].ImageName, pSPI->ImageName.Buffer, len);
323 } else {
324 LoadStringW(hInst, IDS_IDLE_PROCESS, pPerfData[Idx].ImageName,
325 _countof(pPerfData[Idx].ImageName));
326 }
327
328 pPerfData[Idx].ProcessId = pSPI->UniqueProcessId;
329
330 if (pPDOld) {
331 double CurTime = Li2Double(pSPI->KernelTime) + Li2Double(pSPI->UserTime);
332 double OldTime = Li2Double(pPDOld->KernelTime) + Li2Double(pPDOld->UserTime);
333 double CpuTime = (CurTime - OldTime) / dbSystemTime;
334 CpuTime = CpuTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors; /* + 0.5; */
335 pPerfData[Idx].CPUUsage = (ULONG)CpuTime;
336 }
337 pPerfData[Idx].CPUTime.QuadPart = pSPI->UserTime.QuadPart + pSPI->KernelTime.QuadPart;
338 pPerfData[Idx].WorkingSetSizeBytes = pSPI->WorkingSetSize;
339 pPerfData[Idx].PeakWorkingSetSizeBytes = pSPI->PeakWorkingSetSize;
340 if (pPDOld)
341 pPerfData[Idx].WorkingSetSizeDelta = labs((LONG)pSPI->WorkingSetSize - (LONG)pPDOld->WorkingSetSizeBytes);
342 else
343 pPerfData[Idx].WorkingSetSizeDelta = 0;
344 pPerfData[Idx].PageFaultCount = pSPI->PageFaultCount;
345 if (pPDOld)
346 pPerfData[Idx].PageFaultCountDelta = labs((LONG)pSPI->PageFaultCount - (LONG)pPDOld->PageFaultCount);
347 else
348 pPerfData[Idx].PageFaultCountDelta = 0;
349 pPerfData[Idx].VirtualMemorySizeBytes = pSPI->VirtualSize;
350 pPerfData[Idx].PagedPoolUsagePages = pSPI->QuotaPeakPagedPoolUsage;
351 pPerfData[Idx].NonPagedPoolUsagePages = pSPI->QuotaPeakNonPagedPoolUsage;
352 pPerfData[Idx].BasePriority = pSPI->BasePriority;
353 pPerfData[Idx].HandleCount = pSPI->HandleCount;
354 pPerfData[Idx].ThreadCount = pSPI->NumberOfThreads;
355 pPerfData[Idx].SessionId = pSPI->SessionId;
356 pPerfData[Idx].UserName[0] = UNICODE_NULL;
357 pPerfData[Idx].USERObjectCount = 0;
358 pPerfData[Idx].GDIObjectCount = 0;
359 ProcessUser = SystemUserSid;
360 ProcessSD = NULL;
361
362 if (pSPI->UniqueProcessId != NULL) {
363 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | READ_CONTROL, FALSE, PtrToUlong(pSPI->UniqueProcessId));
364 if (hProcess) {
365 /* don't query the information of the system process. It's possible but
366 returns Administrators as the owner of the process instead of SYSTEM */
367 if (pSPI->UniqueProcessId != (HANDLE)0x4)
368 {
369 if (OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken))
370 {
371 DWORD RetLen = 0;
372 BOOL Ret;
373
374 Ret = GetTokenInformation(hProcessToken, TokenUser, (LPVOID)Buffer, sizeof(Buffer), &RetLen);
375 CloseHandle(hProcessToken);
376
377 if (Ret)
378 ProcessUser = ((PTOKEN_USER)Buffer)->User.Sid;
379 else
380 goto ReadProcOwner;
381 }
382 else
383 {
384 ReadProcOwner:
385 GetSecurityInfo(hProcess, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, &ProcessUser, NULL, NULL, NULL, &ProcessSD);
386 }
387
388 pPerfData[Idx].USERObjectCount = GetGuiResources(hProcess, GR_USEROBJECTS);
389 pPerfData[Idx].GDIObjectCount = GetGuiResources(hProcess, GR_GDIOBJECTS);
390 }
391
392 GetProcessIoCounters(hProcess, &pPerfData[Idx].IOCounters);
393 CloseHandle(hProcess);
394 } else {
395 goto ClearInfo;
396 }
397 } else {
398 ClearInfo:
399 /* clear information we were unable to fetch */
400 ZeroMemory(&pPerfData[Idx].IOCounters, sizeof(IO_COUNTERS));
401 }
402
403 cwcUserName = _countof(pPerfData[0].UserName);
404 CachedGetUserFromSid(ProcessUser, pPerfData[Idx].UserName, &cwcUserName);
405
406 if (ProcessSD != NULL)
407 {
408 LocalFree((HLOCAL)ProcessSD);
409 }
410
411 pPerfData[Idx].UserTime.QuadPart = pSPI->UserTime.QuadPart;
412 pPerfData[Idx].KernelTime.QuadPart = pSPI->KernelTime.QuadPart;
413 pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->NextEntryOffset);
414 }
415 HeapFree(GetProcessHeap(), 0, pBuffer);
416 if (pPerfDataOld) {
417 HeapFree(GetProcessHeap(), 0, pPerfDataOld);
418 }
419 pPerfDataOld = pPerfData;
420 LeaveCriticalSection(&PerfDataCriticalSection);
421 }
422
PerfDataGetProcessIndex(ULONG pid)423 ULONG PerfDataGetProcessIndex(ULONG pid)
424 {
425 ULONG idx;
426
427 EnterCriticalSection(&PerfDataCriticalSection);
428
429 for (idx = 0; idx < ProcessCount; idx++)
430 {
431 if (PtrToUlong(pPerfData[idx].ProcessId) == pid)
432 {
433 break;
434 }
435 }
436
437 LeaveCriticalSection(&PerfDataCriticalSection);
438
439 if (idx == ProcessCount)
440 {
441 return -1;
442 }
443 return idx;
444 }
445
PerfDataGetProcessCount(void)446 ULONG PerfDataGetProcessCount(void)
447 {
448 ULONG Result;
449 EnterCriticalSection(&PerfDataCriticalSection);
450 Result = ProcessCount;
451 LeaveCriticalSection(&PerfDataCriticalSection);
452 return Result;
453 }
454
PerfDataGetProcessorUsage(void)455 ULONG PerfDataGetProcessorUsage(void)
456 {
457 ULONG Result;
458 EnterCriticalSection(&PerfDataCriticalSection);
459 Result = (ULONG)min(max(dbIdleTime, 0.), 100.);
460 LeaveCriticalSection(&PerfDataCriticalSection);
461 return Result;
462 }
463
PerfDataGetProcessorSystemUsage(void)464 ULONG PerfDataGetProcessorSystemUsage(void)
465 {
466 ULONG Result;
467 EnterCriticalSection(&PerfDataCriticalSection);
468 Result = (ULONG)min(max(dbKernelTime, 0.), 100.);
469 LeaveCriticalSection(&PerfDataCriticalSection);
470 return Result;
471 }
472
PerfDataGetImageName(ULONG Index,LPWSTR lpImageName,ULONG nMaxCount)473 BOOL PerfDataGetImageName(ULONG Index, LPWSTR lpImageName, ULONG nMaxCount)
474 {
475 BOOL bSuccessful;
476
477 EnterCriticalSection(&PerfDataCriticalSection);
478
479 if (Index < ProcessCount) {
480 wcsncpy(lpImageName, pPerfData[Index].ImageName, nMaxCount);
481 bSuccessful = TRUE;
482 } else {
483 bSuccessful = FALSE;
484 }
485 LeaveCriticalSection(&PerfDataCriticalSection);
486 return bSuccessful;
487 }
488
PerfDataGetProcessId(ULONG Index)489 ULONG PerfDataGetProcessId(ULONG Index)
490 {
491 ULONG ProcessId;
492
493 EnterCriticalSection(&PerfDataCriticalSection);
494
495 if (Index < ProcessCount)
496 ProcessId = PtrToUlong(pPerfData[Index].ProcessId);
497 else
498 ProcessId = 0;
499
500 LeaveCriticalSection(&PerfDataCriticalSection);
501
502 return ProcessId;
503 }
504
PerfDataGetUserName(ULONG Index,LPWSTR lpUserName,ULONG nMaxCount)505 BOOL PerfDataGetUserName(ULONG Index, LPWSTR lpUserName, ULONG nMaxCount)
506 {
507 BOOL bSuccessful;
508
509 EnterCriticalSection(&PerfDataCriticalSection);
510
511 if (Index < ProcessCount) {
512 wcsncpy(lpUserName, pPerfData[Index].UserName, nMaxCount);
513 bSuccessful = TRUE;
514 } else {
515 bSuccessful = FALSE;
516 }
517
518 LeaveCriticalSection(&PerfDataCriticalSection);
519
520 return bSuccessful;
521 }
522
PerfDataGetCommandLine(ULONG Index,LPWSTR lpCommandLine,ULONG nMaxCount)523 BOOL PerfDataGetCommandLine(ULONG Index, LPWSTR lpCommandLine, ULONG nMaxCount)
524 {
525 static const LPWSTR ellipsis = L"...";
526
527 PROCESS_BASIC_INFORMATION pbi = {0};
528 UNICODE_STRING CommandLineStr = {0};
529
530 PVOID ProcessParams = NULL;
531 HANDLE hProcess;
532 ULONG ProcessId;
533
534 NTSTATUS Status;
535 BOOL result;
536
537 PCMD_LINE_CACHE new_entry;
538 LPWSTR new_string;
539
540 PCMD_LINE_CACHE cache = global_cache;
541
542 /* [A] Search for a string already in cache? If so, use it */
543 while (cache && cache->pnext != NULL)
544 {
545 if (cache->idx == Index && cache->str != NULL)
546 {
547 /* Found it. Use it, and add some ellipsis at the very end to make it cute */
548 wcsncpy(lpCommandLine, cache->str, CMD_LINE_MIN(nMaxCount, cache->len));
549 wcscpy(lpCommandLine + CMD_LINE_MIN(nMaxCount, cache->len) - wcslen(ellipsis), ellipsis);
550 return TRUE;
551 }
552
553 cache = cache->pnext;
554 }
555
556 /* [B] We don't; let's allocate and load a value from the process mem... and cache it */
557 ProcessId = PerfDataGetProcessId(Index);
558
559 /* Default blank command line in case things don't work out */
560 wcsncpy(lpCommandLine, L"", nMaxCount);
561
562 /* Ask for a handle to the target process so that we can read its memory and query stuff */
563 hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessId);
564 if (!hProcess)
565 goto cleanup;
566
567 /* First off, get the ProcessEnvironmentBlock location in that process' address space */
568 Status = NtQueryInformationProcess(hProcess, 0, &pbi, sizeof(pbi), NULL);
569 if (!NT_SUCCESS(Status))
570 goto cleanup;
571
572 /* Then get the PEB.ProcessParameters member pointer */
573 result = ReadProcessMemory(hProcess,
574 (PVOID)((ULONG_PTR)pbi.PebBaseAddress + FIELD_OFFSET(PEB, ProcessParameters)),
575 &ProcessParams,
576 sizeof(ProcessParams),
577 NULL);
578 if (!result)
579 goto cleanup;
580
581 /* Then copy the PEB->ProcessParameters.CommandLine member
582 to get the pointer to the string buffer and its size */
583 result = ReadProcessMemory(hProcess,
584 (PVOID)((ULONG_PTR)ProcessParams + FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, CommandLine)),
585 &CommandLineStr,
586 sizeof(CommandLineStr),
587 NULL);
588 if (!result)
589 goto cleanup;
590
591 /* Allocate the next cache entry and its accompanying string in one go */
592 new_entry = HeapAlloc(GetProcessHeap(),
593 HEAP_ZERO_MEMORY,
594 sizeof(CMD_LINE_CACHE) + CommandLineStr.Length + sizeof(UNICODE_NULL));
595 if (!new_entry)
596 goto cleanup;
597
598 new_string = (LPWSTR)((ULONG_PTR)new_entry + sizeof(CMD_LINE_CACHE));
599
600 /* Bingo, the command line should be stored there,
601 copy the string from the other process */
602 result = ReadProcessMemory(hProcess,
603 CommandLineStr.Buffer,
604 new_string,
605 CommandLineStr.Length,
606 NULL);
607 if (!result)
608 {
609 /* Weird, after successfully reading the mem of that process
610 various times it fails now, forget it and bail out */
611 HeapFree(GetProcessHeap(), 0, new_entry);
612 goto cleanup;
613 }
614
615 /* Add our pointer to the cache... */
616 new_entry->idx = Index;
617 new_entry->str = new_string;
618 new_entry->len = CommandLineStr.Length;
619
620 if (!global_cache)
621 global_cache = new_entry;
622 else
623 cache->pnext = new_entry;
624
625 /* ... and print the buffer for the first time */
626 wcsncpy(lpCommandLine, new_string, CMD_LINE_MIN(nMaxCount, CommandLineStr.Length));
627
628 cleanup:
629 if (hProcess) CloseHandle(hProcess);
630 return TRUE;
631 }
632
PerfDataDeallocCommandLineCache()633 void PerfDataDeallocCommandLineCache()
634 {
635 PCMD_LINE_CACHE cache, pnext;
636
637 for (cache = global_cache; cache; cache = pnext)
638 {
639 pnext = cache->pnext;
640 HeapFree(GetProcessHeap(), 0, cache);
641 }
642
643 global_cache = NULL;
644 }
645
PerfDataGetSessionId(ULONG Index)646 ULONG PerfDataGetSessionId(ULONG Index)
647 {
648 ULONG SessionId;
649
650 EnterCriticalSection(&PerfDataCriticalSection);
651
652 if (Index < ProcessCount)
653 SessionId = pPerfData[Index].SessionId;
654 else
655 SessionId = 0;
656
657 LeaveCriticalSection(&PerfDataCriticalSection);
658
659 return SessionId;
660 }
661
PerfDataGetCPUUsage(ULONG Index)662 ULONG PerfDataGetCPUUsage(ULONG Index)
663 {
664 ULONG CpuUsage;
665
666 EnterCriticalSection(&PerfDataCriticalSection);
667
668 if (Index < ProcessCount)
669 CpuUsage = pPerfData[Index].CPUUsage;
670 else
671 CpuUsage = 0;
672
673 LeaveCriticalSection(&PerfDataCriticalSection);
674
675 return CpuUsage;
676 }
677
PerfDataGetCPUTime(ULONG Index)678 LARGE_INTEGER PerfDataGetCPUTime(ULONG Index)
679 {
680 LARGE_INTEGER CpuTime = {{0,0}};
681
682 EnterCriticalSection(&PerfDataCriticalSection);
683
684 if (Index < ProcessCount)
685 CpuTime = pPerfData[Index].CPUTime;
686
687 LeaveCriticalSection(&PerfDataCriticalSection);
688
689 return CpuTime;
690 }
691
PerfDataGetWorkingSetSizeBytes(ULONG Index)692 ULONG PerfDataGetWorkingSetSizeBytes(ULONG Index)
693 {
694 ULONG WorkingSetSizeBytes;
695
696 EnterCriticalSection(&PerfDataCriticalSection);
697
698 if (Index < ProcessCount)
699 WorkingSetSizeBytes = pPerfData[Index].WorkingSetSizeBytes;
700 else
701 WorkingSetSizeBytes = 0;
702
703 LeaveCriticalSection(&PerfDataCriticalSection);
704
705 return WorkingSetSizeBytes;
706 }
707
PerfDataGetPeakWorkingSetSizeBytes(ULONG Index)708 ULONG PerfDataGetPeakWorkingSetSizeBytes(ULONG Index)
709 {
710 ULONG PeakWorkingSetSizeBytes;
711
712 EnterCriticalSection(&PerfDataCriticalSection);
713
714 if (Index < ProcessCount)
715 PeakWorkingSetSizeBytes = pPerfData[Index].PeakWorkingSetSizeBytes;
716 else
717 PeakWorkingSetSizeBytes = 0;
718
719 LeaveCriticalSection(&PerfDataCriticalSection);
720
721 return PeakWorkingSetSizeBytes;
722 }
723
PerfDataGetWorkingSetSizeDelta(ULONG Index)724 ULONG PerfDataGetWorkingSetSizeDelta(ULONG Index)
725 {
726 ULONG WorkingSetSizeDelta;
727
728 EnterCriticalSection(&PerfDataCriticalSection);
729
730 if (Index < ProcessCount)
731 WorkingSetSizeDelta = pPerfData[Index].WorkingSetSizeDelta;
732 else
733 WorkingSetSizeDelta = 0;
734
735 LeaveCriticalSection(&PerfDataCriticalSection);
736
737 return WorkingSetSizeDelta;
738 }
739
PerfDataGetPageFaultCount(ULONG Index)740 ULONG PerfDataGetPageFaultCount(ULONG Index)
741 {
742 ULONG PageFaultCount;
743
744 EnterCriticalSection(&PerfDataCriticalSection);
745
746 if (Index < ProcessCount)
747 PageFaultCount = pPerfData[Index].PageFaultCount;
748 else
749 PageFaultCount = 0;
750
751 LeaveCriticalSection(&PerfDataCriticalSection);
752
753 return PageFaultCount;
754 }
755
PerfDataGetPageFaultCountDelta(ULONG Index)756 ULONG PerfDataGetPageFaultCountDelta(ULONG Index)
757 {
758 ULONG PageFaultCountDelta;
759
760 EnterCriticalSection(&PerfDataCriticalSection);
761
762 if (Index < ProcessCount)
763 PageFaultCountDelta = pPerfData[Index].PageFaultCountDelta;
764 else
765 PageFaultCountDelta = 0;
766
767 LeaveCriticalSection(&PerfDataCriticalSection);
768
769 return PageFaultCountDelta;
770 }
771
PerfDataGetVirtualMemorySizeBytes(ULONG Index)772 ULONG PerfDataGetVirtualMemorySizeBytes(ULONG Index)
773 {
774 ULONG VirtualMemorySizeBytes;
775
776 EnterCriticalSection(&PerfDataCriticalSection);
777
778 if (Index < ProcessCount)
779 VirtualMemorySizeBytes = pPerfData[Index].VirtualMemorySizeBytes;
780 else
781 VirtualMemorySizeBytes = 0;
782
783 LeaveCriticalSection(&PerfDataCriticalSection);
784
785 return VirtualMemorySizeBytes;
786 }
787
PerfDataGetPagedPoolUsagePages(ULONG Index)788 ULONG PerfDataGetPagedPoolUsagePages(ULONG Index)
789 {
790 ULONG PagedPoolUsage;
791
792 EnterCriticalSection(&PerfDataCriticalSection);
793
794 if (Index < ProcessCount)
795 PagedPoolUsage = pPerfData[Index].PagedPoolUsagePages;
796 else
797 PagedPoolUsage = 0;
798
799 LeaveCriticalSection(&PerfDataCriticalSection);
800
801 return PagedPoolUsage;
802 }
803
PerfDataGetNonPagedPoolUsagePages(ULONG Index)804 ULONG PerfDataGetNonPagedPoolUsagePages(ULONG Index)
805 {
806 ULONG NonPagedPoolUsage;
807
808 EnterCriticalSection(&PerfDataCriticalSection);
809
810 if (Index < ProcessCount)
811 NonPagedPoolUsage = pPerfData[Index].NonPagedPoolUsagePages;
812 else
813 NonPagedPoolUsage = 0;
814
815 LeaveCriticalSection(&PerfDataCriticalSection);
816
817 return NonPagedPoolUsage;
818 }
819
PerfDataGetBasePriority(ULONG Index)820 ULONG PerfDataGetBasePriority(ULONG Index)
821 {
822 ULONG BasePriority;
823
824 EnterCriticalSection(&PerfDataCriticalSection);
825
826 if (Index < ProcessCount)
827 BasePriority = pPerfData[Index].BasePriority;
828 else
829 BasePriority = 0;
830
831 LeaveCriticalSection(&PerfDataCriticalSection);
832
833 return BasePriority;
834 }
835
PerfDataGetHandleCount(ULONG Index)836 ULONG PerfDataGetHandleCount(ULONG Index)
837 {
838 ULONG HandleCount;
839
840 EnterCriticalSection(&PerfDataCriticalSection);
841
842 if (Index < ProcessCount)
843 HandleCount = pPerfData[Index].HandleCount;
844 else
845 HandleCount = 0;
846
847 LeaveCriticalSection(&PerfDataCriticalSection);
848
849 return HandleCount;
850 }
851
PerfDataGetThreadCount(ULONG Index)852 ULONG PerfDataGetThreadCount(ULONG Index)
853 {
854 ULONG ThreadCount;
855
856 EnterCriticalSection(&PerfDataCriticalSection);
857
858 if (Index < ProcessCount)
859 ThreadCount = pPerfData[Index].ThreadCount;
860 else
861 ThreadCount = 0;
862
863 LeaveCriticalSection(&PerfDataCriticalSection);
864
865 return ThreadCount;
866 }
867
PerfDataGetUSERObjectCount(ULONG Index)868 ULONG PerfDataGetUSERObjectCount(ULONG Index)
869 {
870 ULONG USERObjectCount;
871
872 EnterCriticalSection(&PerfDataCriticalSection);
873
874 if (Index < ProcessCount)
875 USERObjectCount = pPerfData[Index].USERObjectCount;
876 else
877 USERObjectCount = 0;
878
879 LeaveCriticalSection(&PerfDataCriticalSection);
880
881 return USERObjectCount;
882 }
883
PerfDataGetGDIObjectCount(ULONG Index)884 ULONG PerfDataGetGDIObjectCount(ULONG Index)
885 {
886 ULONG GDIObjectCount;
887
888 EnterCriticalSection(&PerfDataCriticalSection);
889
890 if (Index < ProcessCount)
891 GDIObjectCount = pPerfData[Index].GDIObjectCount;
892 else
893 GDIObjectCount = 0;
894
895 LeaveCriticalSection(&PerfDataCriticalSection);
896
897 return GDIObjectCount;
898 }
899
PerfDataGetIOCounters(ULONG Index,PIO_COUNTERS pIoCounters)900 BOOL PerfDataGetIOCounters(ULONG Index, PIO_COUNTERS pIoCounters)
901 {
902 BOOL bSuccessful;
903
904 EnterCriticalSection(&PerfDataCriticalSection);
905
906 if (Index < ProcessCount)
907 {
908 memcpy(pIoCounters, &pPerfData[Index].IOCounters, sizeof(IO_COUNTERS));
909 bSuccessful = TRUE;
910 }
911 else
912 bSuccessful = FALSE;
913
914 LeaveCriticalSection(&PerfDataCriticalSection);
915
916 return bSuccessful;
917 }
918
PerfDataGetCommitChargeTotalK(void)919 ULONG PerfDataGetCommitChargeTotalK(void)
920 {
921 ULONG Total;
922 ULONG PageSize;
923
924 EnterCriticalSection(&PerfDataCriticalSection);
925
926 Total = SystemPerfInfo.CommittedPages;
927 PageSize = SystemBasicInfo.PageSize;
928
929 LeaveCriticalSection(&PerfDataCriticalSection);
930
931 Total = Total * (PageSize / 1024);
932
933 return Total;
934 }
935
PerfDataGetCommitChargeLimitK(void)936 ULONG PerfDataGetCommitChargeLimitK(void)
937 {
938 ULONG Limit;
939 ULONG PageSize;
940
941 EnterCriticalSection(&PerfDataCriticalSection);
942
943 Limit = SystemPerfInfo.CommitLimit;
944 PageSize = SystemBasicInfo.PageSize;
945
946 LeaveCriticalSection(&PerfDataCriticalSection);
947
948 Limit = Limit * (PageSize / 1024);
949
950 return Limit;
951 }
952
PerfDataGetCommitChargePeakK(void)953 ULONG PerfDataGetCommitChargePeakK(void)
954 {
955 ULONG Peak;
956 ULONG PageSize;
957
958 EnterCriticalSection(&PerfDataCriticalSection);
959
960 Peak = SystemPerfInfo.PeakCommitment;
961 PageSize = SystemBasicInfo.PageSize;
962
963 LeaveCriticalSection(&PerfDataCriticalSection);
964
965 Peak = Peak * (PageSize / 1024);
966
967 return Peak;
968 }
969
PerfDataGetKernelMemoryTotalK(void)970 ULONG PerfDataGetKernelMemoryTotalK(void)
971 {
972 ULONG Total;
973 ULONG Paged;
974 ULONG NonPaged;
975 ULONG PageSize;
976
977 EnterCriticalSection(&PerfDataCriticalSection);
978
979 Paged = SystemPerfInfo.PagedPoolPages;
980 NonPaged = SystemPerfInfo.NonPagedPoolPages;
981 PageSize = SystemBasicInfo.PageSize;
982
983 LeaveCriticalSection(&PerfDataCriticalSection);
984
985 Paged = Paged * (PageSize / 1024);
986 NonPaged = NonPaged * (PageSize / 1024);
987
988 Total = Paged + NonPaged;
989
990 return Total;
991 }
992
PerfDataGetKernelMemoryPagedK(void)993 ULONG PerfDataGetKernelMemoryPagedK(void)
994 {
995 ULONG Paged;
996 ULONG PageSize;
997
998 EnterCriticalSection(&PerfDataCriticalSection);
999
1000 Paged = SystemPerfInfo.PagedPoolPages;
1001 PageSize = SystemBasicInfo.PageSize;
1002
1003 LeaveCriticalSection(&PerfDataCriticalSection);
1004
1005 Paged = Paged * (PageSize / 1024);
1006
1007 return Paged;
1008 }
1009
PerfDataGetKernelMemoryNonPagedK(void)1010 ULONG PerfDataGetKernelMemoryNonPagedK(void)
1011 {
1012 ULONG NonPaged;
1013 ULONG PageSize;
1014
1015 EnterCriticalSection(&PerfDataCriticalSection);
1016
1017 NonPaged = SystemPerfInfo.NonPagedPoolPages;
1018 PageSize = SystemBasicInfo.PageSize;
1019
1020 LeaveCriticalSection(&PerfDataCriticalSection);
1021
1022 NonPaged = NonPaged * (PageSize / 1024);
1023
1024 return NonPaged;
1025 }
1026
PerfDataGetPhysicalMemoryTotalK(void)1027 ULONG PerfDataGetPhysicalMemoryTotalK(void)
1028 {
1029 ULONG Total;
1030 ULONG PageSize;
1031
1032 EnterCriticalSection(&PerfDataCriticalSection);
1033
1034 Total = SystemBasicInfo.NumberOfPhysicalPages;
1035 PageSize = SystemBasicInfo.PageSize;
1036
1037 LeaveCriticalSection(&PerfDataCriticalSection);
1038
1039 Total = Total * (PageSize / 1024);
1040
1041 return Total;
1042 }
1043
PerfDataGetPhysicalMemoryAvailableK(void)1044 ULONG PerfDataGetPhysicalMemoryAvailableK(void)
1045 {
1046 ULONG Available;
1047 ULONG PageSize;
1048
1049 EnterCriticalSection(&PerfDataCriticalSection);
1050
1051 Available = SystemPerfInfo.AvailablePages;
1052 PageSize = SystemBasicInfo.PageSize;
1053
1054 LeaveCriticalSection(&PerfDataCriticalSection);
1055
1056 Available = Available * (PageSize / 1024);
1057
1058 return Available;
1059 }
1060
PerfDataGetPhysicalMemorySystemCacheK(void)1061 ULONG PerfDataGetPhysicalMemorySystemCacheK(void)
1062 {
1063 ULONG SystemCache;
1064 ULONG PageSize;
1065
1066 EnterCriticalSection(&PerfDataCriticalSection);
1067
1068 PageSize = SystemBasicInfo.PageSize;
1069 SystemCache = SystemCacheInfo.CurrentSizeIncludingTransitionInPages * PageSize;
1070
1071 LeaveCriticalSection(&PerfDataCriticalSection);
1072
1073 return SystemCache / 1024;
1074 }
1075
PerfDataGetSystemHandleCount(void)1076 ULONG PerfDataGetSystemHandleCount(void)
1077 {
1078 ULONG HandleCount;
1079
1080 EnterCriticalSection(&PerfDataCriticalSection);
1081
1082 HandleCount = SystemNumberOfHandles;
1083
1084 LeaveCriticalSection(&PerfDataCriticalSection);
1085
1086 return HandleCount;
1087 }
1088
PerfDataGetTotalThreadCount(void)1089 ULONG PerfDataGetTotalThreadCount(void)
1090 {
1091 ULONG ThreadCount = 0;
1092 ULONG i;
1093
1094 EnterCriticalSection(&PerfDataCriticalSection);
1095
1096 for (i=0; i<ProcessCount; i++)
1097 {
1098 ThreadCount += pPerfData[i].ThreadCount;
1099 }
1100
1101 LeaveCriticalSection(&PerfDataCriticalSection);
1102
1103 return ThreadCount;
1104 }
1105
PerfDataGet(ULONG Index,PPERFDATA * lppData)1106 BOOL PerfDataGet(ULONG Index, PPERFDATA *lppData)
1107 {
1108 BOOL bSuccessful = FALSE;
1109
1110 EnterCriticalSection(&PerfDataCriticalSection);
1111 if (Index < ProcessCount)
1112 {
1113 *lppData = pPerfData + Index;
1114 bSuccessful = TRUE;
1115 }
1116 LeaveCriticalSection(&PerfDataCriticalSection);
1117 return bSuccessful;
1118 }
1119
1120