AuroraRuntime/Source/HWInfo/AuCpuTimes.cpp

369 lines
9.8 KiB
C++

/***
Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved.
File: AuCpuTimes.cpp
Date: 2023-12-30
Author: Reece
***/
#include <Source/RuntimeInternal.hpp>
#include "AuCpuTimes.hpp"
#if defined(__FreeBSD__)
#include <errno.h>
#include <sys/sysctl.h>
#include <sys/resource.h>
#elif defined(AURORA_IS_XNU_DERIVED)
#include <sys/dkstat.h>
#elif defined(AURORA_IS_LINUX_DERIVED)
namespace Aurora::HWInfo
{
static AuUInt64 GetKernelFrequency()
{
static AuUInt64 gRet = 0;
if (!gRet)
{
gRet = sysconf(_SC_CLK_TCK);
}
return gRet;
}
}
#endif
namespace Aurora::HWInfo
{
static AuUInt64 ConvertTicks(AuUInt64 uTicks)
{
#if defined(AURORA_IS_MODERNNT_DERIVED)
return uTicks * 100ull;
#else
static const auto kFreq =
#if defined(AURORA_IS_LINUX_DERIVED)
GetKernelFrequency()
#else
CLOCKS_PER_SEC
#endif
;
if (kFreq == 10000000)
{
return uTicks * 100ull;
}
else if (kFreq == 1000000)
{
return uTicks * 1000ull;
}
else if (kFreq == 100000)
{
return uTicks * 10000ull;
}
else if (kFreq == 10000)
{
return uTicks * 100000ull;
}
else if (kFreq == 1000)
{
return uTicks * 1000000ull;
}
else if (kFreq == 100)
{
return uTicks * 10000000ull;
}
else if (kFreq == 100000000)
{
return uTicks * 10ull;
}
else if (kFreq == 1000000000ull)
{
return uTicks;
}
else
{
const long long uWhole = (uTicks / kFreq) * 1'000'000'000ull;
const long long uPart = (uTicks % kFreq) * 1'000'000'000ull / kFreq;
return uWhole + uPart;
}
#endif
}
#if defined(__FreeBSD__)
static int CpuSysctl(const char *pName, void *pBuffer, size_t *pLength)
{
int error = sysctlbyname(pName, pBuffer, pLength, NULL, 0);
if (error != 0 && errno != ENOMEM)
{
SysPushErrorHAL("CpuTimes: {}", errno);
return -1;
}
return (error);
}
static AuOptional<AuUInt32> PlatformGetCoreTimes(const AuMemoryViewWrite &times)
{
auto uThreads = AuHwInfo::GetCPUInfo().uThreads;
AuList<long> cpuStates;
if (!AuTryResize(cpuStates, (uThreads + 1) * CPUSTATES))
{
SysPushErrorMemory();
return {};
}
auto uArraySize = times.Count<CpuCoreTime>();
auto pArrayBase = times.Begin<CpuCoreTime>();
size_t len { cpuStates.size() * sizeof(long) };
if (CpuSysctl("kern.cp_times", cpuStates.data(), &len) < 0)
{
return {};
}
if (uThreads > uArraySize)
{
return {};
}
for (AU_ITERATE_N(uCore, uThreads))
{
auto &coreTimes = pArrayBase[uCore];
coreTimes.uKernelTime = cpuStates[(CPUSTATES * uCore) + CP_SYS];
#if defined(CP_INTR)
coreTimes.uInterruptTime = cpuStates[(CPUSTATES * uCore) + CP_INTR];
#endif
coreTimes.uUserTime = cpuStates[(CPUSTATES * uCore) + CP_USER];
coreTimes.uIdleTime = cpuStates[(CPUSTATES * uCore) + CP_IDLE];
coreTimes.uIdleTime = ConvertTicks(coreTimes.uIdleTime);
coreTimes.uInterruptTime = ConvertTicks(coreTimes.uInterruptTime);
coreTimes.uKernelTime = ConvertTicks(coreTimes.uKernelTime);
coreTimes.uUserTime = ConvertTicks(coreTimes.uUserTime);
coreTimes.uUptime = coreTimes.uKernelTime + coreTimes.uUserTime + coreTimes.uInterruptTime;
coreTimes.uAllPowerUpTime = coreTimes.uIdleTime +
coreTimes.uUptime;
}
return { (AuUInt32)uCount };
}
#elif defined(AURORA_IS_LINUX_DERIVED)
static AuOptional<AuUInt32> PlatformGetCoreTimes(const AuMemoryViewWrite &times)
{
AuUInt uIndex {};
AuString statFile {};
if (!AuFS::ReadString("/proc/stat", statFile))
{
return {};
}
auto uArraySize = times.Count<CpuCoreTime>();
auto pArrayBase = times.Begin<CpuCoreTime>();
AuParse::SplitNewlines(statFile, [&](const AuROString &line)
{
CpuCoreTime coreTimes;
if (!AuStartsWith(line, "cpu"))
{
return;
}
auto line2 = line;
auto intStrs = AuSplitString(line2, " ");
intStrs.erase(intStrs.begin());
if (intStrs.size() > 0)
{
if (auto result = AuParse::ParseUInt(intStrs[0]))
{
coreTimes.uUserTime = result.value();
}
}
if (intStrs.size() > 1)
{
if (auto result = AuParse::ParseUInt(intStrs[1]))
{
coreTimes.uUserTime += result.value();
}
}
if (intStrs.size() > 2)
{
if (auto result = AuParse::ParseUInt(intStrs[2]))
{
coreTimes.uKernelTime = result.value();
}
}
if (intStrs.size() > 3)
{
if (auto result = AuParse::ParseUInt(intStrs[3]))
{
coreTimes.uIdleTime = result.value();
}
}
if (intStrs.size() > 4)
{
if (auto result = AuParse::ParseUInt(intStrs[4]))
{
coreTimes.uKernelTime += result.value();
}
}
if (intStrs.size() > 5)
{
if (auto result = AuParse::ParseUInt(intStrs[5]))
{
coreTimes.uInterruptTime += result.value();
}
}
if (intStrs.size() > 6)
{
if (auto result = AuParse::ParseUInt(intStrs[6]))
{
coreTimes.uInterruptTime += result.value();
}
}
for (AU_ITERATE_N_TO_X(i, 7, intStrs.size()))
{
if (auto result = AuParse::ParseUInt(intStrs[i]))
{
coreTimes.uKernelTime += result.value();
}
}
coreTimes.uIdleTime = ConvertTicks(coreTimes.uIdleTime);
coreTimes.uInterruptTime = ConvertTicks(coreTimes.uInterruptTime);
coreTimes.uKernelTime = ConvertTicks(coreTimes.uKernelTime);
coreTimes.uUserTime = ConvertTicks(coreTimes.uUserTime);
coreTimes.uUptime = coreTimes.uKernelTime +
coreTimes.uUserTime +
coreTimes.uInterruptTime;
coreTimes.uAllPowerUpTime = coreTimes.uIdleTime +
coreTimes.uUptime;
pArrayBase[(uIndex++) % uArraySize] = coreTimes;
});
return { (AuUInt32)uIndex };
}
#elif defined(AURORA_IS_MODERNNT_DERIVED)
static AuOptional<AuUInt32> PlatformGetCoreTimes(const AuMemoryViewWrite &times)
{
#if defined(_AU_MASSIVE_CPUID)
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION processorInfo[256];
#else
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION processorInfo[128];
#endif
auto uArraySize = times.Count<CpuCoreTime>();
auto pArrayBase = times.Begin<CpuCoreTime>();
if (!pNtQuerySystemInformation)
{
SysPushErrorFeatureMissing();
return {};
}
ULONG uOutLen {};
if (FAILED(pNtQuerySystemInformation(SystemProcessorPerformanceInformation,
&processorInfo,
sizeof(processorInfo),
&uOutLen)))
{
return {};
}
auto uCount = uOutLen / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
if (uCount > uArraySize)
{
return {};
}
for (AU_ITERATE_N(uCore, uCount))
{
auto &coreTimes = pArrayBase[uCore];
coreTimes.uIdleTime = ConvertTicks(processorInfo[uCore].IdleTime.QuadPart);
coreTimes.uInterruptTime = 0;
coreTimes.uKernelTime = ConvertTicks(processorInfo[uCore].KernelTime.QuadPart);
coreTimes.uUserTime = ConvertTicks(processorInfo[uCore].UserTime.QuadPart);
coreTimes.uKernelTime -= coreTimes.uIdleTime;
coreTimes.uUptime = coreTimes.uKernelTime +
coreTimes.uUserTime +
coreTimes.uInterruptTime;
coreTimes.uAllPowerUpTime = coreTimes.uIdleTime +
coreTimes.uUptime;
}
return { (AuUInt32)uCount };
}
#elif defined(AURORA_IS_POSIX_DERIVED)
static AuOptional<AuUInt32> PlatformGetCoreTimes(const AuMemoryViewWrite &times)
{
return {};
}
#else
static AuOptional<AuUInt32> PlatformGetCoreTimes(const AuMemoryViewWrite &times)
{
return {};
}
#endif
AUKN_SYM AuOptional<AuUInt32> GetPerCoreCPUTimeEx(const AuMemoryViewWrite &arrayOfCpuCoreTimeTimes)
{
return PlatformGetCoreTimes(arrayOfCpuCoreTimeTimes);
}
AUKN_SYM bool GetPerCoreCPUTime(AuList<CpuCoreTime> &times)
{
if (!AuTryResize(times, 256))
{
SysPushErrorMemory();
return false;
}
if (auto ret = GetPerCoreCPUTimeEx(times))
{
times.resize(ret.Value());
return true;
}
else
{
return false;
}
}
}