DotNetDupe 4.0.6
C++17/20 Implementation of the .NET Base Class Library (BCL)
Loading...
Searching...
No Matches
SystemMetrics.cpp
Go to the documentation of this file.
1#include "pch.h"
8#include <algorithm>
9#include <vector>
10#include <map>
11
12#if defined(_WIN32)
13#include <winsock2.h>
14#include <ws2tcpip.h>
15#include <windows.h>
16#include <winternl.h>
17#include <tlhelp32.h>
18#include <psapi.h>
19#include <iphlpapi.h>
20#include <pdh.h>
21#include <winsvc.h>
22#pragma comment(lib, "ws2_32.lib")
23#pragma comment(lib, "iphlpapi.lib")
24#pragma comment(lib, "psapi.lib")
25#pragma comment(lib, "pdh.lib")
26#pragma comment(lib, "advapi32.lib")
27#else
28#include <fstream>
29#include <sstream>
30#include <dirent.h>
31#include <unistd.h>
32#include <sys/statvfs.h>
33#include <sys/types.h>
34#include <arpa/inet.h>
35#endif
36
37namespace DotNetDupe {
38 namespace System {
39 namespace Diagnostics {
40
43
48
49 void SystemMetrics::EnumerateProcessesAsync(const Action<const ProcessInfo&>& fnOnProcess, const Action<>& fnOnComplete) {
53 auto spStreamer = CreateProcessStreamer(options);
54
56 spStreamer->ProcessDiscovered += [fnOnProcess](const void*, const ProcessEventArgs& e) {
57 if (fnOnProcess) fnOnProcess(e.GetProcess());
58 };
59 if (fnOnComplete) {
60 spStreamer->Completed += [fnOnComplete](const void*, const EventArgs&) {
61 fnOnComplete();
62 };
63 }
64
66 spStreamer->Start();
67 }
68
69#if defined(_WIN32)
70 struct ProcessCpuSample {
71 uint64_t uProcessTime;
72 uint64_t uSystemTime;
73 };
74 static std::map<int, ProcessCpuSample> s_mapProcessCpuSamples;
75
76 struct SystemMetricsWin32Helper {
77 static MemoryInfo GetSystemMemory() {
79 MemoryInfo info;
80 MEMORYSTATUSEX memStatus;
81 memStatus.dwLength = sizeof(MEMORYSTATUSEX);
82 if (::GlobalMemoryStatusEx(&memStatus)) {
83 info.dMemoryUsagePercent = static_cast<double>(memStatus.dwMemoryLoad);
84 info.uMemoryTotalBytes = static_cast<unsigned long long>(memStatus.ullTotalPhys);
85 info.uMemoryUsedBytes = static_cast<unsigned long long>(memStatus.ullTotalPhys - memStatus.ullAvailPhys);
86 }
87 return info;
88 }
89
90 static double CalculateCpuDelta(uint64_t uIdle, uint64_t uKernel, uint64_t uUser) {
92 static uint64_t s_uPrevIdle = 0, s_uPrevTotal = 0;
93 uint64_t uTotal = uKernel + uUser;
94 double dCpu = 0.0;
95 if (s_uPrevTotal > 0 && uTotal > s_uPrevTotal) {
96 uint64_t uTotalDiff = uTotal - s_uPrevTotal;
97 uint64_t uIdleDiff = uIdle - s_uPrevIdle;
98 dCpu = static_cast<double>((uTotalDiff - uIdleDiff) * 100.0 / uTotalDiff);
99 } else if (uTotal > 0) {
100 dCpu = static_cast<double>((uTotal - uIdle) * 100.0 / uTotal);
101 }
102 s_uPrevIdle = uIdle; s_uPrevTotal = uTotal;
103 return (dCpu < 0.0) ? 0.0 : ((dCpu > 100.0) ? 100.0 : dCpu);
104 }
105
106 static double GetSystemCpu() {
108 FILETIME ftIdle, ftKernel, ftUser;
109 if (!::GetSystemTimes(&ftIdle, &ftKernel, &ftUser)) return 0.0;
110 uint64_t uIdle = (static_cast<uint64_t>(ftIdle.dwHighDateTime) << 32) | ftIdle.dwLowDateTime;
111 uint64_t uKernel = (static_cast<uint64_t>(ftKernel.dwHighDateTime) << 32) | ftKernel.dwLowDateTime;
112 uint64_t uUser = (static_cast<uint64_t>(ftUser.dwHighDateTime) << 32) | ftUser.dwLowDateTime;
113 return CalculateCpuDelta(uIdle, uKernel, uUser);
114 }
115
116 static void InitDiskQuery(HQUERY& hQuery, HCOUNTER& hRead, HCOUNTER& hWrite) {
118 static bool s_bPdhInitialized = false;
119 if (!s_bPdhInitialized && ::PdhOpenQueryW(NULL, 0, &hQuery) == ERROR_SUCCESS) {
120 ::PdhAddEnglishCounterW(hQuery, L"\\PhysicalDisk(_Total)\\Disk Read Bytes/sec", 0, &hRead);
121 ::PdhAddEnglishCounterW(hQuery, L"\\PhysicalDisk(_Total)\\Disk Write Bytes/sec", 0, &hWrite);
122 ::PdhCollectQueryData(hQuery);
123 s_bPdhInitialized = true;
124 }
125 }
126
127 static DiskInfo GetSystemDisk() {
129 DiskInfo info;
130 static HQUERY s_hQuery = NULL; static HCOUNTER s_hRead = NULL, s_hWrite = NULL;
131 InitDiskQuery(s_hQuery, s_hRead, s_hWrite);
132 if (s_hQuery && ::PdhCollectQueryData(s_hQuery) == ERROR_SUCCESS) {
133 PDH_FMT_COUNTERVALUE fmtRead, fmtWrite;
134 if (::PdhGetFormattedCounterValue(s_hRead, PDH_FMT_LARGE, NULL, &fmtRead) == ERROR_SUCCESS) info.lDiskReadBytes = static_cast<long long>(fmtRead.largeValue);
135 if (::PdhGetFormattedCounterValue(s_hWrite, PDH_FMT_LARGE, NULL, &fmtWrite) == ERROR_SUCCESS) info.lDiskWriteBytes = static_cast<long long>(fmtWrite.largeValue);
136 }
137 return info;
138 }
139
140 static double CalculateNetRate(uint64_t totalOctets) {
142 static uint64_t s_uPrevOctets = 0; static DWORD s_dwPrevTick = 0;
143 DWORD dwNow = ::GetTickCount(); double dMbps = 0.0;
144 if (s_dwPrevTick > 0 && dwNow > s_dwPrevTick && totalOctets >= s_uPrevOctets) {
145 double dElapsedSec = (dwNow - s_dwPrevTick) / 1000.0;
146 dMbps = ((totalOctets - s_uPrevOctets) / dElapsedSec * 8.0) / 1000000.0;
147 }
148 s_uPrevOctets = totalOctets; s_dwPrevTick = dwNow;
149 return dMbps;
150 }
151
152 static double GetSystemNetwork() {
154 DWORD dwSize = 0;
155 if (::GetIfTable(NULL, &dwSize, FALSE) != ERROR_INSUFFICIENT_BUFFER) return 0.0;
156 std::vector<uint8_t> buf(dwSize, 0);
157 MIB_IFTABLE* pTable = reinterpret_cast<MIB_IFTABLE*>(buf.data());
158 if (::GetIfTable(pTable, &dwSize, FALSE) != NO_ERROR) return 0.0;
159 uint64_t totalOctets = 0;
160 for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
161 if (pTable->table[i].dwType != IF_TYPE_SOFTWARE_LOOPBACK && pTable->table[i].dwOperStatus == IF_OPER_STATUS_OPERATIONAL)
162 totalOctets += pTable->table[i].dwInOctets + pTable->table[i].dwOutOctets;
163 }
164 return CalculateNetRate(totalOctets);
165 }
166
167 static void* OpenProcByName(const String& sProcessName, unsigned long dwAccess, int& iOutPid) {
169 iOutPid = -1;
170 auto arrMatches = Process::GetProcessesByName(sProcessName);
171 if (arrMatches.GetLength() == 0) return NULL;
172 iOutPid = arrMatches[0]->GetId();
173 return ::OpenProcess(dwAccess, FALSE, static_cast<DWORD>(iOutPid));
174 }
175
176 static String ReadPebCommandLine(HANDLE hProc, PVOID pebBase) {
178 PEB peb; SIZE_T bytesRead = 0;
179 if (!::ReadProcessMemory(hProc, pebBase, &peb, sizeof(peb), &bytesRead)) return String("");
180 RTL_USER_PROCESS_PARAMETERS upp;
181 if (!::ReadProcessMemory(hProc, peb.ProcessParameters, &upp, sizeof(upp), &bytesRead) || !upp.CommandLine.Buffer || upp.CommandLine.Length == 0) return String("");
182 std::vector<wchar_t> wCmd(upp.CommandLine.Length / sizeof(wchar_t) + 1, 0);
183 if (::ReadProcessMemory(hProc, upp.CommandLine.Buffer, wCmd.data(), upp.CommandLine.Length, &bytesRead)) return String(wCmd.data());
184 return String("");
185 }
186
187 static String ReadProcCmdLine(void* hProc) {
189 typedef NTSTATUS(NTAPI* pfnNtQuery)(HANDLE, ULONG, PVOID, ULONG, PULONG);
190 HMODULE hNtDll = ::GetModuleHandleW(L"ntdll.dll");
191 pfnNtQuery fnNtQuery = hNtDll ? (pfnNtQuery)::GetProcAddress(hNtDll, "NtQueryInformationProcess") : NULL;
192 if (!fnNtQuery || !hProc) return String("");
193 PROCESS_BASIC_INFORMATION pbi; DWORD dwLen = 0;
194 if (fnNtQuery(static_cast<HANDLE>(hProc), 0, &pbi, sizeof(pbi), &dwLen) == 0 && pbi.PebBaseAddress) {
195 return ReadPebCommandLine(static_cast<HANDLE>(hProc), pbi.PebBaseAddress);
196 }
197 return String("");
198 }
199
200 static MemoryInfo ReadProcMemory(void* hProc) {
202 MemoryInfo info;
203 if (hProc) {
204 PROCESS_MEMORY_COUNTERS_EX pmc;
205 if (::GetProcessMemoryInfo(static_cast<HANDLE>(hProc), reinterpret_cast<PROCESS_MEMORY_COUNTERS*>(&pmc), sizeof(pmc))) {
206 info.lPhysicalMemoryBytes = static_cast<long long>(pmc.WorkingSetSize);
207 info.lPrivateBytes = static_cast<long long>(pmc.PrivateUsage);
208 }
209 }
210 return info;
211 }
212
213 static void QueryPdhIoCounters(const std::wstring& wProcName, DiskInfo& info) {
215 std::wstring rPath = L"\\Process(" + wProcName + L")\\IO Read Bytes/sec";
216 std::wstring wPath = L"\\Process(" + wProcName + L")\\IO Write Bytes/sec";
217 HQUERY hQuery = NULL; HCOUNTER hRead = NULL, hWrite = NULL;
218 if (::PdhOpenQueryW(NULL, 0, &hQuery) != ERROR_SUCCESS) return;
219 bool rOk = (::PdhAddEnglishCounterW(hQuery, rPath.c_str(), 0, &hRead) == ERROR_SUCCESS);
220 bool wOk = (::PdhAddEnglishCounterW(hQuery, wPath.c_str(), 0, &hWrite) == ERROR_SUCCESS);
221 if (::PdhCollectQueryData(hQuery) == ERROR_SUCCESS) {
222 PDH_FMT_COUNTERVALUE fmtR, fmtW;
223 if (rOk && ::PdhGetFormattedCounterValue(hRead, PDH_FMT_LARGE, NULL, &fmtR) == ERROR_SUCCESS) info.lDiskReadBytes = static_cast<long long>(fmtR.largeValue);
224 if (wOk && ::PdhGetFormattedCounterValue(hWrite, PDH_FMT_LARGE, NULL, &fmtW) == ERROR_SUCCESS) info.lDiskWriteBytes = static_cast<long long>(fmtW.largeValue);
225 }
226 ::PdhCloseQuery(hQuery);
227 }
228
229 static DiskInfo ReadProcDisk(void* hProc, const String& sProcessName) {
231 DiskInfo info;
232 std::string sStd(sProcessName.GetRawString() ? sProcessName.GetRawString() : "");
233 std::wstring wProc(sStd.begin(), sStd.end());
234 size_t pos = wProc.rfind(L'.'); if (pos != std::wstring::npos) wProc = wProc.substr(0, pos);
235 QueryPdhIoCounters(wProc, info);
236 if ((info.lDiskReadBytes == -1 || info.lDiskWriteBytes == -1) && hProc) {
237 IO_COUNTERS io;
238 if (::GetProcessIoCounters(static_cast<HANDLE>(hProc), &io)) {
239 if (info.lDiskReadBytes == -1) info.lDiskReadBytes = static_cast<long long>(io.ReadTransferCount);
240 if (info.lDiskWriteBytes == -1) info.lDiskWriteBytes = static_cast<long long>(io.WriteTransferCount);
241 }
242 }
243 return info;
244 }
245
246 static NetworkUsageInfo ReadProcNetwork(void* hProc, const String& sProcessName) {
248 NetworkUsageInfo info;
249 std::string sStd(sProcessName.GetRawString() ? sProcessName.GetRawString() : "");
250 std::wstring wProc(sStd.begin(), sStd.end());
251 size_t pos = wProc.rfind(L'.'); if (pos != std::wstring::npos) wProc = wProc.substr(0, pos);
252 DiskInfo di;
253 QueryPdhIoCounters(wProc, di);
256 return info;
257 }
258
259 static void ReadUdpPorts(int iPid, Collections::Generic::List<int>& lst) {
261 if (iPid <= 0) return;
262 DWORD dwSize = 0;
263 if (::GetExtendedUdpTable(NULL, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) != ERROR_INSUFFICIENT_BUFFER) return;
264 std::vector<uint8_t> buf(dwSize, 0);
265 MIB_UDPTABLE_OWNER_PID* pTable = reinterpret_cast<MIB_UDPTABLE_OWNER_PID*>(buf.data());
266 if (::GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) != NO_ERROR) return;
267 for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
268 if (pTable->table[i].dwOwningPid == static_cast<DWORD>(iPid)) {
269 int p = ntohs(static_cast<u_short>(pTable->table[i].dwLocalPort));
270 if (!lst.Contains(p)) lst.Add(p);
271 }
272 }
273 }
274
275 static Collections::Generic::List<int> ReadProcPorts(int iPid) {
278 if (iPid <= 0) return lst;
279 DWORD dwSize = 0;
280 if (::GetExtendedTcpTable(NULL, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == ERROR_INSUFFICIENT_BUFFER) {
281 std::vector<uint8_t> buf(dwSize, 0);
282 MIB_TCPTABLE_OWNER_PID* pTable = reinterpret_cast<MIB_TCPTABLE_OWNER_PID*>(buf.data());
283 if (::GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == NO_ERROR) {
284 for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
285 if (pTable->table[i].dwOwningPid == static_cast<DWORD>(iPid) && pTable->table[i].dwState == MIB_TCP_STATE_LISTEN) {
286 int p = ntohs(static_cast<u_short>(pTable->table[i].dwLocalPort));
287 if (!lst.Contains(p)) lst.Add(p);
288 }
289 }
290 }
291 }
292 ReadUdpPorts(iPid, lst);
293 return lst;
294 }
295
296 static void ExtractTcpConnection(MIB_TCPROW_OWNER_PID& row, NetworkConnectionInfo& conn) {
298 in_addr lAddr, rAddr;
299 lAddr.S_un.S_addr = row.dwLocalAddr; rAddr.S_un.S_addr = row.dwRemoteAddr;
300 char szL[INET_ADDRSTRLEN] = { 0 }, szR[INET_ADDRSTRLEN] = { 0 };
301 ::inet_ntop(AF_INET, &lAddr, szL, sizeof(szL));
302 ::inet_ntop(AF_INET, &rAddr, szR, sizeof(szR));
303 conn.sLocalAddress = String(szL); conn.iLocalPort = ntohs(static_cast<u_short>(row.dwLocalPort));
304 conn.sRemoteAddress = String(szR); conn.iRemotePort = ntohs(static_cast<u_short>(row.dwRemotePort));
305 conn.sState = (row.dwState == MIB_TCP_STATE_LISTEN) ? "LISTEN" : ((row.dwState == MIB_TCP_STATE_ESTAB) ? "ESTABLISHED" : "OTHER");
306 }
307
308 static void ReadUdpConnections(int iPid, ProcessNetworkConnectionInfo& info) {
310 if (iPid <= 0) return;
311 DWORD dwSize = 0;
312 if (::GetExtendedUdpTable(NULL, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) != ERROR_INSUFFICIENT_BUFFER) return;
313 std::vector<uint8_t> buf(dwSize, 0);
314 MIB_UDPTABLE_OWNER_PID* pTable = reinterpret_cast<MIB_UDPTABLE_OWNER_PID*>(buf.data());
315 if (::GetExtendedUdpTable(pTable, &dwSize, FALSE, AF_INET, UDP_TABLE_OWNER_PID, 0) != NO_ERROR) return;
316 for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
317 if (pTable->table[i].dwOwningPid == static_cast<DWORD>(iPid)) {
318 int p = ntohs(static_cast<u_short>(pTable->table[i].dwLocalPort));
319 if (!info.lstOpenPorts.Contains(p)) info.lstOpenPorts.Add(p);
320 NetworkConnectionInfo conn; conn.iLocalPort = p; conn.sState = "UDP";
321 in_addr lAddr; lAddr.S_un.S_addr = pTable->table[i].dwLocalAddr;
322 char szL[INET_ADDRSTRLEN] = { 0 };
323 ::inet_ntop(AF_INET, &lAddr, szL, sizeof(szL));
324 conn.sLocalAddress = String(szL);
325 info.lstConnections.Add(conn);
326 }
327 }
328 }
329
330 static ProcessNetworkConnectionInfo ReadProcNetInfo(int iPid) {
333 if (iPid <= 0) return info;
334 DWORD dwSize = 0;
335 if (::GetExtendedTcpTable(NULL, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == ERROR_INSUFFICIENT_BUFFER) {
336 std::vector<uint8_t> buf(dwSize, 0);
337 MIB_TCPTABLE_OWNER_PID* pTable = reinterpret_cast<MIB_TCPTABLE_OWNER_PID*>(buf.data());
338 if (::GetExtendedTcpTable(pTable, &dwSize, FALSE, AF_INET, TCP_TABLE_OWNER_PID_ALL, 0) == NO_ERROR) {
339 for (DWORD i = 0; i < pTable->dwNumEntries; ++i) {
340 if (pTable->table[i].dwOwningPid == static_cast<DWORD>(iPid)) {
342 ExtractTcpConnection(pTable->table[i], conn);
343 info.lstConnections.Add(conn);
344 if (pTable->table[i].dwState == MIB_TCP_STATE_LISTEN && !info.lstOpenPorts.Contains(conn.iLocalPort)) info.lstOpenPorts.Add(conn.iLocalPort);
345 else if (pTable->table[i].dwState == MIB_TCP_STATE_ESTAB) info.bHasEstablishedInboundConnection = true;
346 }
347 }
348 }
349 }
350 ReadUdpConnections(iPid, info);
351 return info;
352 }
353
354 static void CalculateProcessCpu(HANDLE hProc, int iPid, double& dCpu) {
356 FILETIME ftCreate, ftExit, ftKernel, ftUser, ftSysIdle, ftSysKernel, ftSysUser, ftNow;
357 if (!::GetProcessTimes(hProc, &ftCreate, &ftExit, &ftKernel, &ftUser) || !::GetSystemTimes(&ftSysIdle, &ftSysKernel, &ftSysUser)) return;
358 uint64_t uProc = ((static_cast<uint64_t>(ftKernel.dwHighDateTime) << 32) | ftKernel.dwLowDateTime) + ((static_cast<uint64_t>(ftUser.dwHighDateTime) << 32) | ftUser.dwLowDateTime);
359 uint64_t uSys = ((static_cast<uint64_t>(ftSysKernel.dwHighDateTime) << 32) | ftSysKernel.dwLowDateTime) + ((static_cast<uint64_t>(ftSysUser.dwHighDateTime) << 32) | ftSysUser.dwLowDateTime);
360 auto it = s_mapProcessCpuSamples.find(iPid);
361 if (it != s_mapProcessCpuSamples.end() && uSys > it->second.uSystemTime && uProc >= it->second.uProcessTime) {
362 double c = (static_cast<double>(uProc - it->second.uProcessTime) * 100.0 / static_cast<double>(uSys - it->second.uSystemTime));
363 dCpu = (c > 100.0) ? 100.0 : ((c < 0.0) ? 0.0 : c);
364 } else {
365 ::GetSystemTimeAsFileTime(&ftNow); SYSTEM_INFO si; ::GetSystemInfo(&si);
366 uint64_t uNow = (static_cast<uint64_t>(ftNow.dwHighDateTime) << 32) | ftNow.dwLowDateTime; uint64_t uCreate = (static_cast<uint64_t>(ftCreate.dwHighDateTime) << 32) | ftCreate.dwLowDateTime;
367 if (uNow > uCreate && uProc > 0) { double c = (static_cast<double>(uProc) * 100.0) / (static_cast<double>(uNow - uCreate) * static_cast<double>(si.dwNumberOfProcessors > 0 ? si.dwNumberOfProcessors : 1)); dCpu = (c > 100.0) ? 100.0 : ((c < 0.0) ? 0.0 : c); }
368 }
369 s_mapProcessCpuSamples[iPid] = { uProc, uSys };
370 }
371
372 static void PopulateProcNetwork(int iPid, ProcessInfo& proc) {
374 auto netInfo = ReadProcNetInfo(iPid);
375 proc.lstOpenPorts = netInfo.lstOpenPorts;
376 proc.lstConnections = netInfo.lstConnections;
377 proc.bHasEstablishedConnection = netInfo.bHasEstablishedInboundConnection;
378 }
379
380 static String GetServiceStartType(SC_HANDLE hSCM, LPCWSTR lpServiceName) {
382 SC_HANDLE hService = ::OpenServiceW(hSCM, lpServiceName, SERVICE_QUERY_CONFIG);
383 if (!hService) return "Manual";
384 BYTE buffer[1024]; DWORD dwNeeded = 0;
385 QUERY_SERVICE_CONFIGW* pConfig = reinterpret_cast<QUERY_SERVICE_CONFIGW*>(buffer);
386 String sType = "Manual";
387 if (::QueryServiceConfigW(hService, pConfig, sizeof(buffer), &dwNeeded)) {
388 switch (pConfig->dwStartType) {
389 case SERVICE_AUTO_START: sType = "Automatic"; break;
390 case SERVICE_DEMAND_START: sType = "Manual"; break;
391 case SERVICE_DISABLED: sType = "Disabled"; break;
392 case SERVICE_BOOT_START: sType = "Boot"; break;
393 case SERVICE_SYSTEM_START: sType = "System"; break;
394 default: sType = "Manual"; break;
395 }
396 }
397 ::CloseServiceHandle(hService);
398 return sType;
399 }
400
401 static void ParseServiceStatus(ENUM_SERVICE_STATUS_PROCESSW& svc, SC_HANDLE hSCM, ServiceInfo& info) {
403 info.sServiceName = String(svc.lpServiceName); info.sDisplayName = String(svc.lpDisplayName);
404 info.iProcessId = static_cast<int>(svc.ServiceStatusProcess.dwProcessId);
405 switch (svc.ServiceStatusProcess.dwCurrentState) {
406 case SERVICE_RUNNING: info.sStatus = "Running"; break;
407 case SERVICE_STOPPED: info.sStatus = "Stopped"; break;
408 case SERVICE_PAUSED: info.sStatus = "Paused"; break;
409 case SERVICE_START_PENDING: info.sStatus = "StartPending"; break;
410 case SERVICE_STOP_PENDING: info.sStatus = "StopPending"; break;
411 default: info.sStatus = "Unknown"; break;
412 }
413 info.sStartType = GetServiceStartType(hSCM, svc.lpServiceName);
414 }
415 };
416
417 MemoryInfo SystemMetrics::GetSystemMemoryUsage() { return SystemMetricsWin32Helper::GetSystemMemory(); }
418 double SystemMetrics::GetSystemCpuUsage() { return SystemMetricsWin32Helper::GetSystemCpu(); }
419 DiskInfo SystemMetrics::GetSystemDiskUsage() { return SystemMetricsWin32Helper::GetSystemDisk(); }
420 double SystemMetrics::GetSystemNetworkUsage() { return SystemMetricsWin32Helper::GetSystemNetwork(); }
421
422 void* SystemMetrics::OpenProcessByName(const String& sProcessName, unsigned long dwDesiredAccess, int& iOutProcessId) {
423 return SystemMetricsWin32Helper::OpenProcByName(sProcessName, dwDesiredAccess, iOutProcessId);
424 }
425
426 String SystemMetrics::ReadProcessCommandLineHandle(void* hProc) {
427 return SystemMetricsWin32Helper::ReadProcCmdLine(hProc);
428 }
429
432 int iPid = -1;
433 HANDLE hProc = static_cast<HANDLE>(OpenProcessByName(sProcessName, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, iPid));
434 if (!hProc) return String("");
435 String sCmd = ReadProcessCommandLineHandle(hProc);
436 if (sCmd.IsEmpty()) {
437 WCHAR szPath[MAX_PATH] = { 0 }; DWORD dwLen = MAX_PATH;
438 if (::QueryFullProcessImageNameW(hProc, 0, szPath, &dwLen)) sCmd = String(szPath);
439 }
440 ::CloseHandle(hProc);
441 return sCmd;
442 }
443
444 MemoryInfo SystemMetrics::ReadProcessMemoryHandle(void* hProc) { return SystemMetricsWin32Helper::ReadProcMemory(hProc); }
447 int iPid = -1; HANDLE hProc = static_cast<HANDLE>(OpenProcessByName(sProcessName, PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, iPid));
448 if (!hProc) return MemoryInfo();
449 MemoryInfo mem = ReadProcessMemoryHandle(hProc);
450 ::CloseHandle(hProc);
451 return mem;
452 }
453
454 DiskInfo SystemMetrics::ReadProcessDiskHandle(void* hProc, const String& sProcessName) { return SystemMetricsWin32Helper::ReadProcDisk(hProc, sProcessName); }
457 int iPid = -1; HANDLE hProc = static_cast<HANDLE>(OpenProcessByName(sProcessName, PROCESS_QUERY_LIMITED_INFORMATION, iPid));
458 DiskInfo di = ReadProcessDiskHandle(hProc, sProcessName);
459 if (hProc) ::CloseHandle(hProc);
460 return di;
461 }
462
463 NetworkUsageInfo SystemMetrics::ReadProcessNetworkHandle(void* hProc, const String& sProcessName) { return SystemMetricsWin32Helper::ReadProcNetwork(hProc, sProcessName); }
466 int iPid = -1; HANDLE hProc = static_cast<HANDLE>(OpenProcessByName(sProcessName, PROCESS_QUERY_LIMITED_INFORMATION, iPid));
467 NetworkUsageInfo net = ReadProcessNetworkHandle(hProc, sProcessName);
468 if (hProc) ::CloseHandle(hProc);
469 return net;
470 }
471
472 Collections::Generic::List<int> SystemMetrics::ReadProcessNetworkPortInternal(int iProcessId) { return SystemMetricsWin32Helper::ReadProcPorts(iProcessId); }
474 int iPid = -1; HANDLE hProc = static_cast<HANDLE>(OpenProcessByName(sProcessName, PROCESS_QUERY_LIMITED_INFORMATION, iPid));
475 if (hProc) ::CloseHandle(hProc);
476 return ReadProcessNetworkPortInternal(iPid);
477 }
479 return ReadProcessNetworkPortInternal(iProcessId);
480 }
481
482 ProcessNetworkConnectionInfo SystemMetrics::ReadProcessNetworkInfoInternal(int iProcessId) { return SystemMetricsWin32Helper::ReadProcNetInfo(iProcessId); }
484 int iPid = -1; HANDLE hProc = static_cast<HANDLE>(OpenProcessByName(sProcessName, PROCESS_QUERY_LIMITED_INFORMATION, iPid));
485 if (hProc) ::CloseHandle(hProc);
486 return ReadProcessNetworkInfoInternal(iPid);
487 }
489 return ReadProcessNetworkInfoInternal(iProcessId);
490 }
491
492 void SystemMetrics::EnrichProcessInfo(ProcessInfo& proc, bool bIncludeNetwork) {
494 if (proc.iProcessId <= 0) throw ArgumentException("Process ID must be greater than zero.");
495
497 HANDLE hProc = ::OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_VM_READ, FALSE, proc.iProcessId);
498 if (hProc) {
499 if (proc.sPath.IsEmpty()) {
500 WCHAR szPath[MAX_PATH] = { 0 }; DWORD dwLen = MAX_PATH;
501 if (::QueryFullProcessImageNameW(hProc, 0, szPath, &dwLen)) proc.sPath = String(szPath);
502 }
503 proc.sCommandLine = SystemMetricsWin32Helper::ReadProcCmdLine(hProc);
504 if (proc.sCommandLine.IsEmpty()) proc.sCommandLine = proc.sPath;
505 proc.memory = SystemMetricsWin32Helper::ReadProcMemory(hProc);
506 proc.disk = SystemMetricsWin32Helper::ReadProcDisk(hProc, proc.sName);
507 proc.network = SystemMetricsWin32Helper::ReadProcNetwork(hProc, proc.sName);
508 SystemMetricsWin32Helper::CalculateProcessCpu(hProc, proc.iProcessId, proc.dCpuUsagePercent);
509 ::CloseHandle(hProc);
510 }
511
513 if (bIncludeNetwork) SystemMetricsWin32Helper::PopulateProcNetwork(proc.iProcessId, proc);
514 }
515
519 SC_HANDLE hSCM = ::OpenSCManagerW(NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE);
520 if (!hSCM) return lst;
521 DWORD dwNeeded = 0, dwReturned = 0, dwResume = 0;
522 ::EnumServicesStatusExW(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, NULL, 0, &dwNeeded, &dwReturned, &dwResume, NULL);
523 if (dwNeeded > 0) {
524 std::vector<uint8_t> buf(dwNeeded, 0);
525 ENUM_SERVICE_STATUS_PROCESSW* pSvcs = reinterpret_cast<ENUM_SERVICE_STATUS_PROCESSW*>(buf.data());
526 if (::EnumServicesStatusExW(hSCM, SC_ENUM_PROCESS_INFO, SERVICE_WIN32, SERVICE_STATE_ALL, buf.data(), dwNeeded, &dwNeeded, &dwReturned, &dwResume, NULL)) {
527 for (DWORD i = 0; i < dwReturned; ++i) {
528 ServiceInfo svc; SystemMetricsWin32Helper::ParseServiceStatus(pSvcs[i], hSCM, svc);
529 lst.Add(svc);
530 }
531 }
532 }
533 ::CloseServiceHandle(hSCM);
534 return lst;
535 }
536#endif
537
538 }
539 }
540}
Defines the exception thrown when an invalid argument is provided to a method.
Provides access to local and remote processes and enables you to start and stop local system processe...
Utility routines for high-performance UTF-8, UTF-16, and wide-character string conversions.
Serves as the base class for system exceptions across the library.
Encapsulates a method that has parameters and does not return a value.
Definition Action.h:46
ArgumentException(const String &sMessage)
Initializes a new instance of the ArgumentException class with a specified error message.
Represents a strongly typed list of objects accessible by index.
Definition List.h:29
bool Contains(const T &item) const
Determines whether an element is in the List.
Definition List.h:170
void Add(const T &item)
Adds an object to the end of the List.
Definition List.h:138
Event arguments containing a single process snapshot.
static Array< SmartPointer< Process > > GetProcessesByName(const String &sProcessName)
Creates an array of new Process components and associates them with all processes sharing the specifi...
Definition Process.cpp:420
static String GetProcessCommandLine(const String &sProcessName)
Queries the full command line used to launch a process by name.
static void EnrichProcessInfo(ProcessInfo &proc, bool bIncludeNetwork=true)
Enriches an existing ProcessInfo instance with CPU, memory, disk, and network telemetry.
static Collections::Generic::List< int > GetProcessNetworkPort(const String &sProcessName)
Retrieves open listening ports bound by a process by name.
static SmartPointer< ProcessStreamer > CreateProcessStreamer(const ProcessStreamOptions &options=ProcessStreamOptions())
Creates a background process streaming engine configured with options.
static DiskInfo GetProcessDiskUsage(const String &sProcessName)
Queries disk transfer throughput for a process by name.
static MemoryInfo GetProcessMemoryUsage(const String &sProcessName)
Queries memory allocation details for a process by name.
static Collections::Generic::List< ServiceInfo > GetAllServices()
Enumerates all registered system background services and their current execution status.
SystemMetrics()
Initializes a new instance of SystemMetrics.
static MemoryInfo GetSystemMemoryUsage()
Retrieves total and consumed physical memory metrics for the entire machine.
static double GetSystemCpuUsage()
Computes total system CPU usage percentage across all processors.
static double GetSystemNetworkUsage()
Computes total network traffic throughput across all operational interfaces in Megabits per second.
static NetworkUsageInfo GetProcessNetworkUsage(const String &sProcessName)
Queries network transfer metrics for a process by name.
static ProcessNetworkConnectionInfo GetProcessNetworkInfo(const String &sProcessName)
Queries detailed socket connections for a process by name.
static void EnumerateProcessesAsync(const Action< const ProcessInfo & > &fnOnProcess, const Action<> &fnOnComplete=nullptr)
Asynchronously discovers processes on the host using fast discovery mode.
static DiskInfo GetSystemDiskUsage()
Retrieves total disk read and write transfer rates for the system.
Represents the base class for classes that contain event data, and provides a value to use for events...
Definition EventArgs.h:17
A unified smart pointer that supports both unique and shared ownership semantics.
static SmartPointer< T > NewShared()
Creates a Shared SmartPointer, default constructing T.
Represents text as a sequence of UTF-8 code units with culture-invariant operations.
Definition String.h:74
String()
Initializes a new instance of the String class to an empty string.
Definition String.cpp:64
const char * GetRawString() const
Definition String.cpp:230
@ FastDiscoveryOnly
Minimal discovery retrieving PID, name, and paths without deep inspection.
static std::map< int, ProcessCpuSample > s_mapProcessCpuSamples
Rate or aggregate count of disk read and write operations.
long long lDiskWriteBytes
Disk write throughput in bytes per second or total transfer count.
long long lDiskReadBytes
Disk read throughput in bytes per second or total transfer count.
Snapshot of physical and virtual memory utilization for the system or a specific process.
MemoryInfo()
Default constructor initializing default counters.
long long lPhysicalMemoryBytes
Working set size (physical RAM resident) for the target process.
unsigned long long uMemoryTotalBytes
Total physical RAM installed on the host in bytes.
double dMemoryUsagePercent
Percentage of total physical memory currently in use (0.0 - 100.0).
long long lPrivateBytes
Private bytes allocated by the target process (-1 if unavailable).
unsigned long long uMemoryUsedBytes
Currently consumed physical RAM across all processes.
Telemetry describing an active TCP or UDP socket endpoint.
String sState
Socket connection state (e.g. "ESTABLISHED", "LISTEN", "TIME_WAIT").
String sLocalAddress
Local IPv4 or IPv6 IP address.
Network throughput counters for bytes transmitted and received.
long long lNetworkReadBytes
Incoming network throughput in bytes or octets.
long long lNetworkWriteBytes
Outgoing network throughput in bytes or octets.
Comprehensive telemetry snapshot for an operating system process.
double dCpuUsagePercent
Process CPU consumption percentage (0.0 - 100.0).
String sName
Process executable name without path.
Collections::Generic::List< int > lstOpenPorts
Open listening ports.
int iProcessId
Operating system process identifier (PID).
NetworkUsageInfo network
Network traffic metrics.
String sCommandLine
Full command-line arguments used to spawn the process.
bool bHasEstablishedConnection
True if any connection is established.
String sPath
Full filesystem path to the executable image.
MemoryInfo memory
Memory allocation statistics.
Collections::Generic::List< NetworkConnectionInfo > lstConnections
Active socket connections.
Collection of open ports and active connections attributed to a specific process.
Collections::Generic::List< NetworkConnectionInfo > lstConnections
Detailed active socket connections.
bool bHasEstablishedInboundConnection
True if process possesses at least one established connection.
Collections::Generic::List< int > lstOpenPorts
List of TCP/UDP ports bound or in LISTEN state.
Configuration options controlling the execution of a ProcessStreamer.
ProcessMetricsDetail eDetailLevel
Depth of telemetry gathered for each process.
Telemetry snapshot for a system background service or daemon.
int iProcessId
Process ID hosting the service (0 if stopped).
String sDisplayName
Human-readable friendly display name.
String sStartType
Startup behavior (e.g. "Automatic", "Manual", "Disabled").
String sStatus
Current service state (e.g. "Running", "Stopped", "Paused").
String sServiceName
Internal service key identifier.