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 
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 
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 
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
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 
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 
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 
446 ULONG PerfDataGetProcessCount(void)
447 {
448     ULONG Result;
449     EnterCriticalSection(&PerfDataCriticalSection);
450     Result = ProcessCount;
451     LeaveCriticalSection(&PerfDataCriticalSection);
452     return Result;
453 }
454 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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 
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