AuroraRuntime/Source/HWInfo/AuCpuTimes.cpp

320 lines
8.6 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)
#if !defined(USER_HZ)
#define USER_HZ 100
#endif
#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)
USER_HZ
#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)
{
SysPushErrorGeneric(errno);
return -1;
}
return (error);
}
static void PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
auto uThreads = AuHwInfo::GetCPUInfo().uThreads;
AuList<long> cpuStates;
cpuStates.resize((uThreads + 1) * CPUSTATES);
size_t len { cpuStates.size() * sizeof(long) };
if (CpuSysctl("kern.cp_times", cpuStates.data(), &len) < 0)
{
return;
}
times.resize(uThreads);
for (AU_ITERATE_N(uCore, uThreads))
{
auto &coreTimes = times[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;
}
}
#elif defined(AURORA_IS_LINUX_DERIVED)
static bool PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
bool bStatus { true };
AuString statFile {};
if (!AuFS::ReadString("/proc/stat", statFile))
{
return false;
}
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;
times.push_back(coreTimes);
});
return bStatus;
}
#elif defined(AURORA_IS_MODERNNT_DERIVED)
static bool PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
#if defined(_AU_MASSIVE_CPUID)
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION processorInfo[256];
#else
SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION processorInfo[128];
#endif
if (!pNtQuerySystemInformation)
{
SysPushErrorFeatureMissing();
return false;
}
ULONG uOutLen {};
if (FAILED(pNtQuerySystemInformation(SystemProcessorPerformanceInformation,
&processorInfo,
sizeof(processorInfo),
&uOutLen)))
{
return false;
}
auto uCount = uOutLen / sizeof(SYSTEM_PROCESSOR_PERFORMANCE_INFORMATION);
if (!AuTryResize(times, uCount))
{
return false;
}
for (AU_ITERATE_N(uCore, uCount))
{
auto &coreTimes = times[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 true;
}
#elif defined(AURORA_IS_POSIX_DERIVED)
static bool PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
return false;
}
#else
static bool PlatformGetCoreTimes(AuList<CpuCoreTime> &times)
{
return false;
}
#endif
AUKN_SYM bool GetPerCoreCPUTime(AuList<CpuCoreTime> &times)
{
return PlatformGetCoreTimes(times);
}
}