/* Copyright (C) 2001 Kimmo Pekkola This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "StdAfx.h" #include "MeasureCPU.h" #include "Rainmeter.h" #include "System.h" #include "Error.h" #define STATUS_SUCCESS 0 #define STATUS_INFO_LENGTH_MISMATCH 0xC0000004 #define SystemProcessorPerformanceInformation 8 typedef struct _SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION { LARGE_INTEGER IdleTime; LARGE_INTEGER KernelTime; LARGE_INTEGER UserTime; LARGE_INTEGER Reserved1[2]; ULONG Reserved2; } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION, *PSYSTEM_PROCESSOR_PERFORMANCE_INFORMATION; #define Li2Double(x) ((double)((x).QuadPart)) #define Ft2Double(x) ((double)((x).dwHighDateTime) * 4.294967296E9 + (double)((x).dwLowDateTime)) PROCNTQSI CMeasureCPU::c_NtQuerySystemInformation = NULL; int CMeasureCPU::c_NumOfProcessors = 0; ULONG CMeasureCPU::c_BufferSize = 0; // ntdll!NtQuerySystemInformation (NT specific!) // // The function copies the system information of the // specified type into a buffer // // NTSYSAPI // NTSTATUS // NTAPI // NtQuerySystemInformation( // IN UINT SystemInformationClass, // information type // OUT PVOID SystemInformation, // pointer to buffer // IN ULONG SystemInformationLength, // buffer size in bytes // OUT PULONG ReturnLength OPTIONAL // pointer to a 32-bit // // variable that receives // // the number of bytes // // written to the buffer // ); /* ** The constructor ** */ CMeasureCPU::CMeasureCPU(CMeterWindow* meterWindow, const WCHAR* name) : CMeasure(meterWindow, name), m_Processor(), m_OldTime() { m_MaxValue = 100.0; if (c_NtQuerySystemInformation == NULL) { c_NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(GetModuleHandle(L"ntdll"), "NtQuerySystemInformation"); } if (c_NumOfProcessors == 0) { SYSTEM_INFO systemInfo; GetSystemInfo(&systemInfo); c_NumOfProcessors = (int)systemInfo.dwNumberOfProcessors; } } /* ** The destructor ** */ CMeasureCPU::~CMeasureCPU() { } /* ** Read the options specified in the ini file. ** */ void CMeasureCPU::ReadOptions(CConfigParser& parser, const WCHAR* section) { CMeasure::ReadOptions(parser, section); int processor = parser.ReadInt(section, L"Processor", 0); if (processor < 0 || processor > c_NumOfProcessors) { LogWithArgs(LOG_WARNING, L"CPU: Processor=%i invalid in [%s]", processor, section); processor = 0; } if (processor != m_Processor) { m_Processor = processor; m_OldTime[0] = m_OldTime[1] = 0.0; } } /* ** Updates the current CPU utilization value. ** */ void CMeasureCPU::UpdateValue() { if (m_Processor == 0) { BOOL status; FILETIME ftIdleTime, ftKernelTime, ftUserTime; // get new CPU's idle/kernel/user time status = GetSystemTimes(&ftIdleTime, &ftKernelTime, &ftUserTime); if (status == 0) return; CalcUsage(Ft2Double(ftIdleTime), Ft2Double(ftKernelTime) + Ft2Double(ftUserTime)); } else if (c_NtQuerySystemInformation) { LONG status; ULONG bufSize = c_BufferSize; BYTE* buf = (bufSize > 0) ? new BYTE[bufSize] : NULL; int loop = 0; do { ULONG size = 0; status = c_NtQuerySystemInformation(SystemProcessorPerformanceInformation, buf, bufSize, &size); if (status == STATUS_INFO_LENGTH_MISMATCH) { if (size == 0) // Returned required buffer size is always 0 on Windows 2000/XP. { if (bufSize == 0) { bufSize = sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION) * c_NumOfProcessors; } else { bufSize += sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); } } else { if (size != bufSize) { bufSize = size; } else // ?? { bufSize += sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION); } } delete [] buf; buf = new BYTE[bufSize]; } else { break; } ++loop; } while (loop < 5); if (status == STATUS_SUCCESS) { if (bufSize != c_BufferSize) { // Store the new buffer size c_BufferSize = bufSize; } SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* systemPerfInfo = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*)buf; int processor = m_Processor - 1; CalcUsage(Li2Double(systemPerfInfo[processor].IdleTime), Li2Double(systemPerfInfo[processor].KernelTime) + Li2Double(systemPerfInfo[processor].UserTime)); } delete [] buf; } } /* ** Calculates the current CPU utilization value. ** */ void CMeasureCPU::CalcUsage(double idleTime, double systemTime) { // CurrentCpuUsage% = 100 - ((IdleTime / SystemTime) * 100) double dbCpuUsage = 100.0 - ((idleTime - m_OldTime[0]) / (systemTime - m_OldTime[1])) * 100.0; dbCpuUsage = min(dbCpuUsage, 100.0); m_Value = max(dbCpuUsage, 0.0); // store new CPU's idle and system time m_OldTime[0] = idleTime; m_OldTime[1] = systemTime; }