Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/openj9
Path: blob/master/runtime/compiler/env/CpuUtilization.cpp
6000 views
1
/*******************************************************************************
2
* Copyright (c) 2000, 2020 IBM Corp. and others
3
*
4
* This program and the accompanying materials are made available under
5
* the terms of the Eclipse Public License 2.0 which accompanies this
6
* distribution and is available at https://www.eclipse.org/legal/epl-2.0/
7
* or the Apache License, Version 2.0 which accompanies this distribution and
8
* is available at https://www.apache.org/licenses/LICENSE-2.0.
9
*
10
* This Source Code may also be made available under the following
11
* Secondary Licenses when the conditions for such availability set
12
* forth in the Eclipse Public License, v. 2.0 are satisfied: GNU
13
* General Public License, version 2 with the GNU Classpath
14
* Exception [1] and GNU General Public License, version 2 with the
15
* OpenJDK Assembly Exception [2].
16
*
17
* [1] https://www.gnu.org/software/classpath/license.html
18
* [2] http://openjdk.java.net/legal/assembly-exception.html
19
*
20
* 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-exception
21
*******************************************************************************/
22
23
#include "control/CompilationRuntime.hpp"
24
25
#include <stdint.h>
26
#include "jni.h"
27
#include "j9.h"
28
#include "j9port.h"
29
#include "control/Options.hpp"
30
#include "control/Options_inlines.hpp"
31
#include "env/CpuUtilization.hpp"
32
#include "env/VerboseLog.hpp"
33
34
/*
35
* Relevant port library API:
36
*
37
* IDATA j9sysinfo_get_CPU_utilization(struct J9PortLibrary *portLibrary, struct J9SysinfoCPUTime *cpuTime)
38
*
39
* typedef struct J9SysinfoCPUTime {
40
*
41
* I_64 timestamp; // time in nanoseconds from a fixed but arbitrary point in time
42
* I_64 cpuTime; // cumulative CPU utilization (sum of system and user time in nanoseconds) of all CPUs on the system.
43
* I_32 numberOfCpus; // number of CPUs as reported by the operating system
44
*
45
* } J9SysinfoCPUTime;
46
*
47
* I_64 j9thread_get_process_times(j9thread_process_time_t * processTime)
48
*
49
* typedef struct j9thread_process_time_t {
50
*
51
* // for consistency sake, times are stored as I_64s
52
* I_64 _systemTime; // system time used
53
* I_64 _userTime; // non-system time used
54
*
55
* } j9thread_process_time_t;
56
*/
57
58
int32_t CpuUtilization::getCpuUtil(J9JITConfig *jitConfig, J9SysinfoCPUTime *machineCpuStats, j9thread_process_time_t *vmCpuStats)
59
{
60
IDATA portLibraryStatusSys; // port lib call return value for system cpu usage
61
IDATA portLibraryStatusVm; // port lib call return value for vm cpu usage
62
63
// get access to portlib
64
PORT_ACCESS_FROM_JITCONFIG(jitConfig);
65
66
// get CPU stats for the machine
67
portLibraryStatusSys = j9sysinfo_get_CPU_utilization(machineCpuStats);
68
portLibraryStatusVm = j9thread_get_process_times(vmCpuStats);
69
70
// if either call failed, disable self
71
if (portLibraryStatusSys < 0 || portLibraryStatusVm < 0)
72
{
73
disable();
74
return (-1);
75
}
76
77
return 0;
78
}
79
80
/*
81
* Update the values stored inside the object. Only updates the values
82
* when called more than _minIntervalLength milliseconds after the last
83
* update.
84
*/
85
int CpuUtilization::updateCpuUtil(J9JITConfig *jitConfig)
86
{
87
// do nothing if turned off
88
if (!_isFunctional)
89
{
90
return (-1);
91
}
92
93
// portlib data
94
J9SysinfoCPUTime machineCpuStats; // stats for overall CPU usage on machine
95
j9thread_process_time_t vmCpuStats; // stats for VM's CPU usage
96
97
if (getCpuUtil(jitConfig, &machineCpuStats, &vmCpuStats) == -1)
98
return (-1);
99
100
// calculate interval
101
_prevIntervalLength = machineCpuStats.timestamp - _prevMachineUptime;
102
103
// calculate usage and idle percentages
104
if (_prevIntervalLength > 0)
105
{
106
int64_t prevTotalTimeUsedByVm = _prevVmSysTime + _prevVmUserTime;
107
int64_t newTotalTimeUsedByVm = vmCpuStats._systemTime + vmCpuStats._userTime;
108
109
_cpuUsage = (100 * (machineCpuStats.cpuTime - _prevMachineCpuTime)) / _prevIntervalLength;
110
_cpuIdle = 100 * machineCpuStats.numberOfCpus - _cpuUsage;
111
_vmCpuUsage = (100 * (newTotalTimeUsedByVm - prevTotalTimeUsedByVm)) / _prevIntervalLength;
112
}
113
114
if (machineCpuStats.numberOfCpus > 0)
115
{
116
_avgCpuUsage = _cpuUsage / machineCpuStats.numberOfCpus;
117
}
118
119
_avgCpuIdle = 100 - _avgCpuUsage;
120
121
// remember values for next time
122
_prevMachineUptime = machineCpuStats.timestamp;
123
_prevMachineCpuTime = machineCpuStats.cpuTime;
124
_prevVmSysTime = vmCpuStats._systemTime;
125
_prevVmUserTime = vmCpuStats._userTime;
126
127
return 0;
128
} // updateCpuUtil
129
130
int32_t CpuUtilization::updateCpuUsageCircularBuffer(J9JITConfig *jitConfig)
131
{
132
// do nothing if turned off
133
if (!_isFunctional || !_isCpuUsageCircularBufferFunctional)
134
{
135
return (-1);
136
}
137
138
// portlib data
139
J9SysinfoCPUTime machineCpuStats; // stats for overall CPU usage on machine
140
j9thread_process_time_t vmCpuStats; // stats for VM's CPU usage
141
142
if (getCpuUtil(jitConfig, &machineCpuStats, &vmCpuStats) == -1)
143
return (-1);
144
145
_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._timeStamp = machineCpuStats.timestamp;
146
_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._sampleSystemCpu = machineCpuStats.cpuTime;
147
_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._sampleJvmCpu = vmCpuStats._systemTime + vmCpuStats._userTime;
148
149
_cpuUsageCircularBufferIndex = (_cpuUsageCircularBufferIndex + 1)%_cpuUsageCircularBufferSize;
150
151
return 0;
152
153
} // updateCpuUsageArray
154
155
CpuUtilization::CpuUtilization(J9JITConfig *jitConfig):
156
157
// initialize usage to INITIAL_USAGE
158
_cpuUsage (INITIAL_USAGE),
159
_vmCpuUsage (INITIAL_USAGE),
160
_avgCpuUsage (INITIAL_USAGE),
161
_cpuIdle (INITIAL_IDLE),
162
_avgCpuIdle (INITIAL_IDLE),
163
164
_prevIntervalLength (0),
165
166
_prevMachineUptime (0),
167
_prevMachineCpuTime (0),
168
_prevVmSysTime (0),
169
_prevVmUserTime (0),
170
171
_isFunctional (true),
172
173
_cpuUsageCircularBufferIndex(0)
174
175
{
176
// If the circular buffer size is set to 0, disable the circular buffer
177
if (TR::Options::_cpuUsageCircularBufferSize == 0)
178
{
179
_isCpuUsageCircularBufferFunctional = false;
180
_cpuUsageCircularBuffer = NULL;
181
return;
182
}
183
184
_isCpuUsageCircularBufferFunctional = true;
185
186
// Enforce a minimum size
187
if (TR::Options::_cpuUsageCircularBufferSize < CPU_UTIL_ARRAY_DEFAULT_SIZE)
188
_cpuUsageCircularBufferSize = CPU_UTIL_ARRAY_DEFAULT_SIZE;
189
else
190
_cpuUsageCircularBufferSize = TR::Options::_cpuUsageCircularBufferSize;
191
192
// Allocate the memory for the buffer
193
_cpuUsageCircularBuffer =
194
(CpuUsageCircularBuffer *)TR_PersistentMemory::jitPersistentAlloc(sizeof(CpuUsageCircularBuffer)*_cpuUsageCircularBufferSize);
195
196
// Since this happens at bootstrap, if this fails, disable and return
197
if (!_cpuUsageCircularBuffer)
198
{
199
_isCpuUsageCircularBufferFunctional = false;
200
return;
201
}
202
203
// Initialize the buffer; no need to initialize the _sampleSystemCpu and _sampleJvmCpu
204
// fields since we can just check if _timeStamp equals 0
205
for (int32_t i = 0; i < _cpuUsageCircularBufferSize; i++)
206
{
207
_cpuUsageCircularBuffer[_cpuUsageCircularBufferIndex]._timeStamp = 0;
208
}
209
} // CpuUtilization
210
211
212
CpuSelfThreadUtilization::CpuSelfThreadUtilization(TR::PersistentInfo *persistentInfo, J9JITConfig *jitConfig, int64_t minPeriodNs, int32_t id)
213
: _jitConfig(jitConfig),
214
_persistentInfo(persistentInfo),
215
_minMeasurementIntervalLength(minPeriodNs),
216
_cpuTimeDuringLastInterval(-1), // Invalid value to avoid using it
217
_lastIntervalLength(1), // 1 to avoid a division by 0
218
_lastCpuUtil(-1), // Invalid value to avoid using it
219
_cpuTimeDuringSecondLastInterval(-1), // Invalid value, so avoid using it
220
_secondLastIntervalLength(1),
221
_secondLastCpuUtil(-1), // Invalid value, so avoid using it
222
_id(id),
223
_isFunctional(true) // optimistic
224
{
225
_lowResolutionClockAtLastUpdate = _persistentInfo->getElapsedTime();
226
_clockTimeAtLastUpdate = getCrtTimeNs();
227
_cpuTimeAtLastUpdate = 0; //getCpuTimeNow(); The constructor might not be called by the compilation thread
228
}
229
230
void CpuSelfThreadUtilization::setAsUnfunctional()
231
{
232
_isFunctional = false;
233
// set an invalid value which sometimes may obviate the need to test _isFunctional
234
_cpuTimeDuringLastInterval = _cpuTimeDuringSecondLastInterval = _cpuTimeAtLastUpdate = -1;
235
_lastCpuUtil = _secondLastCpuUtil = - 1;
236
// TODO: place a VM style trace point
237
}
238
239
bool CpuSelfThreadUtilization::update() // return true if an update was performed
240
{
241
if (!_isFunctional)
242
return false;
243
// Refuse to update if not enough time has passed;
244
// use the lower resolution time to avoid overhead
245
if ((_persistentInfo->getElapsedTime() - _lowResolutionClockAtLastUpdate)*1000000 < _minMeasurementIntervalLength)
246
return false;
247
int64_t currentThreadCpuTime = getCpuTimeNow();
248
if (currentThreadCpuTime < 0)
249
{
250
setAsUnfunctional(); // once we got a wrong value there is no point to try again
251
return false;
252
}
253
int64_t crtTime = getCrtTimeNs();
254
if (crtTime <= 0) // not likely, but let's make sure
255
{
256
setAsUnfunctional(); // once we got a wrong value there is no point to try again
257
return false;
258
}
259
// Propagate the old values
260
_secondLastIntervalLength = _lastIntervalLength;
261
_cpuTimeDuringSecondLastInterval = _cpuTimeDuringLastInterval;
262
_secondLastCpuUtil = _lastCpuUtil;
263
264
// Sanity checks regarding new values
265
//
266
int64_t elapsedTime = crtTime - _clockTimeAtLastUpdate;
267
int64_t elapsedCPU = currentThreadCpuTime - _cpuTimeAtLastUpdate;
268
int32_t cpuUtil;
269
// If time goes backwards, the CPU utilization will look negative (to avoid using it)
270
if (elapsedTime <= 0) // the equality test will also avoid division by zero later on
271
{
272
cpuUtil = -1;
273
}
274
else
275
{
276
// We cannot spend more than 100% on a thread
277
// However, we must allow for small imprecisions in time and CPU bookkeeping
278
// Thus, if CPU exceeds elapsed time by more than 10% set a value of -1 for cpuUtil
279
if (elapsedCPU > elapsedTime) // unlikely case
280
cpuUtil = (elapsedCPU > (elapsedTime * 11 / 10)) ? -1 : 100;
281
else
282
cpuUtil = (int32_t)(100 * elapsedCPU / elapsedTime);
283
}
284
// Store the new readouts
285
_lowResolutionClockAtLastUpdate = _persistentInfo->getElapsedTime();
286
_cpuTimeDuringLastInterval = elapsedCPU;
287
_lastIntervalLength = elapsedTime; // If time goes backwards, the CPU utilization will look negative
288
_lastCpuUtil = cpuUtil;
289
_cpuTimeAtLastUpdate = currentThreadCpuTime;
290
_clockTimeAtLastUpdate = crtTime;
291
return true;
292
}
293
294
//---------------------------- computeThreadCpuUtilOverLastNns ---------------------------
295
// Returns thread CPU utilization for the last two interval measurement periods
296
// only if these measurement periods are included in [crtTime-validInterval, crtTime]
297
// May return -1 in case of error
298
//------------------------------------------------------------------------------------
299
int32_t CpuSelfThreadUtilization::computeThreadCpuUtilOverLastNns(int64_t validInterval) const
300
{
301
int32_t cpuUtil = 0;
302
if (_lastCpuUtil < 0)
303
return -1; // error case
304
// If the last readout happened too much in the past, it should not be included
305
int64_t crtTimeNs = _persistentInfo->getElapsedTime() * 1000000;
306
int64_t lastValidTimeNs = crtTimeNs - validInterval;
307
int64_t lastIntervalEndNs = _lowResolutionClockAtLastUpdate * 1000000;
308
int64_t lastIntervalStartNs = lastIntervalEndNs - _lastIntervalLength;
309
if (lastIntervalStartNs < lastValidTimeNs)
310
{
311
return 0; // the thread may have accumulated some CPU cycles since the last readout, so 0 is an underestimate
312
}
313
else // I can include at least the last interval
314
{
315
// Account for the last metered interval
316
int64_t totalCPU = _cpuTimeDuringLastInterval;
317
int64_t totalTime = _lastIntervalLength;
318
319
320
// The interval between crtTimeNs and lastIntervalEndNs is not accounted for; if this interval
321
// is larger than the measurement period the thread might have gone to sleep and not
322
// had a chance to update its CPU utilization. Blindly assume a 0% duty cycle
323
if (crtTimeNs - lastIntervalEndNs > _minMeasurementIntervalLength)
324
{
325
totalTime += crtTimeNs - lastIntervalEndNs;
326
}
327
328
// Can I include the second last interval?
329
if (_secondLastCpuUtil >= 0) // yes
330
{
331
if (lastIntervalStartNs - _secondLastIntervalLength >= lastValidTimeNs)
332
{
333
totalCPU += _cpuTimeDuringSecondLastInterval;
334
totalTime += _secondLastIntervalLength;
335
}
336
}
337
return (int32_t)(100 * totalCPU / totalTime);
338
}
339
}
340
341
void CpuSelfThreadUtilization::printInfoToVlog() const
342
{
343
TR_VerboseLog::writeLineLocked(TR_Vlog_INFO, "t=%6u CPUid=%d: lastCheckPoint=%u, lastCpuUtil=%3d (interval=%d ms) prevCpuUtil=%3d (interval=%d ms)",
344
(uint32_t)_persistentInfo->getElapsedTime(),
345
getId(),
346
getLowResolutionClockAtLastUpdate(),
347
getThreadLastCpuUtil(),
348
(int32_t)(getLastMeasurementInterval()/1000000),
349
getThreadPrevCpuUtil(),
350
(int32_t)(getSecondLastMeasurementInterval()/1000000));
351
}
352
353
354
bool TR_CpuEntitlement::isHypervisorPresent()
355
{
356
if (_hypervisorPresent == TR_maybe)
357
{
358
// Caching works because we don't expect the information to change
359
// However, we must call this after portlib is up and running
360
PORT_ACCESS_FROM_JITCONFIG(_jitConfig);
361
if (j9hypervisor_hypervisor_present() > 0) // J9HYPERVISOR_NOT_PRESENT/J9HYPERVISOR_PRESENT/J9PORT_ERROR_HYPERVISOR_UNSUPPORTED
362
{
363
_hypervisorPresent = TR_yes;
364
}
365
else
366
{
367
_hypervisorPresent = TR_no;
368
}
369
}
370
return (_hypervisorPresent == TR_yes);
371
}
372
373
double TR_CpuEntitlement::computeGuestCpuEntitlement() const
374
{
375
// Caller must use isHypervisorPresent first to make sure we are running on a supported hypervisor
376
PORT_ACCESS_FROM_JITCONFIG(_jitConfig);
377
J9GuestProcessorUsage guestProcUsage;
378
if (0 == j9hypervisor_get_guest_processor_usage(&guestProcUsage))
379
return guestProcUsage.cpuEntitlement * 100;
380
else // error case
381
return 0.0;
382
}
383
384
void TR_CpuEntitlement::computeAndCacheCpuEntitlement()
385
{
386
PORT_ACCESS_FROM_JITCONFIG(_jitConfig);
387
_numTargetCpu = j9sysinfo_get_number_CPUs_by_type(J9PORT_CPU_TARGET);
388
if (_numTargetCpu == 0)
389
_numTargetCpu = 1; // some correction in case we get it wrong
390
uint32_t numTargetCpuEntitlement = _numTargetCpu * 100;
391
if (isHypervisorPresent())
392
{
393
_guestCpuEntitlement = computeGuestCpuEntitlement();
394
// If the number of target CPUs is smaller (bind the JVM to a subset of CPUs), use that value
395
if (numTargetCpuEntitlement < _guestCpuEntitlement || _guestCpuEntitlement <= 0)
396
_jvmCpuEntitlement = numTargetCpuEntitlement;
397
else
398
_jvmCpuEntitlement = _guestCpuEntitlement;
399
}
400
else
401
{
402
_jvmCpuEntitlement = numTargetCpuEntitlement;
403
}
404
}
405
406
407