// // WindowSystemInfo.h // interface/src // // Split from Application.cpp by HifiExperiments on 3/30/24 // Created by Andrzej Kapolka on 5/10/13. // Copyright 2013 High Fidelity, Inc. // Copyright 2020 Vircadia contributors. // Copyright 2022-2023 Overte e.V. // // Distributed under the Apache License, Version 2.0. // See the accompanying file LICENSE or http://www.apache.org/licenses/LICENSE-2.0.html // SPDX-License-Identifier: Apache-2.0 // #ifndef hifi_WindowSystemInfo_h #define hifi_WindowSystemInfo_h #include #ifdef Q_OS_WIN #include #include #include #pragma comment(lib, "pdh.lib") #pragma comment(lib, "ntdll.lib") #include #include #include #include #include #include #include "Profile.h" #include "ThreadHelpers.h" extern "C" { enum SYSTEM_INFORMATION_CLASS { SystemBasicInformation = 0, SystemProcessorPerformanceInformation = 8, }; struct SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { LARGE_INTEGER IdleTime; LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER DpcTime; LARGE_INTEGER InterruptTime; ULONG InterruptCount; }; struct SYSTEM_BASIC_INFORMATION { ULONG Reserved; ULONG TimerResolution; ULONG PageSize; ULONG NumberOfPhysicalPages; ULONG LowestPhysicalPageNumber; ULONG HighestPhysicalPageNumber; ULONG AllocationGranularity; ULONG_PTR MinimumUserModeAddress; ULONG_PTR MaximumUserModeAddress; ULONG_PTR ActiveProcessorsAffinityMask; CCHAR NumberOfProcessors; }; NTSYSCALLAPI LONG NTAPI NtQuerySystemInformation( _In_ SYSTEM_INFORMATION_CLASS SystemInformationClass, _Out_writes_bytes_opt_(SystemInformationLength) PVOID SystemInformation, _In_ ULONG SystemInformationLength, _Out_opt_ PULONG ReturnLength ); } template LONG NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, T& t) { return NtQuerySystemInformation(SystemInformationClass, &t, (ULONG)sizeof(T), nullptr); } template LONG NtQuerySystemInformation(SYSTEM_INFORMATION_CLASS SystemInformationClass, std::vector& t) { return NtQuerySystemInformation(SystemInformationClass, t.data(), (ULONG)(sizeof(T) * t.size()), nullptr); } template void updateValueAndDelta(std::pair& pair, T newValue) { auto& value = pair.first; auto& delta = pair.second; delta = (value != 0) ? newValue - value : 0; value = newValue; } struct MyCpuInfo { using ValueAndDelta = std::pair; std::string name; ValueAndDelta kernel { 0, 0 }; ValueAndDelta user { 0, 0 }; ValueAndDelta idle { 0, 0 }; float kernelUsage { 0.0f }; float userUsage { 0.0f }; void update(const SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION& cpuInfo) { updateValueAndDelta(kernel, cpuInfo.KernelTime.QuadPart); updateValueAndDelta(user, cpuInfo.UserTime.QuadPart); updateValueAndDelta(idle, cpuInfo.IdleTime.QuadPart); auto totalTime = kernel.second + user.second + idle.second; if (totalTime != 0) { kernelUsage = (FLOAT)kernel.second / totalTime; userUsage = (FLOAT)user.second / totalTime; } else { kernelUsage = userUsage = 0.0f; } } }; void updateCpuInformation() { static std::once_flag once; static SYSTEM_BASIC_INFORMATION systemInfo {}; static SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION cpuTotals; static std::vector cpuInfos; static std::vector myCpuInfos; static MyCpuInfo myCpuTotals; std::call_once(once, [&] { NtQuerySystemInformation( SystemBasicInformation, systemInfo); cpuInfos.resize(systemInfo.NumberOfProcessors); myCpuInfos.resize(systemInfo.NumberOfProcessors); for (size_t i = 0; i < systemInfo.NumberOfProcessors; ++i) { myCpuInfos[i].name = "cpu." + std::to_string(i); } myCpuTotals.name = "cpu.total"; }); NtQuerySystemInformation(SystemProcessorPerformanceInformation, cpuInfos); // Zero the CPU totals. memset(&cpuTotals, 0, sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION)); for (size_t i = 0; i < systemInfo.NumberOfProcessors; ++i) { auto& cpuInfo = cpuInfos[i]; // KernelTime includes IdleTime. cpuInfo.KernelTime.QuadPart -= cpuInfo.IdleTime.QuadPart; // Update totals cpuTotals.IdleTime.QuadPart += cpuInfo.IdleTime.QuadPart; cpuTotals.KernelTime.QuadPart += cpuInfo.KernelTime.QuadPart; cpuTotals.UserTime.QuadPart += cpuInfo.UserTime.QuadPart; // Update friendly structure auto& myCpuInfo = myCpuInfos[i]; myCpuInfo.update(cpuInfo); PROFILE_COUNTER(app, myCpuInfo.name.c_str(), { { "kernel", myCpuInfo.kernelUsage }, { "user", myCpuInfo.userUsage } }); } myCpuTotals.update(cpuTotals); PROFILE_COUNTER(app, myCpuTotals.name.c_str(), { { "kernel", myCpuTotals.kernelUsage }, { "user", myCpuTotals.userUsage } }); } static ULARGE_INTEGER lastCPU, lastSysCPU, lastUserCPU; static int numProcessors; static HANDLE self; static PDH_HQUERY cpuQuery; static PDH_HCOUNTER cpuTotal; void initCpuUsage() { SYSTEM_INFO sysInfo; FILETIME ftime, fsys, fuser; GetSystemInfo(&sysInfo); numProcessors = sysInfo.dwNumberOfProcessors; GetSystemTimeAsFileTime(&ftime); memcpy(&lastCPU, &ftime, sizeof(FILETIME)); self = GetCurrentProcess(); GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); memcpy(&lastSysCPU, &fsys, sizeof(FILETIME)); memcpy(&lastUserCPU, &fuser, sizeof(FILETIME)); PdhOpenQuery(NULL, NULL, &cpuQuery); PdhAddCounter(cpuQuery, "\\Processor(_Total)\\% Processor Time", NULL, &cpuTotal); PdhCollectQueryData(cpuQuery); } void getCpuUsage(glm::vec3& systemAndUser) { FILETIME ftime, fsys, fuser; ULARGE_INTEGER now, sys, user; GetSystemTimeAsFileTime(&ftime); memcpy(&now, &ftime, sizeof(FILETIME)); GetProcessTimes(self, &ftime, &ftime, &fsys, &fuser); memcpy(&sys, &fsys, sizeof(FILETIME)); memcpy(&user, &fuser, sizeof(FILETIME)); systemAndUser.x = (sys.QuadPart - lastSysCPU.QuadPart); systemAndUser.y = (user.QuadPart - lastUserCPU.QuadPart); systemAndUser /= (float)(now.QuadPart - lastCPU.QuadPart); systemAndUser /= (float)numProcessors; systemAndUser *= 100.0f; lastCPU = now; lastUserCPU = user; lastSysCPU = sys; PDH_FMT_COUNTERVALUE counterVal; PdhCollectQueryData(cpuQuery); PdhGetFormattedCounterValue(cpuTotal, PDH_FMT_DOUBLE, NULL, &counterVal); systemAndUser.z = (float)counterVal.doubleValue; } void setupCpuMonitorThread() { initCpuUsage(); auto cpuMonitorThread = QThread::currentThread(); setThreadName("CPU Monitor Thread"); QTimer* timer = new QTimer(); timer->setInterval(50); QObject::connect(timer, &QTimer::timeout, [] { updateCpuInformation(); glm::vec3 kernelUserAndSystem; getCpuUsage(kernelUserAndSystem); PROFILE_COUNTER(app, "cpuProcess", { { "system", kernelUserAndSystem.x }, { "user", kernelUserAndSystem.y } }); PROFILE_COUNTER(app, "cpuSystem", { { "system", kernelUserAndSystem.z } }); }); QObject::connect(cpuMonitorThread, &QThread::finished, [=] { timer->deleteLater(); cpuMonitorThread->deleteLater(); }); timer->start(); } #endif #endif // hifi_WindowSystemInfo_h