Skip to content

Commit

Permalink
8265836: OperatingSystemImpl.getCpuLoad() returns incorrect CPU load …
Browse files Browse the repository at this point in the history
…inside a container

Co-authored-by: Shaojun Wang <jeffery.wsj@alibaba-inc.com>
Co-authored-by: Severin Gehwolf <sgehwolf@openjdk.org>
Reviewed-by: sgehwolf, ysuenaga
  • Loading branch information
3 people committed May 27, 2021
1 parent 10a6f5d commit ef368b3
Show file tree
Hide file tree
Showing 4 changed files with 88 additions and 12 deletions.
Expand Up @@ -51,6 +51,13 @@ Java_com_sun_management_internal_OperatingSystemImpl_getSingleCpuLoad0
return -1.0;
}

JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostTotalCpuTicks0
(JNIEnv *env, jobject mbean)
{
return -1.0;
}

JNIEXPORT jint JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostConfiguredCpuCount0
(JNIEnv *env, jobject mbean)
Expand Down
Expand Up @@ -61,6 +61,7 @@ static struct perfbuf {
} counters;

#define DEC_64 "%"SCNd64
#define NS_PER_SEC 1000000000

static void next_line(FILE *f) {
while (fgetc(f) != '\n');
Expand Down Expand Up @@ -363,6 +364,31 @@ Java_com_sun_management_internal_OperatingSystemImpl_getHostConfiguredCpuCount0
}
}

// Return the host cpu ticks since boot in nanoseconds
JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostTotalCpuTicks0
(JNIEnv *env, jobject mbean)
{
if (perfInit() == 0) {
if (get_totalticks(-1, &counters.cpuTicks) < 0) {
return -1;
} else {
long ticks_per_sec = sysconf(_SC_CLK_TCK);
jlong result = (jlong)counters.cpuTicks.total;
if (ticks_per_sec <= NS_PER_SEC) {
long scale_factor = NS_PER_SEC/ticks_per_sec;
result = result * scale_factor;
} else {
long scale_factor = ticks_per_sec/NS_PER_SEC;
result = result / scale_factor;
}
return result;
}
} else {
return -1;
}
}

JNIEXPORT jint JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostOnlineCpuCount0
(JNIEnv *env, jobject mbean)
Expand Down
Expand Up @@ -167,6 +167,13 @@ Java_com_sun_management_internal_OperatingSystemImpl_getSingleCpuLoad0
return -1.0;
}

JNIEXPORT jlong JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostTotalCpuTicks0
(JNIEnv *env, jobject mbean)
{
return -1.0;
}

JNIEXPORT jint JNICALL
Java_com_sun_management_internal_OperatingSystemImpl_getHostConfiguredCpuCount0
(JNIEnv *env, jobject mbean)
Expand Down
Expand Up @@ -42,6 +42,8 @@ class OperatingSystemImpl extends BaseOperatingSystemImpl

private static final int MAX_ATTEMPTS_NUMBER = 10;
private final Metrics containerMetrics;
private long usageTicks = 0; // used for cpu load calculation
private long totalTicks = 0; // used for cpu load calculation

OperatingSystemImpl(VMManagement vm) {
super(vm);
Expand Down Expand Up @@ -132,24 +134,56 @@ public long getMaxFileDescriptorCount() {
return getMaxFileDescriptorCount0();
}

private double getUsageDividesTotal(long usageTicks, long totalTicks) {
// If cpu quota or cpu shares are in effect calculate the cpu load
// based on the following formula (similar to how
// getCpuLoad0() is being calculated):
//
// | usageTicks - usageTicks' |
// ------------------------------
// | totalTicks - totalTicks' |
//
// where usageTicks' and totalTicks' are historical values
// retrieved via an earlier call of this method.
//
// Total ticks should be scaled to the container effective number
// of cpus, if cpu shares are in effect.
if (usageTicks < 0 || totalTicks <= 0) {
return -1;
}
long distance = usageTicks - this.usageTicks;
this.usageTicks = usageTicks;
long totalDistance = totalTicks - this.totalTicks;
this.totalTicks = totalTicks;

double systemLoad = 0.0;
if (distance > 0 && totalDistance > 0) {
systemLoad = ((double)distance) / totalDistance;
}
// Ensure the return value is in the range 0.0 -> 1.0
systemLoad = Math.max(0.0, systemLoad);
systemLoad = Math.min(1.0, systemLoad);
return systemLoad;
}

public double getCpuLoad() {
if (containerMetrics != null) {
long quota = containerMetrics.getCpuQuota();
long share = containerMetrics.getCpuShares();
long usageNanos = containerMetrics.getCpuUsage();
if (quota > 0) {
long periodLength = containerMetrics.getCpuPeriod();
long numPeriods = containerMetrics.getCpuNumPeriods();
long usageNanos = containerMetrics.getCpuUsage();
if (periodLength > 0 && numPeriods > 0 && usageNanos > 0) {
long elapsedNanos = TimeUnit.MICROSECONDS.toNanos(periodLength * numPeriods);
double systemLoad = (double) usageNanos / elapsedNanos;
// Ensure the return value is in the range 0.0 -> 1.0
systemLoad = Math.max(0.0, systemLoad);
systemLoad = Math.min(1.0, systemLoad);
return systemLoad;
}
return -1;
long quotaNanos = TimeUnit.MICROSECONDS.toNanos(quota * numPeriods);
return getUsageDividesTotal(usageNanos, quotaNanos);
} else if (share > 0) {
long hostTicks = getHostTotalCpuTicks0();
int totalCPUs = getHostOnlineCpuCount0();
int containerCPUs = getAvailableProcessors();
// scale the total host load to the actual container cpus
hostTicks = hostTicks * containerCPUs / totalCPUs;
return getUsageDividesTotal(usageNanos, hostTicks);
} else {
// If CPU quotas are not active then find the average system load for
// If CPU quotas and shares are not active then find the average system load for
// all online CPUs that are allowed to run this container.

// If the cpuset is the same as the host's one there is no need to iterate over each CPU
Expand Down Expand Up @@ -208,6 +242,8 @@ private boolean isCpuSetSameAsHostCpuSet() {
private native double getSingleCpuLoad0(int cpuNum);
private native int getHostConfiguredCpuCount0();
private native int getHostOnlineCpuCount0();
// CPU ticks since boot in nanoseconds
private native long getHostTotalCpuTicks0();

static {
initialize0();
Expand Down

1 comment on commit ef368b3

@openjdk-notifier
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please sign in to comment.