Permalink
Switch branches/tags
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
1038 lines (926 sloc) 30.4 KB
/*
* Copyright (C) 2005-2018 Team Kodi
* This file is part of Kodi - https://kodi.tv
*
* SPDX-License-Identifier: GPL-2.0-or-later
* See LICENSES/README.md for more information.
*/
#include <cstdlib>
#include "CPUInfo.h"
#include "utils/log.h"
#include "utils/SysfsUtils.h"
#include "utils/Temperature.h"
#include <string>
#include <string.h>
#if defined(TARGET_DARWIN)
#include <sys/types.h>
#include <sys/sysctl.h>
#if defined(__ppc__) || defined (TARGET_DARWIN_IOS)
#include <mach-o/arch.h>
#endif // defined(__ppc__) || defined (TARGET_DARWIN_IOS)
#ifdef TARGET_DARWIN_OSX
#include "platform/darwin/osx/smc.h"
#endif
#include "platform/linux/LinuxResourceCounter.h"
#endif
#if defined(TARGET_FREEBSD)
#include <sys/types.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#endif
#if defined(TARGET_LINUX) && defined(HAS_NEON) && !defined(TARGET_ANDROID)
#include <fcntl.h>
#include <unistd.h>
#include <elf.h>
#include <linux/auxvec.h>
#include <asm/hwcap.h>
#endif
#if defined(TARGET_ANDROID)
#include "platform/android/activity/AndroidFeatures.h"
#endif
#ifdef TARGET_WINDOWS
#include "platform/win32/CharsetConverter.h"
#include <algorithm>
#include <intrin.h>
#include <Pdh.h>
#include <PdhMsg.h>
#ifdef TARGET_WINDOWS_DESKTOP
#pragma comment(lib, "Pdh.lib")
#endif
#ifdef TARGET_WINDOWS_STORE
#include <winrt/Windows.Foundation.Metadata.h>
#include <winrt/Windows.System.Diagnostics.h>
#endif
// Defines to help with calls to CPUID
#define CPUID_INFOTYPE_STANDARD 0x00000001
#define CPUID_INFOTYPE_EXTENDED 0x80000001
// Standard Features
// Bitmasks for the values returned by a call to cpuid with eax=0x00000001
#define CPUID_00000001_ECX_SSE3 (1<<0)
#define CPUID_00000001_ECX_SSSE3 (1<<9)
#define CPUID_00000001_ECX_SSE4 (1<<19)
#define CPUID_00000001_ECX_SSE42 (1<<20)
#define CPUID_00000001_EDX_MMX (1<<23)
#define CPUID_00000001_EDX_SSE (1<<25)
#define CPUID_00000001_EDX_SSE2 (1<<26)
// Extended Features
// Bitmasks for the values returned by a call to cpuid with eax=0x80000001
#define CPUID_80000001_EDX_MMX2 (1<<22)
#define CPUID_80000001_EDX_MMX (1<<23)
#define CPUID_80000001_EDX_3DNOWEXT (1<<30)
#define CPUID_80000001_EDX_3DNOW (1<<31)
// Help with the __cpuid intrinsic of MSVC
#define CPUINFO_EAX 0
#define CPUINFO_EBX 1
#define CPUINFO_ECX 2
#define CPUINFO_EDX 3
#endif
#ifdef TARGET_POSIX
#include "ServiceBroker.h"
#include "settings/AdvancedSettings.h"
#include "settings/SettingsComponent.h"
#endif
#include "utils/StringUtils.h"
// In milliseconds
#define MINIMUM_TIME_BETWEEN_READS 500
CCPUInfo::CCPUInfo(void)
{
#ifdef TARGET_POSIX
m_fProcStat = m_fProcTemperature = m_fCPUFreq = NULL;
m_cpuInfoForFreq = false;
#elif defined(TARGET_WINDOWS)
m_cpuQueryFreq = nullptr;
m_cpuQueryLoad = nullptr;
#endif
m_lastUsedPercentage = 0;
m_cpuFeatures = 0;
#if defined(TARGET_DARWIN)
m_pResourceCounter = new CLinuxResourceCounter();
size_t len = 4;
std::string cpuVendor;
// The number of cores.
if (sysctlbyname("hw.activecpu", &m_cpuCount, &len, NULL, 0) == -1)
m_cpuCount = 1;
// The model.
#if defined(__ppc__) || defined (TARGET_DARWIN_IOS)
const NXArchInfo *info = NXGetLocalArchInfo();
if (info != NULL)
m_cpuModel = info->description;
#else
// NXGetLocalArchInfo is ugly for intel so keep using this method
char buffer[512];
len = 512;
if (sysctlbyname("machdep.cpu.brand_string", &buffer, &len, NULL, 0) == 0)
m_cpuModel = buffer;
// The CPU vendor
len = 512;
if (sysctlbyname("machdep.cpu.vendor", &buffer, &len, NULL, 0) == 0)
cpuVendor = buffer;
#endif
// Go through each core.
for (int i=0; i<m_cpuCount; i++)
{
CoreInfo core;
core.m_id = i;
core.m_strModel = m_cpuModel;
core.m_strVendor = cpuVendor;
m_cores[core.m_id] = core;
}
#elif defined(TARGET_WINDOWS_STORE)
SYSTEM_INFO siSysInfo;
GetNativeSystemInfo(&siSysInfo);
m_cpuCount = siSysInfo.dwNumberOfProcessors;
m_cpuModel = "Unknown";
#elif defined(TARGET_WINDOWS_DESKTOP)
using KODI::PLATFORM::WINDOWS::FromW;
HKEY hKeyCpuRoot;
if (RegOpenKeyExW(HKEY_LOCAL_MACHINE, L"HARDWARE\\DESCRIPTION\\System\\CentralProcessor", 0, KEY_READ, &hKeyCpuRoot) == ERROR_SUCCESS)
{
DWORD num = 0;
std::vector<CoreInfo> cpuCores;
wchar_t subKeyName[200]; // more than enough
DWORD subKeyNameLen = sizeof(subKeyName) / sizeof(wchar_t);
while (RegEnumKeyExW(hKeyCpuRoot, num++, subKeyName, &subKeyNameLen, nullptr, nullptr, nullptr, nullptr) == ERROR_SUCCESS)
{
HKEY hCpuKey;
if (RegOpenKeyExW(hKeyCpuRoot, subKeyName, 0, KEY_QUERY_VALUE, &hCpuKey) == ERROR_SUCCESS)
{
CoreInfo cpuCore;
if (swscanf_s(subKeyName, L"%i", &cpuCore.m_id) != 1)
cpuCore.m_id = num - 1;
wchar_t buf[300]; // more than enough
DWORD bufSize = sizeof(buf);
DWORD valType;
if (RegQueryValueExW(hCpuKey, L"ProcessorNameString", nullptr, &valType, LPBYTE(buf), &bufSize) == ERROR_SUCCESS &&
valType == REG_SZ)
{
cpuCore.m_strModel = FromW(buf, bufSize / sizeof(wchar_t));
cpuCore.m_strModel = cpuCore.m_strModel.substr(0, cpuCore.m_strModel.find(char(0))); // remove extra null terminations
StringUtils::RemoveDuplicatedSpacesAndTabs(cpuCore.m_strModel);
StringUtils::Trim(cpuCore.m_strModel);
}
bufSize = sizeof(buf);
if (RegQueryValueExW(hCpuKey, L"VendorIdentifier", nullptr, &valType, LPBYTE(buf), &bufSize) == ERROR_SUCCESS &&
valType == REG_SZ)
{
cpuCore.m_strVendor = FromW(buf, bufSize / sizeof(wchar_t));
cpuCore.m_strVendor = cpuCore.m_strVendor.substr(0, cpuCore.m_strVendor.find(char(0))); // remove extra null terminations
}
DWORD mhzVal;
bufSize = sizeof(mhzVal);
if (RegQueryValueExW(hCpuKey, L"~MHz", nullptr, &valType, LPBYTE(&mhzVal), &bufSize) == ERROR_SUCCESS &&
valType == REG_DWORD)
cpuCore.m_fSpeed = double(mhzVal);
RegCloseKey(hCpuKey);
if (cpuCore.m_strModel.empty())
cpuCore.m_strModel = "Unknown";
cpuCores.push_back(cpuCore);
}
subKeyNameLen = sizeof(subKeyName) / sizeof(wchar_t); // restore length value
}
RegCloseKey(hKeyCpuRoot);
std::sort(cpuCores.begin(), cpuCores.end()); // sort cores by id
for (size_t i = 0; i < cpuCores.size(); i++)
m_cores[i] = cpuCores[i]; // add in sorted order
}
if (!m_cores.empty())
m_cpuModel = m_cores.begin()->second.m_strModel;
else
m_cpuModel = "Unknown";
SYSTEM_INFO siSysInfo;
GetNativeSystemInfo(&siSysInfo);
m_cpuCount = siSysInfo.dwNumberOfProcessors;
if (PdhOpenQueryW(nullptr, 0, &m_cpuQueryFreq) == ERROR_SUCCESS)
{
if (PdhAddEnglishCounterW(m_cpuQueryFreq, L"\\Processor Information(0,0)\\Processor Frequency", 0, &m_cpuFreqCounter) != ERROR_SUCCESS)
m_cpuFreqCounter = nullptr;
}
else
m_cpuQueryFreq = nullptr;
if (PdhOpenQueryW(nullptr, 0, &m_cpuQueryLoad) == ERROR_SUCCESS)
{
for (size_t i = 0; i < m_cores.size(); i++)
{
if (PdhAddEnglishCounterW(m_cpuQueryLoad, StringUtils::Format(L"\\Processor(%d)\\%% Idle Time", int(i)).c_str(), 0, &m_cores[i].m_coreCounter) != ERROR_SUCCESS)
m_cores[i].m_coreCounter = nullptr;
}
}
else
m_cpuQueryLoad = nullptr;
#elif defined(TARGET_FREEBSD)
size_t len;
int i;
char cpumodel[512];
len = sizeof(m_cpuCount);
if (sysctlbyname("hw.ncpu", &m_cpuCount, &len, NULL, 0) != 0)
m_cpuCount = 1;
len = sizeof(cpumodel);
if (sysctlbyname("hw.model", &cpumodel, &len, NULL, 0) != 0)
(void)strncpy(cpumodel, "Unknown", 8);
m_cpuModel = cpumodel;
for (i = 0; i < m_cpuCount; i++)
{
CoreInfo core;
core.m_id = i;
core.m_strModel = m_cpuModel;
m_cores[core.m_id] = core;
}
#else
m_fProcStat = fopen("/proc/stat", "r");
m_fProcTemperature = fopen("/proc/acpi/thermal_zone/THM0/temperature", "r");
if (m_fProcTemperature == NULL)
m_fProcTemperature = fopen("/proc/acpi/thermal_zone/THRM/temperature", "r");
if (m_fProcTemperature == NULL)
m_fProcTemperature = fopen("/proc/acpi/thermal_zone/THR0/temperature", "r");
if (m_fProcTemperature == NULL)
m_fProcTemperature = fopen("/proc/acpi/thermal_zone/TZ0/temperature", "r");
// read from the new location of the temperature data on new kernels, 2.6.39, 3.0 etc
if (m_fProcTemperature == NULL)
m_fProcTemperature = fopen("/sys/class/hwmon/hwmon0/temp1_input", "r");
if (m_fProcTemperature == NULL)
m_fProcTemperature = fopen("/sys/class/thermal/thermal_zone0/temp", "r"); // On Raspberry PIs
m_fCPUFreq = fopen ("/sys/devices/system/cpu/cpu0/cpufreq/scaling_cur_freq", "r");
if (!m_fCPUFreq)
{
m_cpuInfoForFreq = true;
m_fCPUFreq = fopen("/proc/cpuinfo", "r");
}
else
m_cpuInfoForFreq = false;
FILE* fCPUInfo = fopen("/proc/cpuinfo", "r");
m_cpuCount = 0;
if (fCPUInfo)
{
char buffer[512];
int nCurrId = 0;
while (fgets(buffer, sizeof(buffer), fCPUInfo))
{
if (strncmp(buffer, "processor", strlen("processor"))==0)
{
char *needle = strstr(buffer, ":");
if (needle)
{
CoreInfo core;
core.m_id = atoi(needle+2);
nCurrId = core.m_id;
m_cores[core.m_id] = core;
}
m_cpuCount++;
}
else if (strncmp(buffer, "vendor_id", strlen("vendor_id"))==0)
{
char *needle = strstr(buffer, ":");
if (needle && strlen(needle)>3)
{
needle+=2;
m_cores[nCurrId].m_strVendor = needle;
StringUtils::Trim(m_cores[nCurrId].m_strVendor);
}
}
else if (strncmp(buffer, "Processor", strlen("Processor"))==0)
{
char *needle = strstr(buffer, ":");
if (needle && strlen(needle)>3)
{
needle+=2;
m_cpuModel = needle;
m_cores[nCurrId].m_strModel = m_cpuModel;
StringUtils::Trim(m_cores[nCurrId].m_strModel);
}
}
else if (strncmp(buffer, "BogoMIPS", strlen("BogoMIPS"))==0)
{
char *needle = strstr(buffer, ":");
if (needle && strlen(needle)>3)
{
needle+=2;
m_cpuBogoMips = needle;
m_cores[nCurrId].m_strBogoMips = m_cpuBogoMips;
StringUtils::Trim(m_cores[nCurrId].m_strBogoMips);
}
}
else if (strncmp(buffer, "Hardware", strlen("Hardware"))==0)
{
char *needle = strstr(buffer, ":");
if (needle && strlen(needle)>3)
{
needle+=2;
m_cpuHardware = needle;
m_cores[nCurrId].m_strHardware = m_cpuHardware;
StringUtils::Trim(m_cores[nCurrId].m_strHardware);
}
}
else if (strncmp(buffer, "Revision", strlen("Revision"))==0)
{
char *needle = strstr(buffer, ":");
if (needle && strlen(needle)>3)
{
needle+=2;
m_cpuRevision = needle;
m_cores[nCurrId].m_strRevision = m_cpuRevision;
StringUtils::Trim(m_cores[nCurrId].m_strRevision);
}
}
else if (strncmp(buffer, "Serial", strlen("Serial"))==0)
{
char *needle = strstr(buffer, ":");
if (needle && strlen(needle)>3)
{
needle+=2;
m_cpuSerial = needle;
m_cores[nCurrId].m_strSerial = m_cpuSerial;
StringUtils::Trim(m_cores[nCurrId].m_strSerial);
}
}
else if (strncmp(buffer, "model name", strlen("model name"))==0)
{
char *needle = strstr(buffer, ":");
if (needle && strlen(needle)>3)
{
needle+=2;
m_cpuModel = needle;
m_cores[nCurrId].m_strModel = m_cpuModel;
StringUtils::Trim(m_cores[nCurrId].m_strModel);
}
}
else if (strncmp(buffer, "flags", 5) == 0)
{
char* needle = strchr(buffer, ':');
if (needle)
{
char* tok = NULL,
* save;
needle++;
tok = strtok_r(needle, " ", &save);
while (tok)
{
if (0 == strcmp(tok, "mmx"))
m_cpuFeatures |= CPU_FEATURE_MMX;
else if (0 == strcmp(tok, "mmxext"))
m_cpuFeatures |= CPU_FEATURE_MMX2;
else if (0 == strcmp(tok, "sse"))
m_cpuFeatures |= CPU_FEATURE_SSE;
else if (0 == strcmp(tok, "sse2"))
m_cpuFeatures |= CPU_FEATURE_SSE2;
else if (0 == strcmp(tok, "sse3"))
m_cpuFeatures |= CPU_FEATURE_SSE3;
else if (0 == strcmp(tok, "ssse3"))
m_cpuFeatures |= CPU_FEATURE_SSSE3;
else if (0 == strcmp(tok, "sse4_1"))
m_cpuFeatures |= CPU_FEATURE_SSE4;
else if (0 == strcmp(tok, "sse4_2"))
m_cpuFeatures |= CPU_FEATURE_SSE42;
else if (0 == strcmp(tok, "3dnow"))
m_cpuFeatures |= CPU_FEATURE_3DNOW;
else if (0 == strcmp(tok, "3dnowext"))
m_cpuFeatures |= CPU_FEATURE_3DNOWEXT;
tok = strtok_r(NULL, " ", &save);
}
}
}
}
fclose(fCPUInfo);
// new socs use the sysfs soc interface to describe the hardware
if (SysfsUtils::Has("/sys/bus/soc/devices/soc0"))
{
std::string machine, family, soc_id;
if (SysfsUtils::Has("/sys/bus/soc/devices/soc0/machine"))
SysfsUtils::GetString("/sys/bus/soc/devices/soc0/machine", machine);
if (SysfsUtils::Has("/sys/bus/soc/devices/soc0/family"))
SysfsUtils::GetString("/sys/bus/soc/devices/soc0/family", family);
if (SysfsUtils::Has("/sys/bus/soc/devices/soc0/soc_id"))
SysfsUtils::GetString("/sys/bus/soc/devices/soc0/soc_id", soc_id);
if (m_cpuHardware.empty() && !machine.empty())
m_cpuHardware = machine;
if (!family.empty() && !soc_id.empty())
m_cpuSoC = family + " " + soc_id;
}
// /proc/cpuinfo is not reliable on some Android platforms
// At least we should get the correct cpu count for multithreaded decoding
#if defined(TARGET_ANDROID)
if (CAndroidFeatures::GetCPUCount() > m_cpuCount)
{
for (int i = m_cpuCount; i < CAndroidFeatures::GetCPUCount(); i++)
{
// Copy info from cpu 0
CoreInfo core(m_cores[0]);
core.m_id = i;
m_cores[core.m_id] = core;
}
m_cpuCount = CAndroidFeatures::GetCPUCount();
}
#endif
}
else
{
m_cpuCount = 1;
m_cpuModel = "Unknown";
}
#endif
StringUtils::Replace(m_cpuModel, '\r', ' ');
StringUtils::Replace(m_cpuModel, '\n', ' ');
StringUtils::RemoveDuplicatedSpacesAndTabs(m_cpuModel);
StringUtils::Trim(m_cpuModel);
/* Set some default for empty string variables */
if (m_cpuBogoMips.empty())
m_cpuBogoMips = "N/A";
if (m_cpuHardware.empty())
m_cpuHardware = "N/A";
if (m_cpuRevision.empty())
m_cpuRevision = "N/A";
if (m_cpuSerial.empty())
m_cpuSerial = "N/A";
readProcStat(m_userTicks, m_niceTicks, m_systemTicks, m_idleTicks, m_ioTicks);
m_nextUsedReadTime.Set(MINIMUM_TIME_BETWEEN_READS);
ReadCPUFeatures();
// Set MMX2 when SSE is present as SSE is a superset of MMX2 and Intel doesn't set the MMX2 cap
if (m_cpuFeatures & CPU_FEATURE_SSE)
m_cpuFeatures |= CPU_FEATURE_MMX2;
if (HasNeon())
m_cpuFeatures |= CPU_FEATURE_NEON;
}
CCPUInfo::~CCPUInfo()
{
#ifdef TARGET_POSIX
if (m_fProcStat != NULL)
fclose(m_fProcStat);
if (m_fProcTemperature != NULL)
fclose(m_fProcTemperature);
if (m_fCPUFreq != NULL)
fclose(m_fCPUFreq);
#elif defined(TARGET_WINDOWS_DESKTOP)
if (m_cpuQueryFreq)
PdhCloseQuery(m_cpuQueryFreq);
if (m_cpuQueryLoad)
PdhCloseQuery(m_cpuQueryLoad);
#elif defined(TARGET_DARWIN)
delete m_pResourceCounter;
#endif
}
int CCPUInfo::getUsedPercentage()
{
int result = 0;
if (!m_nextUsedReadTime.IsTimePast())
return m_lastUsedPercentage;
#if defined(TARGET_DARWIN)
result = m_pResourceCounter->GetCPUUsage();
#else
unsigned long long userTicks;
unsigned long long niceTicks;
unsigned long long systemTicks;
unsigned long long idleTicks;
unsigned long long ioTicks;
if (!readProcStat(userTicks, niceTicks, systemTicks, idleTicks, ioTicks))
return m_lastUsedPercentage;
userTicks -= m_userTicks;
niceTicks -= m_niceTicks;
systemTicks -= m_systemTicks;
idleTicks -= m_idleTicks;
ioTicks -= m_ioTicks;
if(userTicks + niceTicks + systemTicks + idleTicks + ioTicks == 0)
return m_lastUsedPercentage;
result = static_cast<int>(double(userTicks + niceTicks + systemTicks) * 100.0 / double(userTicks + niceTicks + systemTicks + idleTicks + ioTicks) + 0.5);
m_userTicks += userTicks;
m_niceTicks += niceTicks;
m_systemTicks += systemTicks;
m_idleTicks += idleTicks;
m_ioTicks += ioTicks;
#endif
m_lastUsedPercentage = result;
m_nextUsedReadTime.Set(MINIMUM_TIME_BETWEEN_READS);
return result;
}
float CCPUInfo::getCPUFrequency()
{
// Get CPU frequency, scaled to MHz.
#if defined(TARGET_DARWIN)
long long hz = 0;
size_t len = sizeof(hz);
if (sysctlbyname("hw.cpufrequency", &hz, &len, NULL, 0) == -1)
return 0.f;
return hz / 1000000.0;
#elif defined(TARGET_WINDOWS_STORE)
CLog::Log(LOGDEBUG, "%s is not implemented", __FUNCTION__);
return 0.f;
#elif defined(TARGET_WINDOWS_DESKTOP)
if (m_cpuFreqCounter && PdhCollectQueryData(m_cpuQueryFreq) == ERROR_SUCCESS)
{
PDH_RAW_COUNTER cnt;
DWORD cntType;
if (PdhGetRawCounterValue(m_cpuFreqCounter, &cntType, &cnt) == ERROR_SUCCESS &&
(cnt.CStatus == PDH_CSTATUS_VALID_DATA || cnt.CStatus == PDH_CSTATUS_NEW_DATA))
{
return float(cnt.FirstValue);
}
}
if (!m_cores.empty())
return float(m_cores.begin()->second.m_fSpeed);
else
return 0.f;
#elif defined(TARGET_FREEBSD)
int hz = 0;
size_t len = sizeof(hz);
if (sysctlbyname("dev.cpu.0.freq", &hz, &len, NULL, 0) != 0)
hz = 0;
return (float)hz;
#else
int value = 0;
if (m_fCPUFreq && !m_cpuInfoForFreq)
{
rewind(m_fCPUFreq);
fflush(m_fCPUFreq);
if (fscanf(m_fCPUFreq, "%d", &value))
value /= 1000.0;
}
if (m_fCPUFreq && m_cpuInfoForFreq)
{
rewind(m_fCPUFreq);
fflush(m_fCPUFreq);
float mhz, avg=0.0;
int n, cpus=0;
while(EOF!=(n=fscanf(m_fCPUFreq," MHz : %f ", &mhz)))
{
if (n>0) {
cpus++;
avg += mhz;
}
if (!fscanf(m_fCPUFreq,"%*s"))
break;
}
if (cpus > 0)
value = avg/cpus;
}
return value;
#endif
}
bool CCPUInfo::getTemperature(CTemperature& temperature)
{
int value = 0;
char scale = 0;
#ifdef TARGET_POSIX
#if defined(TARGET_DARWIN_OSX)
value = SMCGetTemperature(SMC_KEY_CPU_TEMP);
scale = 'c';
#else
int ret = 0;
FILE *p = NULL;
std::string cmd = CServiceBroker::GetSettingsComponent()->GetAdvancedSettings()->m_cpuTempCmd;
temperature.SetValid(false);
if (cmd.empty() && m_fProcTemperature == NULL)
return false;
if (!cmd.empty())
{
p = popen (cmd.c_str(), "r");
if (p)
{
ret = fscanf(p, "%d %c", &value, &scale);
pclose(p);
}
}
else
{
// procfs is deprecated in the linux kernel, we should move away from
// using it for temperature data. It doesn't seem that sysfs has a
// general enough interface to bother implementing ATM.
rewind(m_fProcTemperature);
fflush(m_fProcTemperature);
ret = fscanf(m_fProcTemperature, "temperature: %d %c", &value, &scale);
// read from the temperature file of the new kernels
if (!ret)
{
ret = fscanf(m_fProcTemperature, "%d", &value);
value = value / 1000;
scale = 'c';
ret++;
}
}
if (ret != 2)
return false;
#endif
#endif // TARGET_POSIX
if (scale == 'C' || scale == 'c')
temperature = CTemperature::CreateFromCelsius(value);
else if (scale == 'F' || scale == 'f')
temperature = CTemperature::CreateFromFahrenheit(value);
else
return false;
return true;
}
bool CCPUInfo::HasCoreId(int nCoreId) const
{
std::map<int, CoreInfo>::const_iterator iter = m_cores.find(nCoreId);
if (iter != m_cores.end())
return true;
return false;
}
const CoreInfo &CCPUInfo::GetCoreInfo(int nCoreId)
{
std::map<int, CoreInfo>::iterator iter = m_cores.find(nCoreId);
if (iter != m_cores.end())
return iter->second;
static CoreInfo dummy;
return dummy;
}
bool CCPUInfo::readProcStat(unsigned long long& user, unsigned long long& nice,
unsigned long long& system, unsigned long long& idle, unsigned long long& io)
{
#if defined(TARGET_WINDOWS)
nice = 0;
io = 0;
#if defined (TARGET_WINDOWS_DESKTOP)
FILETIME idleTime;
FILETIME kernelTime;
FILETIME userTime;
if (GetSystemTimes(&idleTime, &kernelTime, &userTime) == 0)
return false;
idle = (uint64_t(idleTime.dwHighDateTime) << 32) + uint64_t(idleTime.dwLowDateTime);
// returned "kernelTime" includes "idleTime"
system = (uint64_t(kernelTime.dwHighDateTime) << 32) + uint64_t(kernelTime.dwLowDateTime) - idle;
user = (uint64_t(userTime.dwHighDateTime) << 32) + uint64_t(userTime.dwLowDateTime);
if (m_cpuFreqCounter && PdhCollectQueryData(m_cpuQueryLoad) == ERROR_SUCCESS)
{
for (std::map<int, CoreInfo>::iterator it = m_cores.begin(); it != m_cores.end(); ++it)
{
CoreInfo& curCore = it->second; // simplify usage
PDH_RAW_COUNTER cnt;
DWORD cntType;
if (curCore.m_coreCounter && PdhGetRawCounterValue(curCore.m_coreCounter, &cntType, &cnt) == ERROR_SUCCESS &&
(cnt.CStatus == PDH_CSTATUS_VALID_DATA || cnt.CStatus == PDH_CSTATUS_NEW_DATA))
{
const LONGLONG coreTotal = cnt.SecondValue,
coreIdle = cnt.FirstValue;
const LONGLONG deltaTotal = coreTotal - curCore.m_total,
deltaIdle = coreIdle - curCore.m_idle;
const double load = (double(deltaTotal - deltaIdle) * 100.0) / double(deltaTotal);
// win32 has some problems with calculation of load if load close to zero
curCore.m_fPct = (load < 0) ? 0 : load;
if (load >= 0 || deltaTotal > 5 * 10 * 1000 * 1000) // do not update (smooth) values for 5 seconds on negative loads
{
curCore.m_total = coreTotal;
curCore.m_idle = coreIdle;
}
}
else
curCore.m_fPct = double(m_lastUsedPercentage); // use CPU average as fallback
}
}
else
for (std::map<int, CoreInfo>::iterator it = m_cores.begin(); it != m_cores.end(); ++it)
it->second.m_fPct = double(m_lastUsedPercentage); // use CPU average as fallback
#endif // TARGET_WINDOWS_DESKTOP
#if defined(TARGET_WINDOWS_STORE)
if (winrt::Windows::Foundation::Metadata::ApiInformation::IsTypePresent(L"Windows.System.Diagnostics.SystemDiagnosticInfo"))
{
auto diagnostic = winrt::Windows::System::Diagnostics::SystemDiagnosticInfo::GetForCurrentSystem();
auto usage = diagnostic.CpuUsage();
auto report = usage.GetReport();
user = report.UserTime().count();
idle = report.IdleTime().count();
system = report.KernelTime().count() - idle;
return true;
}
else
return false;
#endif // TARGET_WINDOWS_STORE
#elif defined(TARGET_FREEBSD)
long *cptimes;
size_t len;
int i;
len = sizeof(long) * 32 * CPUSTATES;
if (sysctlbyname("kern.cp_times", NULL, &len, NULL, 0) != 0)
return false;
cptimes = (long*)malloc(len);
if (cptimes == NULL)
return false;
if (sysctlbyname("kern.cp_times", cptimes, &len, NULL, 0) != 0)
{
free(cptimes);
return false;
}
user = 0;
nice = 0;
system = 0;
idle = 0;
io = 0;
for (i = 0; i < m_cpuCount; i++)
{
long coreUser, coreNice, coreSystem, coreIdle, coreIO;
double total;
coreUser = cptimes[i * CPUSTATES + CP_USER];
coreNice = cptimes[i * CPUSTATES + CP_NICE];
coreSystem = cptimes[i * CPUSTATES + CP_SYS];
coreIO = cptimes[i * CPUSTATES + CP_INTR];
coreIdle = cptimes[i * CPUSTATES + CP_IDLE];
std::map<int, CoreInfo>::iterator iter = m_cores.find(i);
if (iter != m_cores.end())
{
coreUser -= iter->second.m_user;
coreNice -= iter->second.m_nice;
coreSystem -= iter->second.m_system;
coreIdle -= iter->second.m_idle;
coreIO -= iter->second.m_io;
total = (double)(coreUser + coreNice + coreSystem + coreIdle + coreIO);
if(total != 0.0f)
iter->second.m_fPct = ((double)(coreUser + coreNice + coreSystem) * 100.0) / total;
iter->second.m_user += coreUser;
iter->second.m_nice += coreNice;
iter->second.m_system += coreSystem;
iter->second.m_idle += coreIdle;
iter->second.m_io += coreIO;
user += coreUser;
nice += coreNice;
system += coreSystem;
idle += coreIdle;
io += coreIO;
}
}
free(cptimes);
#else
if (m_fProcStat == NULL)
return false;
#ifdef TARGET_ANDROID
// Just another (vanilla) NDK quirk:
// rewind + fflush do not actually flush the buffers,
// the same initial content is returned rather than re-read
fclose(m_fProcStat);
m_fProcStat = fopen("/proc/stat", "r");
#else
rewind(m_fProcStat);
fflush(m_fProcStat);
#endif
char buf[256];
if (!fgets(buf, sizeof(buf), m_fProcStat))
return false;
int num = sscanf(buf, "cpu %llu %llu %llu %llu %llu %*s\n", &user, &nice, &system, &idle, &io);
if (num < 5)
io = 0;
while (fgets(buf, sizeof(buf), m_fProcStat) && num >= 4)
{
unsigned long long coreUser, coreNice, coreSystem, coreIdle, coreIO;
int nCpu=0;
num = sscanf(buf, "cpu%d %llu %llu %llu %llu %llu %*s\n", &nCpu, &coreUser, &coreNice, &coreSystem, &coreIdle, &coreIO);
if (num < 6)
coreIO = 0;
std::map<int, CoreInfo>::iterator iter = m_cores.find(nCpu);
if (num > 4 && iter != m_cores.end())
{
coreUser -= iter->second.m_user;
coreNice -= iter->second.m_nice;
coreSystem -= iter->second.m_system;
coreIdle -= iter->second.m_idle;
coreIO -= iter->second.m_io;
double total = (double)(coreUser + coreNice + coreSystem + coreIdle + coreIO);
if(total == 0.0f)
iter->second.m_fPct = 0.0f;
else
iter->second.m_fPct = ((double)(coreUser + coreNice + coreSystem) * 100.0) / total;
iter->second.m_user += coreUser;
iter->second.m_nice += coreNice;
iter->second.m_system += coreSystem;
iter->second.m_idle += coreIdle;
iter->second.m_io += coreIO;
}
}
#endif
return true;
}
std::string CCPUInfo::GetCoresUsageString() const
{
std::string strCores;
if (!m_cores.empty())
{
for (std::map<int, CoreInfo>::const_iterator it = m_cores.begin(); it != m_cores.end(); ++it)
{
if (!strCores.empty())
strCores += ' ';
if (it->second.m_fPct < 10.0)
strCores += StringUtils::Format("#%d: %1.1f%%", it->first, it->second.m_fPct);
else
strCores += StringUtils::Format("#%d: %3.0f%%", it->first, it->second.m_fPct);
}
}
else
{
strCores += StringUtils::Format("%3.0f%%", double(m_lastUsedPercentage));
}
return strCores;
}
void CCPUInfo::ReadCPUFeatures()
{
#ifdef TARGET_WINDOWS
#ifndef _M_ARM
int CPUInfo[4]; // receives EAX, EBX, ECD and EDX in that order
__cpuid(CPUInfo, 0);
int MaxStdInfoType = CPUInfo[0];
if (MaxStdInfoType >= CPUID_INFOTYPE_STANDARD)
{
__cpuid(CPUInfo, CPUID_INFOTYPE_STANDARD);
if (CPUInfo[CPUINFO_EDX] & CPUID_00000001_EDX_MMX)
m_cpuFeatures |= CPU_FEATURE_MMX;
if (CPUInfo[CPUINFO_EDX] & CPUID_00000001_EDX_SSE)
m_cpuFeatures |= CPU_FEATURE_SSE;
if (CPUInfo[CPUINFO_EDX] & CPUID_00000001_EDX_SSE2)
m_cpuFeatures |= CPU_FEATURE_SSE2;
if (CPUInfo[CPUINFO_ECX] & CPUID_00000001_ECX_SSE3)
m_cpuFeatures |= CPU_FEATURE_SSE3;
if (CPUInfo[CPUINFO_ECX] & CPUID_00000001_ECX_SSSE3)
m_cpuFeatures |= CPU_FEATURE_SSSE3;
if (CPUInfo[CPUINFO_ECX] & CPUID_00000001_ECX_SSE4)
m_cpuFeatures |= CPU_FEATURE_SSE4;
if (CPUInfo[CPUINFO_ECX] & CPUID_00000001_ECX_SSE42)
m_cpuFeatures |= CPU_FEATURE_SSE42;
}
__cpuid(CPUInfo, 0x80000000);
int MaxExtInfoType = CPUInfo[0];
if (MaxExtInfoType >= CPUID_INFOTYPE_EXTENDED)
{
__cpuid(CPUInfo, CPUID_INFOTYPE_EXTENDED);
if (CPUInfo[CPUINFO_EDX] & CPUID_80000001_EDX_MMX)
m_cpuFeatures |= CPU_FEATURE_MMX;
if (CPUInfo[CPUINFO_EDX] & CPUID_80000001_EDX_MMX2)
m_cpuFeatures |= CPU_FEATURE_MMX2;
if (CPUInfo[CPUINFO_EDX] & CPUID_80000001_EDX_3DNOW)
m_cpuFeatures |= CPU_FEATURE_3DNOW;
if (CPUInfo[CPUINFO_EDX] & CPUID_80000001_EDX_3DNOWEXT)
m_cpuFeatures |= CPU_FEATURE_3DNOWEXT;
}
#endif // ! _M_ARM
#elif defined(TARGET_DARWIN)
#if defined(__ppc__)
m_cpuFeatures |= CPU_FEATURE_ALTIVEC;
#elif defined(TARGET_DARWIN_IOS)
#else
size_t len = 512 - 1; // '-1' for trailing space
char buffer[512] ={0};
if (sysctlbyname("machdep.cpu.features", &buffer, &len, NULL, 0) == 0)
{
strcat(buffer, " ");
if (strstr(buffer,"MMX "))
m_cpuFeatures |= CPU_FEATURE_MMX;
if (strstr(buffer,"MMXEXT "))
m_cpuFeatures |= CPU_FEATURE_MMX2;
if (strstr(buffer,"SSE "))
m_cpuFeatures |= CPU_FEATURE_SSE;
if (strstr(buffer,"SSE2 "))
m_cpuFeatures |= CPU_FEATURE_SSE2;
if (strstr(buffer,"SSE3 "))
m_cpuFeatures |= CPU_FEATURE_SSE3;
if (strstr(buffer,"SSSE3 "))
m_cpuFeatures |= CPU_FEATURE_SSSE3;
if (strstr(buffer,"SSE4.1 "))
m_cpuFeatures |= CPU_FEATURE_SSE4;
if (strstr(buffer,"SSE4.2 "))
m_cpuFeatures |= CPU_FEATURE_SSE42;
if (strstr(buffer,"3DNOW "))
m_cpuFeatures |= CPU_FEATURE_3DNOW;
if (strstr(buffer,"3DNOWEXT "))
m_cpuFeatures |= CPU_FEATURE_3DNOWEXT;
}
else
m_cpuFeatures |= CPU_FEATURE_MMX;
#endif
#elif defined(LINUX)
// empty on purpose, the implementation is in the constructor
#elif !defined(__powerpc__) && !defined(__ppc__) && !defined(__arm__) && !defined(__aarch64__)
m_cpuFeatures |= CPU_FEATURE_MMX;
#elif defined(__powerpc__) || defined(__ppc__)
m_cpuFeatures |= CPU_FEATURE_ALTIVEC;
#endif
}
bool CCPUInfo::HasNeon()
{
static int has_neon = -1;
#if defined (TARGET_ANDROID)
if (has_neon == -1)
has_neon = (CAndroidFeatures::HasNeon()) ? 1 : 0;
#elif defined(TARGET_DARWIN_IOS)
has_neon = 1;
#elif defined(TARGET_LINUX) && defined(HAS_NEON)
#if defined(__LP64__)
has_neon = 1;
#else
if (has_neon == -1)
{
has_neon = 0;
// why are we not looking at the Features in
// /proc/cpuinfo for neon ?
int fd = open("/proc/self/auxv", O_RDONLY);
if (fd >= 0)
{
Elf32_auxv_t auxv;
while (read(fd, &auxv, sizeof(Elf32_auxv_t)) == sizeof(Elf32_auxv_t))
{
if (auxv.a_type == AT_HWCAP)
{
has_neon = (auxv.a_un.a_val & HWCAP_NEON) ? 1 : 0;
break;
}
}
close(fd);
}
}
#endif
#endif
return has_neon == 1;
}
CCPUInfo g_cpuInfo;