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 ULONG                                      SystemNumberOfHandles;
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     SYSTEM_HANDLE_INFORMATION                  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      * Number of handles is enough, no need for data array.
225      */
226     status = NtQuerySystemInformation(SystemHandleInformation, &SysHandleInfoData, sizeof(SysHandleInfoData), NULL);
227     /* On unexpected error, reuse previous value.
228      * STATUS_SUCCESS (0-1 handle) should never happen.
229      */
230     if (status != STATUS_INFO_LENGTH_MISMATCH)
231         SysHandleInfoData.NumberOfHandles = SystemNumberOfHandles;
232 
233     /* Get process information
234      * We don't know how much data there is so just keep
235      * increasing the buffer size until the call succeeds
236      */
237     BufferSize = 0;
238     do
239     {
240         BufferSize += 0x10000;
241         pBuffer = (LPBYTE)HeapAlloc(GetProcessHeap(), 0, BufferSize);
242 
243         status = NtQuerySystemInformation(SystemProcessInformation, pBuffer, BufferSize, &ulSize);
244 
245         if (status == STATUS_INFO_LENGTH_MISMATCH) {
246             HeapFree(GetProcessHeap(), 0, pBuffer);
247         }
248 
249     } while (status == STATUS_INFO_LENGTH_MISMATCH);
250 
251     EnterCriticalSection(&PerfDataCriticalSection);
252 
253     /*
254      * Save system performance info
255      */
256     memcpy(&SystemPerfInfo, &SysPerfInfo, sizeof(SYSTEM_PERFORMANCE_INFORMATION));
257 
258     /*
259      * Save system cache info
260      */
261     memcpy(&SystemCacheInfo, &SysCacheInfo, sizeof(SYSTEM_FILECACHE_INFORMATION));
262 
263     /*
264      * Save system processor time info
265      */
266     if (SystemProcessorTimeInfo) {
267         HeapFree(GetProcessHeap(), 0, SystemProcessorTimeInfo);
268     }
269     SystemProcessorTimeInfo = SysProcessorTimeInfo;
270 
271     /*
272      * Save system handle info
273      */
274     SystemNumberOfHandles = SysHandleInfoData.NumberOfHandles;
275 
276     for (CurrentKernelTime=0, Idx=0; Idx<(ULONG)SystemBasicInfo.NumberOfProcessors; Idx++) {
277         CurrentKernelTime += Li2Double(SystemProcessorTimeInfo[Idx].KernelTime);
278         CurrentKernelTime += Li2Double(SystemProcessorTimeInfo[Idx].DpcTime);
279         CurrentKernelTime += Li2Double(SystemProcessorTimeInfo[Idx].InterruptTime);
280     }
281 
282     /* If it's a first call - skip idle time calcs */
283     if (liOldIdleTime.QuadPart != 0) {
284         /*  CurrentValue = NewValue - OldValue */
285         dbIdleTime = Li2Double(SysPerfInfo.IdleProcessTime) - Li2Double(liOldIdleTime);
286         dbKernelTime = CurrentKernelTime - OldKernelTime;
287         dbSystemTime = Li2Double(SysTimeInfo.CurrentTime) - Li2Double(liOldSystemTime);
288 
289         /*  CurrentCpuIdle = IdleTime / SystemTime */
290         dbIdleTime = dbIdleTime / dbSystemTime;
291         dbKernelTime = dbKernelTime / dbSystemTime;
292 
293         /*  CurrentCpuUsage% = 100 - (CurrentCpuIdle * 100) / NumberOfProcessors */
294         dbIdleTime = 100.0 - dbIdleTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors; /* + 0.5; */
295         dbKernelTime = 100.0 - dbKernelTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors; /* + 0.5; */
296     }
297 
298     /* Store new CPU's idle and system time */
299     liOldIdleTime = SysPerfInfo.IdleProcessTime;
300     liOldSystemTime = SysTimeInfo.CurrentTime;
301     OldKernelTime = CurrentKernelTime;
302 
303     /* Determine the process count
304      * We loop through the data we got from NtQuerySystemInformation
305      * and count how many structures there are (until RelativeOffset is 0)
306      */
307     ProcessCountOld = ProcessCount;
308     ProcessCount = 0;
309     pSPI = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
310     while (pSPI) {
311         ProcessCount++;
312         if (pSPI->NextEntryOffset == 0)
313             break;
314         pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->NextEntryOffset);
315     }
316 
317     /* Now alloc a new PERFDATA array and fill in the data */
318     pPerfData = (PPERFDATA)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(PERFDATA) * ProcessCount);
319 
320     pSPI = (PSYSTEM_PROCESS_INFORMATION)pBuffer;
321     for (Idx=0; Idx<ProcessCount; Idx++) {
322         /* Get the old perf data for this process (if any) */
323         /* so that we can establish delta values */
324         pPDOld = NULL;
325         if (pPerfDataOld) {
326             for (Idx2=0; Idx2<ProcessCountOld; Idx2++) {
327                 if (pPerfDataOld[Idx2].ProcessId == pSPI->UniqueProcessId) {
328                     pPDOld = &pPerfDataOld[Idx2];
329                     break;
330                 }
331             }
332         }
333 
334         if (pSPI->ImageName.Buffer) {
335             /* Don't assume a UNICODE_STRING Buffer is zero terminated: */
336             int len = pSPI->ImageName.Length / 2;
337             /* Check against max size and allow for terminating zero (already zeroed): */
338             if(len >= MAX_PATH)len=MAX_PATH - 1;
339             wcsncpy(pPerfData[Idx].ImageName, pSPI->ImageName.Buffer, len);
340         } else {
341             LoadStringW(hInst, IDS_IDLE_PROCESS, pPerfData[Idx].ImageName,
342                        sizeof(pPerfData[Idx].ImageName) / sizeof(pPerfData[Idx].ImageName[0]));
343         }
344 
345         pPerfData[Idx].ProcessId = pSPI->UniqueProcessId;
346 
347         if (pPDOld)    {
348             double    CurTime = Li2Double(pSPI->KernelTime) + Li2Double(pSPI->UserTime);
349             double    OldTime = Li2Double(pPDOld->KernelTime) + Li2Double(pPDOld->UserTime);
350             double    CpuTime = (CurTime - OldTime) / dbSystemTime;
351             CpuTime = CpuTime * 100.0 / (double)SystemBasicInfo.NumberOfProcessors; /* + 0.5; */
352             pPerfData[Idx].CPUUsage = (ULONG)CpuTime;
353         }
354         pPerfData[Idx].CPUTime.QuadPart = pSPI->UserTime.QuadPart + pSPI->KernelTime.QuadPart;
355         pPerfData[Idx].WorkingSetSizeBytes = pSPI->WorkingSetSize;
356         pPerfData[Idx].PeakWorkingSetSizeBytes = pSPI->PeakWorkingSetSize;
357         if (pPDOld)
358             pPerfData[Idx].WorkingSetSizeDelta = labs((LONG)pSPI->WorkingSetSize - (LONG)pPDOld->WorkingSetSizeBytes);
359         else
360             pPerfData[Idx].WorkingSetSizeDelta = 0;
361         pPerfData[Idx].PageFaultCount = pSPI->PageFaultCount;
362         if (pPDOld)
363             pPerfData[Idx].PageFaultCountDelta = labs((LONG)pSPI->PageFaultCount - (LONG)pPDOld->PageFaultCount);
364         else
365             pPerfData[Idx].PageFaultCountDelta = 0;
366         pPerfData[Idx].VirtualMemorySizeBytes = pSPI->VirtualSize;
367         pPerfData[Idx].PagedPoolUsagePages = pSPI->QuotaPeakPagedPoolUsage;
368         pPerfData[Idx].NonPagedPoolUsagePages = pSPI->QuotaPeakNonPagedPoolUsage;
369         pPerfData[Idx].BasePriority = pSPI->BasePriority;
370         pPerfData[Idx].HandleCount = pSPI->HandleCount;
371         pPerfData[Idx].ThreadCount = pSPI->NumberOfThreads;
372         pPerfData[Idx].SessionId = pSPI->SessionId;
373         pPerfData[Idx].UserName[0] = UNICODE_NULL;
374         pPerfData[Idx].USERObjectCount = 0;
375         pPerfData[Idx].GDIObjectCount = 0;
376         ProcessUser = SystemUserSid;
377         ProcessSD = NULL;
378 
379         if (pSPI->UniqueProcessId != NULL) {
380             hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | READ_CONTROL, FALSE, PtrToUlong(pSPI->UniqueProcessId));
381             if (hProcess) {
382                 /* don't query the information of the system process. It's possible but
383                    returns Administrators as the owner of the process instead of SYSTEM */
384                 if (pSPI->UniqueProcessId != (HANDLE)0x4)
385                 {
386                     if (OpenProcessToken(hProcess, TOKEN_QUERY, &hProcessToken))
387                     {
388                         DWORD RetLen = 0;
389                         BOOL Ret;
390 
391                         Ret = GetTokenInformation(hProcessToken, TokenUser, (LPVOID)Buffer, sizeof(Buffer), &RetLen);
392                         CloseHandle(hProcessToken);
393 
394                         if (Ret)
395                             ProcessUser = ((PTOKEN_USER)Buffer)->User.Sid;
396                         else
397                             goto ReadProcOwner;
398                     }
399                     else
400                     {
401 ReadProcOwner:
402                         GetSecurityInfo(hProcess, SE_KERNEL_OBJECT, OWNER_SECURITY_INFORMATION, &ProcessUser, NULL, NULL, NULL, &ProcessSD);
403                     }
404 
405                     pPerfData[Idx].USERObjectCount = GetGuiResources(hProcess, GR_USEROBJECTS);
406                     pPerfData[Idx].GDIObjectCount = GetGuiResources(hProcess, GR_GDIOBJECTS);
407                 }
408 
409                 GetProcessIoCounters(hProcess, &pPerfData[Idx].IOCounters);
410                 CloseHandle(hProcess);
411             } else {
412                 goto ClearInfo;
413             }
414         } else {
415 ClearInfo:
416             /* clear information we were unable to fetch */
417             ZeroMemory(&pPerfData[Idx].IOCounters, sizeof(IO_COUNTERS));
418         }
419 
420         cwcUserName = sizeof(pPerfData[0].UserName) / sizeof(pPerfData[0].UserName[0]);
421         CachedGetUserFromSid(ProcessUser, pPerfData[Idx].UserName, &cwcUserName);
422 
423         if (ProcessSD != NULL)
424         {
425             LocalFree((HLOCAL)ProcessSD);
426         }
427 
428         pPerfData[Idx].UserTime.QuadPart = pSPI->UserTime.QuadPart;
429         pPerfData[Idx].KernelTime.QuadPart = pSPI->KernelTime.QuadPart;
430         pSPI = (PSYSTEM_PROCESS_INFORMATION)((LPBYTE)pSPI + pSPI->NextEntryOffset);
431     }
432     HeapFree(GetProcessHeap(), 0, pBuffer);
433     if (pPerfDataOld) {
434         HeapFree(GetProcessHeap(), 0, pPerfDataOld);
435     }
436     pPerfDataOld = pPerfData;
437     LeaveCriticalSection(&PerfDataCriticalSection);
438 }
439 
440 ULONG PerfDataGetProcessIndex(ULONG pid)
441 {
442     ULONG idx;
443 
444     EnterCriticalSection(&PerfDataCriticalSection);
445 
446     for (idx = 0; idx < ProcessCount; idx++)
447     {
448         if (PtrToUlong(pPerfData[idx].ProcessId) == pid)
449         {
450             break;
451         }
452     }
453 
454     LeaveCriticalSection(&PerfDataCriticalSection);
455 
456     if (idx == ProcessCount)
457     {
458         return -1;
459     }
460     return idx;
461 }
462 
463 ULONG PerfDataGetProcessCount(void)
464 {
465     ULONG Result;
466     EnterCriticalSection(&PerfDataCriticalSection);
467     Result = ProcessCount;
468     LeaveCriticalSection(&PerfDataCriticalSection);
469     return Result;
470 }
471 
472 ULONG PerfDataGetProcessorUsage(void)
473 {
474     ULONG Result;
475     EnterCriticalSection(&PerfDataCriticalSection);
476     Result = (ULONG)dbIdleTime;
477     LeaveCriticalSection(&PerfDataCriticalSection);
478     return Result;
479 }
480 
481 ULONG PerfDataGetProcessorSystemUsage(void)
482 {
483     ULONG Result;
484     EnterCriticalSection(&PerfDataCriticalSection);
485     Result = (ULONG)dbKernelTime;
486     LeaveCriticalSection(&PerfDataCriticalSection);
487     return Result;
488 }
489 
490 BOOL PerfDataGetImageName(ULONG Index, LPWSTR lpImageName, ULONG nMaxCount)
491 {
492     BOOL  bSuccessful;
493 
494     EnterCriticalSection(&PerfDataCriticalSection);
495 
496     if (Index < ProcessCount) {
497         wcsncpy(lpImageName, pPerfData[Index].ImageName, nMaxCount);
498         bSuccessful = TRUE;
499     } else {
500         bSuccessful = FALSE;
501     }
502     LeaveCriticalSection(&PerfDataCriticalSection);
503     return bSuccessful;
504 }
505 
506 ULONG PerfDataGetProcessId(ULONG Index)
507 {
508     ULONG  ProcessId;
509 
510     EnterCriticalSection(&PerfDataCriticalSection);
511 
512     if (Index < ProcessCount)
513         ProcessId = PtrToUlong(pPerfData[Index].ProcessId);
514     else
515         ProcessId = 0;
516 
517     LeaveCriticalSection(&PerfDataCriticalSection);
518 
519     return ProcessId;
520 }
521 
522 BOOL PerfDataGetUserName(ULONG Index, LPWSTR lpUserName, ULONG nMaxCount)
523 {
524     BOOL  bSuccessful;
525 
526     EnterCriticalSection(&PerfDataCriticalSection);
527 
528     if (Index < ProcessCount) {
529         wcsncpy(lpUserName, pPerfData[Index].UserName, nMaxCount);
530         bSuccessful = TRUE;
531     } else {
532         bSuccessful = FALSE;
533     }
534 
535     LeaveCriticalSection(&PerfDataCriticalSection);
536 
537     return bSuccessful;
538 }
539 
540 BOOL PerfDataGetCommandLine(ULONG Index, LPWSTR lpCommandLine, ULONG nMaxCount)
541 {
542     static const LPWSTR ellipsis = L"...";
543 
544     PROCESS_BASIC_INFORMATION pbi = {0};
545     UNICODE_STRING CommandLineStr = {0};
546 
547     PVOID ProcessParams = NULL;
548     HANDLE hProcess;
549     ULONG ProcessId;
550 
551     NTSTATUS Status;
552     BOOL result;
553 
554     PCMD_LINE_CACHE new_entry;
555     LPWSTR new_string;
556 
557     PCMD_LINE_CACHE cache = global_cache;
558 
559     /* [A] Search for a string already in cache? If so, use it */
560     while (cache && cache->pnext != NULL)
561     {
562         if (cache->idx == Index && cache->str != NULL)
563         {
564             /* Found it. Use it, and add some ellipsis at the very end to make it cute */
565             wcsncpy(lpCommandLine, cache->str, CMD_LINE_MIN(nMaxCount, cache->len));
566             wcscpy(lpCommandLine + CMD_LINE_MIN(nMaxCount, cache->len) - wcslen(ellipsis), ellipsis);
567             return TRUE;
568         }
569 
570         cache = cache->pnext;
571     }
572 
573     /* [B] We don't; let's allocate and load a value from the process mem... and cache it */
574     ProcessId = PerfDataGetProcessId(Index);
575 
576     /* Default blank command line in case things don't work out */
577     wcsncpy(lpCommandLine, L"", nMaxCount);
578 
579     /* Ask for a handle to the target process so that we can read its memory and query stuff */
580     hProcess = OpenProcess(PROCESS_QUERY_INFORMATION | PROCESS_VM_READ, FALSE, ProcessId);
581     if (!hProcess)
582         goto cleanup;
583 
584     /* First off, get the ProcessEnvironmentBlock location in that process' address space */
585     Status = NtQueryInformationProcess(hProcess, 0, &pbi, sizeof(pbi), NULL);
586     if (!NT_SUCCESS(Status))
587         goto cleanup;
588 
589     /* Then get the PEB.ProcessParameters member pointer */
590     result = ReadProcessMemory(hProcess,
591                                (PVOID)((ULONG_PTR)pbi.PebBaseAddress + FIELD_OFFSET(PEB, ProcessParameters)),
592                                &ProcessParams,
593                                sizeof(ProcessParams),
594                                NULL);
595     if (!result)
596         goto cleanup;
597 
598     /* Then copy the PEB->ProcessParameters.CommandLine member
599        to get the pointer to the string buffer and its size */
600     result = ReadProcessMemory(hProcess,
601                                (PVOID)((ULONG_PTR)ProcessParams + FIELD_OFFSET(RTL_USER_PROCESS_PARAMETERS, CommandLine)),
602                                &CommandLineStr,
603                                sizeof(CommandLineStr),
604                                NULL);
605     if (!result)
606         goto cleanup;
607 
608     /* Allocate the next cache entry and its accompanying string in one go */
609     new_entry = HeapAlloc(GetProcessHeap(),
610                           HEAP_ZERO_MEMORY,
611                           sizeof(CMD_LINE_CACHE) + CommandLineStr.Length + sizeof(UNICODE_NULL));
612     if (!new_entry)
613         goto cleanup;
614 
615     new_string = (LPWSTR)((ULONG_PTR)new_entry + sizeof(CMD_LINE_CACHE));
616 
617     /* Bingo, the command line should be stored there,
618        copy the string from the other process */
619     result = ReadProcessMemory(hProcess,
620                                CommandLineStr.Buffer,
621                                new_string,
622                                CommandLineStr.Length,
623                                NULL);
624     if (!result)
625     {
626         /* Weird, after successfully reading the mem of that process
627            various times it fails now, forget it and bail out */
628         HeapFree(GetProcessHeap(), 0, new_entry);
629         goto cleanup;
630     }
631 
632     /* Add our pointer to the cache... */
633     new_entry->idx = Index;
634     new_entry->str = new_string;
635     new_entry->len = CommandLineStr.Length;
636 
637     if (!global_cache)
638         global_cache = new_entry;
639     else
640         cache->pnext = new_entry;
641 
642     /* ... and print the buffer for the first time */
643     wcsncpy(lpCommandLine, new_string, CMD_LINE_MIN(nMaxCount, CommandLineStr.Length));
644 
645 cleanup:
646     if (hProcess) CloseHandle(hProcess);
647     return TRUE;
648 }
649 
650 void PerfDataDeallocCommandLineCache()
651 {
652     PCMD_LINE_CACHE cache = global_cache;
653     PCMD_LINE_CACHE cache_old;
654 
655     while (cache && cache->pnext != NULL)
656     {
657         cache_old = cache;
658         cache = cache->pnext;
659 
660         HeapFree(GetProcessHeap(), 0, cache_old);
661     }
662 }
663 
664 ULONG PerfDataGetSessionId(ULONG Index)
665 {
666     ULONG  SessionId;
667 
668     EnterCriticalSection(&PerfDataCriticalSection);
669 
670     if (Index < ProcessCount)
671         SessionId = pPerfData[Index].SessionId;
672     else
673         SessionId = 0;
674 
675     LeaveCriticalSection(&PerfDataCriticalSection);
676 
677     return SessionId;
678 }
679 
680 ULONG PerfDataGetCPUUsage(ULONG Index)
681 {
682     ULONG  CpuUsage;
683 
684     EnterCriticalSection(&PerfDataCriticalSection);
685 
686     if (Index < ProcessCount)
687         CpuUsage = pPerfData[Index].CPUUsage;
688     else
689         CpuUsage = 0;
690 
691     LeaveCriticalSection(&PerfDataCriticalSection);
692 
693     return CpuUsage;
694 }
695 
696 LARGE_INTEGER PerfDataGetCPUTime(ULONG Index)
697 {
698     LARGE_INTEGER  CpuTime = {{0,0}};
699 
700     EnterCriticalSection(&PerfDataCriticalSection);
701 
702     if (Index < ProcessCount)
703         CpuTime = pPerfData[Index].CPUTime;
704 
705     LeaveCriticalSection(&PerfDataCriticalSection);
706 
707     return CpuTime;
708 }
709 
710 ULONG PerfDataGetWorkingSetSizeBytes(ULONG Index)
711 {
712     ULONG  WorkingSetSizeBytes;
713 
714     EnterCriticalSection(&PerfDataCriticalSection);
715 
716     if (Index < ProcessCount)
717         WorkingSetSizeBytes = pPerfData[Index].WorkingSetSizeBytes;
718     else
719         WorkingSetSizeBytes = 0;
720 
721     LeaveCriticalSection(&PerfDataCriticalSection);
722 
723     return WorkingSetSizeBytes;
724 }
725 
726 ULONG PerfDataGetPeakWorkingSetSizeBytes(ULONG Index)
727 {
728     ULONG  PeakWorkingSetSizeBytes;
729 
730     EnterCriticalSection(&PerfDataCriticalSection);
731 
732     if (Index < ProcessCount)
733         PeakWorkingSetSizeBytes = pPerfData[Index].PeakWorkingSetSizeBytes;
734     else
735         PeakWorkingSetSizeBytes = 0;
736 
737     LeaveCriticalSection(&PerfDataCriticalSection);
738 
739     return PeakWorkingSetSizeBytes;
740 }
741 
742 ULONG PerfDataGetWorkingSetSizeDelta(ULONG Index)
743 {
744     ULONG  WorkingSetSizeDelta;
745 
746     EnterCriticalSection(&PerfDataCriticalSection);
747 
748     if (Index < ProcessCount)
749         WorkingSetSizeDelta = pPerfData[Index].WorkingSetSizeDelta;
750     else
751         WorkingSetSizeDelta = 0;
752 
753     LeaveCriticalSection(&PerfDataCriticalSection);
754 
755     return WorkingSetSizeDelta;
756 }
757 
758 ULONG PerfDataGetPageFaultCount(ULONG Index)
759 {
760     ULONG  PageFaultCount;
761 
762     EnterCriticalSection(&PerfDataCriticalSection);
763 
764     if (Index < ProcessCount)
765         PageFaultCount = pPerfData[Index].PageFaultCount;
766     else
767         PageFaultCount = 0;
768 
769     LeaveCriticalSection(&PerfDataCriticalSection);
770 
771     return PageFaultCount;
772 }
773 
774 ULONG PerfDataGetPageFaultCountDelta(ULONG Index)
775 {
776     ULONG  PageFaultCountDelta;
777 
778     EnterCriticalSection(&PerfDataCriticalSection);
779 
780     if (Index < ProcessCount)
781         PageFaultCountDelta = pPerfData[Index].PageFaultCountDelta;
782     else
783         PageFaultCountDelta = 0;
784 
785     LeaveCriticalSection(&PerfDataCriticalSection);
786 
787     return PageFaultCountDelta;
788 }
789 
790 ULONG PerfDataGetVirtualMemorySizeBytes(ULONG Index)
791 {
792     ULONG  VirtualMemorySizeBytes;
793 
794     EnterCriticalSection(&PerfDataCriticalSection);
795 
796     if (Index < ProcessCount)
797         VirtualMemorySizeBytes = pPerfData[Index].VirtualMemorySizeBytes;
798     else
799         VirtualMemorySizeBytes = 0;
800 
801     LeaveCriticalSection(&PerfDataCriticalSection);
802 
803     return VirtualMemorySizeBytes;
804 }
805 
806 ULONG PerfDataGetPagedPoolUsagePages(ULONG Index)
807 {
808     ULONG  PagedPoolUsage;
809 
810     EnterCriticalSection(&PerfDataCriticalSection);
811 
812     if (Index < ProcessCount)
813         PagedPoolUsage = pPerfData[Index].PagedPoolUsagePages;
814     else
815         PagedPoolUsage = 0;
816 
817     LeaveCriticalSection(&PerfDataCriticalSection);
818 
819     return PagedPoolUsage;
820 }
821 
822 ULONG PerfDataGetNonPagedPoolUsagePages(ULONG Index)
823 {
824     ULONG  NonPagedPoolUsage;
825 
826     EnterCriticalSection(&PerfDataCriticalSection);
827 
828     if (Index < ProcessCount)
829         NonPagedPoolUsage = pPerfData[Index].NonPagedPoolUsagePages;
830     else
831         NonPagedPoolUsage = 0;
832 
833     LeaveCriticalSection(&PerfDataCriticalSection);
834 
835     return NonPagedPoolUsage;
836 }
837 
838 ULONG PerfDataGetBasePriority(ULONG Index)
839 {
840     ULONG  BasePriority;
841 
842     EnterCriticalSection(&PerfDataCriticalSection);
843 
844     if (Index < ProcessCount)
845         BasePriority = pPerfData[Index].BasePriority;
846     else
847         BasePriority = 0;
848 
849     LeaveCriticalSection(&PerfDataCriticalSection);
850 
851     return BasePriority;
852 }
853 
854 ULONG PerfDataGetHandleCount(ULONG Index)
855 {
856     ULONG  HandleCount;
857 
858     EnterCriticalSection(&PerfDataCriticalSection);
859 
860     if (Index < ProcessCount)
861         HandleCount = pPerfData[Index].HandleCount;
862     else
863         HandleCount = 0;
864 
865     LeaveCriticalSection(&PerfDataCriticalSection);
866 
867     return HandleCount;
868 }
869 
870 ULONG PerfDataGetThreadCount(ULONG Index)
871 {
872     ULONG  ThreadCount;
873 
874     EnterCriticalSection(&PerfDataCriticalSection);
875 
876     if (Index < ProcessCount)
877         ThreadCount = pPerfData[Index].ThreadCount;
878     else
879         ThreadCount = 0;
880 
881     LeaveCriticalSection(&PerfDataCriticalSection);
882 
883     return ThreadCount;
884 }
885 
886 ULONG PerfDataGetUSERObjectCount(ULONG Index)
887 {
888     ULONG  USERObjectCount;
889 
890     EnterCriticalSection(&PerfDataCriticalSection);
891 
892     if (Index < ProcessCount)
893         USERObjectCount = pPerfData[Index].USERObjectCount;
894     else
895         USERObjectCount = 0;
896 
897     LeaveCriticalSection(&PerfDataCriticalSection);
898 
899     return USERObjectCount;
900 }
901 
902 ULONG PerfDataGetGDIObjectCount(ULONG Index)
903 {
904     ULONG  GDIObjectCount;
905 
906     EnterCriticalSection(&PerfDataCriticalSection);
907 
908     if (Index < ProcessCount)
909         GDIObjectCount = pPerfData[Index].GDIObjectCount;
910     else
911         GDIObjectCount = 0;
912 
913     LeaveCriticalSection(&PerfDataCriticalSection);
914 
915     return GDIObjectCount;
916 }
917 
918 BOOL PerfDataGetIOCounters(ULONG Index, PIO_COUNTERS pIoCounters)
919 {
920     BOOL  bSuccessful;
921 
922     EnterCriticalSection(&PerfDataCriticalSection);
923 
924     if (Index < ProcessCount)
925     {
926         memcpy(pIoCounters, &pPerfData[Index].IOCounters, sizeof(IO_COUNTERS));
927         bSuccessful = TRUE;
928     }
929     else
930         bSuccessful = FALSE;
931 
932     LeaveCriticalSection(&PerfDataCriticalSection);
933 
934     return bSuccessful;
935 }
936 
937 ULONG PerfDataGetCommitChargeTotalK(void)
938 {
939     ULONG  Total;
940     ULONG  PageSize;
941 
942     EnterCriticalSection(&PerfDataCriticalSection);
943 
944     Total = SystemPerfInfo.CommittedPages;
945     PageSize = SystemBasicInfo.PageSize;
946 
947     LeaveCriticalSection(&PerfDataCriticalSection);
948 
949     Total = Total * (PageSize / 1024);
950 
951     return Total;
952 }
953 
954 ULONG PerfDataGetCommitChargeLimitK(void)
955 {
956     ULONG  Limit;
957     ULONG  PageSize;
958 
959     EnterCriticalSection(&PerfDataCriticalSection);
960 
961     Limit = SystemPerfInfo.CommitLimit;
962     PageSize = SystemBasicInfo.PageSize;
963 
964     LeaveCriticalSection(&PerfDataCriticalSection);
965 
966     Limit = Limit * (PageSize / 1024);
967 
968     return Limit;
969 }
970 
971 ULONG PerfDataGetCommitChargePeakK(void)
972 {
973     ULONG  Peak;
974     ULONG  PageSize;
975 
976     EnterCriticalSection(&PerfDataCriticalSection);
977 
978     Peak = SystemPerfInfo.PeakCommitment;
979     PageSize = SystemBasicInfo.PageSize;
980 
981     LeaveCriticalSection(&PerfDataCriticalSection);
982 
983     Peak = Peak * (PageSize / 1024);
984 
985     return Peak;
986 }
987 
988 ULONG PerfDataGetKernelMemoryTotalK(void)
989 {
990     ULONG  Total;
991     ULONG  Paged;
992     ULONG  NonPaged;
993     ULONG  PageSize;
994 
995     EnterCriticalSection(&PerfDataCriticalSection);
996 
997     Paged = SystemPerfInfo.PagedPoolPages;
998     NonPaged = SystemPerfInfo.NonPagedPoolPages;
999     PageSize = SystemBasicInfo.PageSize;
1000 
1001     LeaveCriticalSection(&PerfDataCriticalSection);
1002 
1003     Paged = Paged * (PageSize / 1024);
1004     NonPaged = NonPaged * (PageSize / 1024);
1005 
1006     Total = Paged + NonPaged;
1007 
1008     return Total;
1009 }
1010 
1011 ULONG PerfDataGetKernelMemoryPagedK(void)
1012 {
1013     ULONG  Paged;
1014     ULONG  PageSize;
1015 
1016     EnterCriticalSection(&PerfDataCriticalSection);
1017 
1018     Paged = SystemPerfInfo.PagedPoolPages;
1019     PageSize = SystemBasicInfo.PageSize;
1020 
1021     LeaveCriticalSection(&PerfDataCriticalSection);
1022 
1023     Paged = Paged * (PageSize / 1024);
1024 
1025     return Paged;
1026 }
1027 
1028 ULONG PerfDataGetKernelMemoryNonPagedK(void)
1029 {
1030     ULONG  NonPaged;
1031     ULONG  PageSize;
1032 
1033     EnterCriticalSection(&PerfDataCriticalSection);
1034 
1035     NonPaged = SystemPerfInfo.NonPagedPoolPages;
1036     PageSize = SystemBasicInfo.PageSize;
1037 
1038     LeaveCriticalSection(&PerfDataCriticalSection);
1039 
1040     NonPaged = NonPaged * (PageSize / 1024);
1041 
1042     return NonPaged;
1043 }
1044 
1045 ULONG PerfDataGetPhysicalMemoryTotalK(void)
1046 {
1047     ULONG  Total;
1048     ULONG  PageSize;
1049 
1050     EnterCriticalSection(&PerfDataCriticalSection);
1051 
1052     Total = SystemBasicInfo.NumberOfPhysicalPages;
1053     PageSize = SystemBasicInfo.PageSize;
1054 
1055     LeaveCriticalSection(&PerfDataCriticalSection);
1056 
1057     Total = Total * (PageSize / 1024);
1058 
1059     return Total;
1060 }
1061 
1062 ULONG PerfDataGetPhysicalMemoryAvailableK(void)
1063 {
1064     ULONG  Available;
1065     ULONG  PageSize;
1066 
1067     EnterCriticalSection(&PerfDataCriticalSection);
1068 
1069     Available = SystemPerfInfo.AvailablePages;
1070     PageSize = SystemBasicInfo.PageSize;
1071 
1072     LeaveCriticalSection(&PerfDataCriticalSection);
1073 
1074     Available = Available * (PageSize / 1024);
1075 
1076     return Available;
1077 }
1078 
1079 ULONG PerfDataGetPhysicalMemorySystemCacheK(void)
1080 {
1081     ULONG  SystemCache;
1082     ULONG  PageSize;
1083 
1084     EnterCriticalSection(&PerfDataCriticalSection);
1085 
1086     PageSize = SystemBasicInfo.PageSize;
1087     SystemCache = SystemCacheInfo.CurrentSizeIncludingTransitionInPages * PageSize;
1088 
1089     LeaveCriticalSection(&PerfDataCriticalSection);
1090 
1091     return SystemCache / 1024;
1092 }
1093 
1094 ULONG PerfDataGetSystemHandleCount(void)
1095 {
1096     ULONG  HandleCount;
1097 
1098     EnterCriticalSection(&PerfDataCriticalSection);
1099 
1100     HandleCount = SystemNumberOfHandles;
1101 
1102     LeaveCriticalSection(&PerfDataCriticalSection);
1103 
1104     return HandleCount;
1105 }
1106 
1107 ULONG PerfDataGetTotalThreadCount(void)
1108 {
1109     ULONG  ThreadCount = 0;
1110     ULONG  i;
1111 
1112     EnterCriticalSection(&PerfDataCriticalSection);
1113 
1114     for (i=0; i<ProcessCount; i++)
1115     {
1116         ThreadCount += pPerfData[i].ThreadCount;
1117     }
1118 
1119     LeaveCriticalSection(&PerfDataCriticalSection);
1120 
1121     return ThreadCount;
1122 }
1123 
1124 BOOL PerfDataGet(ULONG Index, PPERFDATA *lppData)
1125 {
1126     BOOL  bSuccessful = FALSE;
1127 
1128     EnterCriticalSection(&PerfDataCriticalSection);
1129     if (Index < ProcessCount)
1130     {
1131         *lppData = pPerfData + Index;
1132         bSuccessful = TRUE;
1133     }
1134     LeaveCriticalSection(&PerfDataCriticalSection);
1135     return bSuccessful;
1136 }
1137 
1138