rainmeter-studio/Library/MeasureCPU.cpp
2010-03-30 22:37:05 +00:00

349 lines
8.5 KiB
C++

/*
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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
#include "StdAfx.h"
#include "MeasureCPU.h"
#include "Rainmeter.h"
#include "Error.h"
#define STATUS_SUCCESS 0
#define STATUS_INFO_LENGTH_MISMATCH 0xC0000004
#define SystemProcessorPerformanceInformation 8
#define Li2Double(x) ((double)((x).HighPart) * 4.294967296E9 + (double)((x).LowPart))
#define Ft2Double(x) ((double)((x).dwHighDateTime) * 4.294967296E9 + (double)((x).dwLowDateTime))
// 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
// );
/*
** CMeasureCPU
**
** The constructor
**
*/
CMeasureCPU::CMeasureCPU(CMeterWindow* meterWindow) : CMeasure(meterWindow)
{
m_CPUFromRegistry = false;
m_MaxValue = 100.0;
m_MinValue = 0.0;
m_FirstTime = true;
m_Processor = 0;
m_NtQuerySystemInformation = (PROCNTQSI)GetProcAddress(
GetModuleHandle(L"ntdll"),
"NtQuerySystemInformation"
);
m_GetSystemTimes = (PROCGST)GetProcAddress(
GetModuleHandle(L"kernel32"),
"GetSystemTimes"
);
SYSTEM_INFO systemInfo = {0};
GetSystemInfo(&systemInfo);
m_NumOfProcessors = (int)systemInfo.dwNumberOfProcessors;
}
/*
** ~CMeasureCPU
**
** The destructor
**
*/
CMeasureCPU::~CMeasureCPU()
{
if(m_CPUFromRegistry)
{
// Stop the counter if it was started
HKEY hkey;
DWORD dwDataSize;
DWORD dwType;
DWORD dwDummy;
RegOpenKeyEx(HKEY_DYN_DATA, L"PerfStats\\StopStat", 0, KEY_ALL_ACCESS, &hkey);
dwDataSize = sizeof(dwDummy);
RegQueryValueEx(hkey, L"KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwDummy, &dwDataSize);
RegCloseKey(hkey);
}
}
/*
** ReadConfig
**
** Reads the measure specific configs.
**
*/
void CMeasureCPU::ReadConfig(CConfigParser& parser, const WCHAR* section)
{
CMeasure::ReadConfig(parser, section);
int processor = parser.ReadInt(section, L"Processor", 0);
if (processor < 0 || processor > m_NumOfProcessors)
{
DebugLog(L"[%s] Invalid Processor: %i", section, processor);
processor = 0;
}
if (processor != m_Processor)
{
m_Processor = processor;
m_FirstTime = true;
}
if (m_FirstTime)
{
if (m_Processor == 0 && m_GetSystemTimes == NULL)
{
m_OldTime.assign(m_NumOfProcessors * 2, 0.0);
}
else
{
m_OldTime.assign(2, 0.0);
}
}
}
/*
** Update
**
** Updates the current CPU utilization value. On NT the value is taken
** from the performance counters and on 9x we'll use the registry.
**
*/
bool CMeasureCPU::Update()
{
if (!CMeasure::PreUpdate()) return false;
if (CRainmeter::IsNT() != PLATFORM_9X)
{
if (m_Processor == 0 && m_GetSystemTimes)
{
BOOL status;
FILETIME ftIdleTime, ftKernelTime, ftUserTime;
// get new CPU's idle/kernel/user time
status = m_GetSystemTimes(&ftIdleTime, &ftKernelTime, &ftUserTime);
if (status == 0) return false;
CalcUsage(Ft2Double(ftIdleTime),
Ft2Double(ftKernelTime) + Ft2Double(ftUserTime));
}
else if (m_NtQuerySystemInformation)
{
LONG status;
BYTE* buf = NULL;
ULONG bufSize = 0;
int loop = 0;
do
{
ULONG size = 0;
status = m_NtQuerySystemInformation(SystemProcessorPerformanceInformation, buf, bufSize, &size);
if (status == STATUS_SUCCESS) break;
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) * m_NumOfProcessors;
}
else
{
bufSize += sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
}
}
else
{
if (size != bufSize)
{
bufSize = size;
}
else // ??
{
bufSize += sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
}
}
if (buf) delete [] buf;
buf = new BYTE[bufSize];
}
else // failed
{
if (buf) delete [] buf;
return false;
}
++loop;
} while (loop < 10);
if (status != STATUS_SUCCESS) // failed
{
if (buf) delete [] buf;
return false;
}
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* systemPerfInfo = (SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION*)buf;
if (m_Processor == 0)
{
CalcAverageUsage(systemPerfInfo);
}
else
{
int processor = m_Processor - 1;
CalcUsage(Li2Double(systemPerfInfo[processor].IdleTime),
Li2Double(systemPerfInfo[processor].KernelTime) + Li2Double(systemPerfInfo[processor].UserTime));
}
delete [] buf;
}
else
{
return false;
}
}
else
{
// It's a wintendo!
HKEY hkey;
DWORD dwDataSize;
DWORD dwType;
DWORD dwCpuUsage;
if(m_FirstTime)
{
RegOpenKeyEx(HKEY_DYN_DATA, L"PerfStats\\StartStat", 0, KEY_ALL_ACCESS, &hkey);
dwDataSize = sizeof(dwCpuUsage);
RegQueryValueEx(hkey, L"KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwDataSize);
RegCloseKey(hkey);
m_FirstTime = false;
}
RegOpenKeyEx(HKEY_DYN_DATA, L"PerfStats\\StatData", 0, KEY_ALL_ACCESS, &hkey);
dwDataSize = sizeof(dwCpuUsage);
RegQueryValueEx(hkey, L"KERNEL\\CPUUsage", NULL, &dwType, (LPBYTE)&dwCpuUsage, &dwDataSize);
RegCloseKey(hkey);
m_Value = dwCpuUsage;
m_CPUFromRegistry = true;
}
return PostUpdate();
}
/*
** CalcUsage
**
** Calculates the current CPU utilization value.
**
*/
void CMeasureCPU::CalcUsage(double idleTime, double systemTime)
{
if (!m_FirstTime)
{
double dbCpuUsage;
// CurrentCpuUsage% = 100 - ((IdleTime / SystemTime) * 100)
dbCpuUsage = 100.0 - ((idleTime - m_OldTime[0]) / (systemTime - m_OldTime[1])) * 100.0;
dbCpuUsage = min(dbCpuUsage, 100.0);
m_Value = max(dbCpuUsage, 0.0);
}
else
{
m_FirstTime = false;
}
// store new CPU's idle and system time
m_OldTime[0] = idleTime;
m_OldTime[1] = systemTime;
}
/*
** CalcAverageUsage
**
** Calculates the current CPU average utilization value.
** This function is used if GetSystemTimes function is not available.
**
*/
void CMeasureCPU::CalcAverageUsage(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION* systemPerfInfo)
{
if(!m_FirstTime)
{
double dbIdleTimeDiff = 0, dbSystemTimeDiff = 0;
double dbCpuUsage;
for (int i = 0; i < m_NumOfProcessors; ++i)
{
double dbIdleTime, dbSystemTime;
dbIdleTime = Li2Double(systemPerfInfo[i].IdleTime);
dbSystemTime = Li2Double(systemPerfInfo[i].KernelTime) + Li2Double(systemPerfInfo[i].UserTime);
dbIdleTimeDiff += dbIdleTime - m_OldTime[i * 2 + 0];
dbSystemTimeDiff += dbSystemTime - m_OldTime[i * 2 + 1];
// store new CPU's idle and system time
m_OldTime[i * 2 + 0] = dbIdleTime;
m_OldTime[i * 2 + 1] = dbSystemTime;
}
// CurrentCpuUsage% = 100 - ((IdleTime / SystemTime) * 100)
dbCpuUsage = 100.0 - (dbIdleTimeDiff / dbSystemTimeDiff) * 100.0;
dbCpuUsage = min(dbCpuUsage, 100.0);
m_Value = max(dbCpuUsage, 0.0);
}
else
{
// store new CPU's idle and system time
for (int i = 0; i < m_NumOfProcessors; ++i)
{
m_OldTime[i * 2 + 0] = Li2Double(systemPerfInfo[i].IdleTime);
m_OldTime[i * 2 + 1] = Li2Double(systemPerfInfo[i].KernelTime) + Li2Double(systemPerfInfo[i].UserTime);
}
m_FirstTime = false;
}
}