Path: blob/master/runtime/compiler/env/CpuUtilization.cpp
6000 views
/*******************************************************************************1* Copyright (c) 2000, 2020 IBM Corp. and others2*3* This program and the accompanying materials are made available under4* the terms of the Eclipse Public License 2.0 which accompanies this5* distribution and is available at https://www.eclipse.org/legal/epl-2.0/6* or the Apache License, Version 2.0 which accompanies this distribution and7* is available at https://www.apache.org/licenses/LICENSE-2.0.8*9* This Source Code may also be made available under the following10* Secondary Licenses when the conditions for such availability set11* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU12* General Public License, version 2 with the GNU Classpath13* Exception [1] and GNU General Public License, version 2 with the14* OpenJDK Assembly Exception [2].15*16* [1] https://www.gnu.org/software/classpath/license.html17* [2] http://openjdk.java.net/legal/assembly-exception.html18*19* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0 OR GPL-2.0 WITH Classpath-exception-2.0 OR LicenseRef-GPL-2.0 WITH Assembly-exception20*******************************************************************************/2122#include "control/CompilationRuntime.hpp"2324#include <stdint.h>25#include "jni.h"26#include "j9.h"27#include "j9port.h"28#include "control/Options.hpp"29#include "control/Options_inlines.hpp"30#include "env/CpuUtilization.hpp"31#include "env/VerboseLog.hpp"3233/*34* Relevant port library API:35*36* IDATA j9sysinfo_get_CPU_utilization(struct J9PortLibrary *portLibrary, struct J9SysinfoCPUTime *cpuTime)37*38* typedef struct J9SysinfoCPUTime {39*40* I_64 timestamp; // time in nanoseconds from a fixed but arbitrary point in time41* I_64 cpuTime; // cumulative CPU utilization (sum of system and user time in nanoseconds) of all CPUs on the system.42* I_32 numberOfCpus; // number of CPUs as reported by the operating system43*44* } J9SysinfoCPUTime;45*46* I_64 j9thread_get_process_times(j9thread_process_time_t * processTime)47*48* typedef struct j9thread_process_time_t {49*50* // for consistency sake, times are stored as I_64s51* I_64 _systemTime; // system time used52* I_64 _userTime; // non-system time used53*54* } j9thread_process_time_t;55*/5657int32_t CpuUtilization::getCpuUtil(J9JITConfig *jitConfig, J9SysinfoCPUTime *machineCpuStats, j9thread_process_time_t *vmCpuStats)58{59IDATA portLibraryStatusSys; // port lib call return value for system cpu usage60IDATA portLibraryStatusVm; // port lib call return value for vm cpu usage6162// get access to portlib63PORT_ACCESS_FROM_JITCONFIG(jitConfig);6465// get CPU stats for the machine66portLibraryStatusSys = j9sysinfo_get_CPU_utilization(machineCpuStats);67portLibraryStatusVm = j9thread_get_process_times(vmCpuStats);6869// if either call failed, disable self70if (portLibraryStatusSys < 0 || portLibraryStatusVm < 0)71{72disable();73return (-1);74}7576return 0;77}7879/*80* Update the values stored inside the object. Only updates the values81* when called more than _minIntervalLength milliseconds after the last82* update.83*/84int CpuUtilization::updateCpuUtil(J9JITConfig *jitConfig)85{86// do nothing if turned off87if (!_isFunctional)88{89return (-1);90}9192// portlib data93J9SysinfoCPUTime machineCpuStats; // stats for overall CPU usage on machine94j9thread_process_time_t vmCpuStats; // stats for VM's CPU usage9596if (getCpuUtil(jitConfig, &machineCpuStats, &vmCpuStats) == -1)97return (-1);9899// calculate interval100_prevIntervalLength = machineCpuStats.timestamp - _prevMachineUptime;101102// calculate usage and idle percentages103if (_prevIntervalLength > 0)104{105int64_t prevTotalTimeUsedByVm = _prevVmSysTime + _prevVmUserTime;106int64_t newTotalTimeUsedByVm = vmCpuStats._systemTime + vmCpuStats._userTime;107108_cpuUsage = (100 * (machineCpuStats.cpuTime - _prevMachineCpuTime)) / _prevIntervalLength;109_cpuIdle = 100 * machineCpuStats.numberOfCpus - _cpuUsage;110_vmCpuUsage = (100 * (newTotalTimeUsedByVm - prevTotalTimeUsedByVm)) / _prevIntervalLength;111}112113if (machineCpuStats.numberOfCpus > 0)114{115_avgCpuUsage = _cpuUsage / machineCpuStats.numberOfCpus;116}117118_avgCpuIdle = 100 - _avgCpuUsage;119120// remember values for next time121_prevMachineUptime = machineCpuStats.timestamp;122_prevMachineCpuTime = machineCpuStats.cpuTime;123_prevVmSysTime = vmCpuStats._systemTime;124_prevVmUserTime = vmCpuStats._userTime;125126return 0;127} // updateCpuUtil128129int32_t CpuUtilization::updateCpuUsageCircularBuffer(J9JITConfig *jitConfig)130{131// do nothing if turned off132if (!_isFunctional || !_isCpuUsageCircularBufferFunctional)133{134return (-1);135}136137// portlib data138J9SysinfoCPUTime machineCpuStats; // stats for overall CPU usage on machine139j9thread_process_time_t vmCpuStats; // stats for VM's CPU usage140141if (getCpuUtil(jitConfig, &machineCpuStats, &vmCpuStats) == -1)142return (-1);143144_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._timeStamp = machineCpuStats.timestamp;145_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._sampleSystemCpu = machineCpuStats.cpuTime;146_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._sampleJvmCpu = vmCpuStats._systemTime + vmCpuStats._userTime;147148_cpuUsageCircularBufferIndex = (_cpuUsageCircularBufferIndex + 1)%_cpuUsageCircularBufferSize;149150return 0;151152} // updateCpuUsageArray153154CpuUtilization::CpuUtilization(J9JITConfig *jitConfig):155156// initialize usage to INITIAL_USAGE157_cpuUsage (INITIAL_USAGE),158_vmCpuUsage (INITIAL_USAGE),159_avgCpuUsage (INITIAL_USAGE),160_cpuIdle (INITIAL_IDLE),161_avgCpuIdle (INITIAL_IDLE),162163_prevIntervalLength (0),164165_prevMachineUptime (0),166_prevMachineCpuTime (0),167_prevVmSysTime (0),168_prevVmUserTime (0),169170_isFunctional (true),171172_cpuUsageCircularBufferIndex(0)173174{175// If the circular buffer size is set to 0, disable the circular buffer176if (TR::Options::_cpuUsageCircularBufferSize == 0)177{178_isCpuUsageCircularBufferFunctional = false;179_cpuUsageCircularBuffer = NULL;180return;181}182183_isCpuUsageCircularBufferFunctional = true;184185// Enforce a minimum size186if (TR::Options::_cpuUsageCircularBufferSize < CPU_UTIL_ARRAY_DEFAULT_SIZE)187_cpuUsageCircularBufferSize = CPU_UTIL_ARRAY_DEFAULT_SIZE;188else189_cpuUsageCircularBufferSize = TR::Options::_cpuUsageCircularBufferSize;190191// Allocate the memory for the buffer192_cpuUsageCircularBuffer =193(CpuUsageCircularBuffer *)TR_PersistentMemory::jitPersistentAlloc(sizeof(CpuUsageCircularBuffer)*_cpuUsageCircularBufferSize);194195// Since this happens at bootstrap, if this fails, disable and return196if (!_cpuUsageCircularBuffer)197{198_isCpuUsageCircularBufferFunctional = false;199return;200}201202// Initialize the buffer; no need to initialize the _sampleSystemCpu and _sampleJvmCpu203// fields since we can just check if _timeStamp equals 0204for (int32_t i = 0; i < _cpuUsageCircularBufferSize; i++)205{206_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._timeStamp = 0;207}208} // CpuUtilization209210211CpuSelfThreadUtilization::CpuSelfThreadUtilization(TR::PersistentInfo *persistentInfo, J9JITConfig *jitConfig, int64_t minPeriodNs, int32_t id)212: _jitConfig(jitConfig),213_persistentInfo(persistentInfo),214_minMeasurementIntervalLength(minPeriodNs),215_cpuTimeDuringLastInterval(-1), // Invalid value to avoid using it216_lastIntervalLength(1), // 1 to avoid a division by 0217_lastCpuUtil(-1), // Invalid value to avoid using it218_cpuTimeDuringSecondLastInterval(-1), // Invalid value, so avoid using it219_secondLastIntervalLength(1),220_secondLastCpuUtil(-1), // Invalid value, so avoid using it221_id(id),222_isFunctional(true) // optimistic223{224_lowResolutionClockAtLastUpdate = _persistentInfo->getElapsedTime();225_clockTimeAtLastUpdate = getCrtTimeNs();226_cpuTimeAtLastUpdate = 0; //getCpuTimeNow(); The constructor might not be called by the compilation thread227}228229void CpuSelfThreadUtilization::setAsUnfunctional()230{231_isFunctional = false;232// set an invalid value which sometimes may obviate the need to test _isFunctional233_cpuTimeDuringLastInterval = _cpuTimeDuringSecondLastInterval = _cpuTimeAtLastUpdate = -1;234_lastCpuUtil = _secondLastCpuUtil = - 1;235// TODO: place a VM style trace point236}237238bool CpuSelfThreadUtilization::update() // return true if an update was performed239{240if (!_isFunctional)241return false;242// Refuse to update if not enough time has passed;243// use the lower resolution time to avoid overhead244if ((_persistentInfo->getElapsedTime() - _lowResolutionClockAtLastUpdate)*1000000 < _minMeasurementIntervalLength)245return false;246int64_t currentThreadCpuTime = getCpuTimeNow();247if (currentThreadCpuTime < 0)248{249setAsUnfunctional(); // once we got a wrong value there is no point to try again250return false;251}252int64_t crtTime = getCrtTimeNs();253if (crtTime <= 0) // not likely, but let's make sure254{255setAsUnfunctional(); // once we got a wrong value there is no point to try again256return false;257}258// Propagate the old values259_secondLastIntervalLength = _lastIntervalLength;260_cpuTimeDuringSecondLastInterval = _cpuTimeDuringLastInterval;261_secondLastCpuUtil = _lastCpuUtil;262263// Sanity checks regarding new values264//265int64_t elapsedTime = crtTime - _clockTimeAtLastUpdate;266int64_t elapsedCPU = currentThreadCpuTime - _cpuTimeAtLastUpdate;267int32_t cpuUtil;268// If time goes backwards, the CPU utilization will look negative (to avoid using it)269if (elapsedTime <= 0) // the equality test will also avoid division by zero later on270{271cpuUtil = -1;272}273else274{275// We cannot spend more than 100% on a thread276// However, we must allow for small imprecisions in time and CPU bookkeeping277// Thus, if CPU exceeds elapsed time by more than 10% set a value of -1 for cpuUtil278if (elapsedCPU > elapsedTime) // unlikely case279cpuUtil = (elapsedCPU > (elapsedTime * 11 / 10)) ? -1 : 100;280else281cpuUtil = (int32_t)(100 * elapsedCPU / elapsedTime);282}283// Store the new readouts284_lowResolutionClockAtLastUpdate = _persistentInfo->getElapsedTime();285_cpuTimeDuringLastInterval = elapsedCPU;286_lastIntervalLength = elapsedTime; // If time goes backwards, the CPU utilization will look negative287_lastCpuUtil = cpuUtil;288_cpuTimeAtLastUpdate = currentThreadCpuTime;289_clockTimeAtLastUpdate = crtTime;290return true;291}292293//---------------------------- computeThreadCpuUtilOverLastNns ---------------------------294// Returns thread CPU utilization for the last two interval measurement periods295// only if these measurement periods are included in [crtTime-validInterval, crtTime]296// May return -1 in case of error297//------------------------------------------------------------------------------------298int32_t CpuSelfThreadUtilization::computeThreadCpuUtilOverLastNns(int64_t validInterval) const299{300int32_t cpuUtil = 0;301if (_lastCpuUtil < 0)302return -1; // error case303// If the last readout happened too much in the past, it should not be included304int64_t crtTimeNs = _persistentInfo->getElapsedTime() * 1000000;305int64_t lastValidTimeNs = crtTimeNs - validInterval;306int64_t lastIntervalEndNs = _lowResolutionClockAtLastUpdate * 1000000;307int64_t lastIntervalStartNs = lastIntervalEndNs - _lastIntervalLength;308if (lastIntervalStartNs < lastValidTimeNs)309{310return 0; // the thread may have accumulated some CPU cycles since the last readout, so 0 is an underestimate311}312else // I can include at least the last interval313{314// Account for the last metered interval315int64_t totalCPU = _cpuTimeDuringLastInterval;316int64_t totalTime = _lastIntervalLength;317318319// The interval between crtTimeNs and lastIntervalEndNs is not accounted for; if this interval320// is larger than the measurement period the thread might have gone to sleep and not321// had a chance to update its CPU utilization. Blindly assume a 0% duty cycle322if (crtTimeNs - lastIntervalEndNs > _minMeasurementIntervalLength)323{324totalTime += crtTimeNs - lastIntervalEndNs;325}326327// Can I include the second last interval?328if (_secondLastCpuUtil >= 0) // yes329{330if (lastIntervalStartNs - _secondLastIntervalLength >= lastValidTimeNs)331{332totalCPU += _cpuTimeDuringSecondLastInterval;333totalTime += _secondLastIntervalLength;334}335}336return (int32_t)(100 * totalCPU / totalTime);337}338}339340void CpuSelfThreadUtilization::printInfoToVlog() const341{342TR_VerboseLog::writeLineLocked(TR_Vlog_INFO, "t=%6u CPUid=%d: lastCheckPoint=%u, lastCpuUtil=%3d (interval=%d ms) prevCpuUtil=%3d (interval=%d ms)",343(uint32_t)_persistentInfo->getElapsedTime(),344getId(),345getLowResolutionClockAtLastUpdate(),346getThreadLastCpuUtil(),347(int32_t)(getLastMeasurementInterval()/1000000),348getThreadPrevCpuUtil(),349(int32_t)(getSecondLastMeasurementInterval()/1000000));350}351352353bool TR_CpuEntitlement::isHypervisorPresent()354{355if (_hypervisorPresent == TR_maybe)356{357// Caching works because we don't expect the information to change358// However, we must call this after portlib is up and running359PORT_ACCESS_FROM_JITCONFIG(_jitConfig);360if (j9hypervisor_hypervisor_present() > 0) // J9HYPERVISOR_NOT_PRESENT/J9HYPERVISOR_PRESENT/J9PORT_ERROR_HYPERVISOR_UNSUPPORTED361{362_hypervisorPresent = TR_yes;363}364else365{366_hypervisorPresent = TR_no;367}368}369return (_hypervisorPresent == TR_yes);370}371372double TR_CpuEntitlement::computeGuestCpuEntitlement() const373{374// Caller must use isHypervisorPresent first to make sure we are running on a supported hypervisor375PORT_ACCESS_FROM_JITCONFIG(_jitConfig);376J9GuestProcessorUsage guestProcUsage;377if (0 == j9hypervisor_get_guest_processor_usage(&guestProcUsage))378return guestProcUsage.cpuEntitlement * 100;379else // error case380return 0.0;381}382383void TR_CpuEntitlement::computeAndCacheCpuEntitlement()384{385PORT_ACCESS_FROM_JITCONFIG(_jitConfig);386_numTargetCpu = j9sysinfo_get_number_CPUs_by_type(J9PORT_CPU_TARGET);387if (_numTargetCpu == 0)388_numTargetCpu = 1; // some correction in case we get it wrong389uint32_t numTargetCpuEntitlement = _numTargetCpu * 100;390if (isHypervisorPresent())391{392_guestCpuEntitlement = computeGuestCpuEntitlement();393// If the number of target CPUs is smaller (bind the JVM to a subset of CPUs), use that value394if (numTargetCpuEntitlement < _guestCpuEntitlement || _guestCpuEntitlement <= 0)395_jvmCpuEntitlement = numTargetCpuEntitlement;396else397_jvmCpuEntitlement = _guestCpuEntitlement;398}399else400{401_jvmCpuEntitlement = numTargetCpuEntitlement;402}403}404405406407