/*** Copyright (C) 2023 J Reece Wilson (a/k/a "Reece"). All rights reserved. File: AuCpuTimes.cpp Date: 2023-12-30 Author: Reece ***/ #include #include "AuCpuTimes.hpp" #if defined(__FreeBSD__) #include #include #include #elif defined(AURORA_IS_XNU_DERIVED) #include #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 ×) { auto uThreads = AuHwInfo::GetCPUInfo().uThreads; AuList 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 ×) { 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 ×) { #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 ×) { return false; } #else static bool PlatformGetCoreTimes(AuList ×) { return false; } #endif AUKN_SYM bool GetPerCoreCPUTime(AuList ×) { return PlatformGetCoreTimes(times); } }