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