// Copyright 2014 The Bazel Authors. All rights reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package com.google.devtools.build.lib.util;
import static java.nio.charset.StandardCharsets.US_ASCII;
import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.collect.Iterables;
import com.google.common.io.Files;
import com.sun.management.OperatingSystemMXBean;
import java.io.File;
import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.lang.management.MemoryMXBean;
import java.util.Iterator;
/**
* Provides methods to measure the current resource usage of the current
* process. Also provides some convenience methods to obtain several system
* characteristics, like number of processors , total memory, etc.
*/
public final class ResourceUsage {
/*
* Use com.sun.management.OperatingSystemMXBean instead of
* java.lang.management.OperatingSystemMXBean because the latter does not
* support getTotalPhysicalMemorySize() and getFreePhysicalMemorySize().
*/
private static final OperatingSystemMXBean OS_BEAN =
(OperatingSystemMXBean) ManagementFactory.getOperatingSystemMXBean();
private static final MemoryMXBean MEM_BEAN = ManagementFactory.getMemoryMXBean();
private static final Splitter WHITESPACE_SPLITTER = Splitter.on(CharMatcher.whitespace());
/**
* Calculates an estimate of the current total CPU usage and the CPU usage of
* the process in percent measured from the two given measurements. The
* returned CPU usages rea average values for the time between the two
* measurements. The returned array contains the total CPU usage at index 0
* and the CPU usage of the measured process at index 1.
*/
public static float[] calculateCurrentCpuUsage(Measurement oldMeasurement,
Measurement newMeasurement) {
if (oldMeasurement == null) {
return new float[2];
}
long idleJiffies =
newMeasurement.getTotalCpuIdleTimeInJiffies()
- oldMeasurement.getTotalCpuIdleTimeInJiffies();
long oldProcessJiffies =
oldMeasurement.getCpuUtilizationInJiffies()[0]
+ oldMeasurement.getCpuUtilizationInJiffies()[1];
long newProcessJiffies =
newMeasurement.getCpuUtilizationInJiffies()[0]
+ newMeasurement.getCpuUtilizationInJiffies()[1];
long processJiffies = newProcessJiffies - oldProcessJiffies;
long elapsedTimeJiffies =
newMeasurement.getTimeInJiffies() - oldMeasurement.getTimeInJiffies();
int processors = getAvailableProcessors();
// TODO(bazel-team): Sometimes smaller then zero. Not sure why.
double totalUsage = Math.max(0, 1.0D - (double) idleJiffies / elapsedTimeJiffies / processors);
double usage = Math.max(0, (double) processJiffies / elapsedTimeJiffies / processors);
return new float[] {(float) totalUsage * 100, (float) usage * 100};
}
private ResourceUsage() {
}
/**
* Returns the number of processors available to the Java virtual machine.
*/
public static int getAvailableProcessors() {
return OS_BEAN.getAvailableProcessors();
}
/**
* Returns the total physical memory in bytes.
*/
public static long getTotalPhysicalMemorySize() {
return OS_BEAN.getTotalPhysicalMemorySize();
}
/**
* Returns the operating system architecture.
*/
public static String getOsArchitecture() {
return OS_BEAN.getArch();
}
/**
* Returns the operating system name.
*/
public static String getOsName() {
return OS_BEAN.getName();
}
/**
* Returns the operating system version.
*/
public static String getOsVersion() {
return OS_BEAN.getVersion();
}
/**
* Returns the initial size of heap memory in bytes.
*
* @see MemoryMXBean#getHeapMemoryUsage()
*/
public static long getHeapMemoryInit() {
return MEM_BEAN.getHeapMemoryUsage().getInit();
}
/**
* Returns the initial size of non heap memory in bytes.
*
* @see MemoryMXBean#getNonHeapMemoryUsage()
*/
public static long getNonHeapMemoryInit() {
return MEM_BEAN.getNonHeapMemoryUsage().getInit();
}
/**
* Returns the maximum size of heap memory in bytes.
*
* @see MemoryMXBean#getHeapMemoryUsage()
*/
public static long getHeapMemoryMax() {
return MEM_BEAN.getHeapMemoryUsage().getMax();
}
/**
* Returns the maximum size of non heap memory in bytes.
*
* @see MemoryMXBean#getNonHeapMemoryUsage()
*/
public static long getNonHeapMemoryMax() {
return MEM_BEAN.getNonHeapMemoryUsage().getMax();
}
/**
* Returns a measurement of the current resource usage of the current process.
*/
public static Measurement measureCurrentResourceUsage() {
return measureCurrentResourceUsage("self");
}
/**
* Returns a measurement of the current resource usage of the process with the
* given process id.
*
* @param processId the process id or self
for the current
* process.
*/
public static Measurement measureCurrentResourceUsage(String processId) {
return new Measurement(MEM_BEAN.getHeapMemoryUsage().getUsed(), MEM_BEAN.getHeapMemoryUsage()
.getCommitted(), MEM_BEAN.getNonHeapMemoryUsage().getUsed(), MEM_BEAN
.getNonHeapMemoryUsage().getCommitted(), (float) OS_BEAN.getSystemLoadAverage(), OS_BEAN
.getFreePhysicalMemorySize(), getCurrentTotalIdleTimeInJiffies(),
getCurrentCpuUtilizationInJiffies(processId));
}
/**
* Returns the current total idle time of the processors since system boot.
* Reads /proc/stat to obtain this information.
*/
private static long getCurrentTotalIdleTimeInJiffies() {
try {
File file = new File("/proc/stat");
String content = Files.toString(file, US_ASCII);
String value = Iterables.get(WHITESPACE_SPLITTER.split(content), 5);
return Long.parseLong(value);
} catch (NumberFormatException | IOException e) {
return 0L;
}
}
/**
* Returns the current cpu utilization of the current process with the given
* id in jiffies. The returned array contains the following information: The
* 1st entry is the number of jiffies that the process has executed in user
* mode, and the 2nd entry is the number of jiffies that the process has
* executed in kernel mode. Reads /proc/self/stat to obtain this information.
*
* @param processId the process id or self
for the current
* process.
*/
private static long[] getCurrentCpuUtilizationInJiffies(String processId) {
try {
File file = new File("/proc/" + processId + "/stat");
if (file.isDirectory()) {
return new long[2];
}
Iterator stat = WHITESPACE_SPLITTER.split(
Files.toString(file, US_ASCII)).iterator();
for (int i = 0; i < 13; ++i) {
stat.next();
}
long token13 = Long.parseLong(stat.next());
long token14 = Long.parseLong(stat.next());
return new long[] { token13, token14 };
} catch (NumberFormatException | IOException e) {
return new long[2];
}
}
/**
* A snapshot of the resource usage of the current process at a point in time.
*/
public static class Measurement {
private final long timeInNanos;
private final long heapMemoryUsed;
private final long heapMemoryCommitted;
private final long nonHeapMemoryUsed;
private final long nonHeapMemoryCommitted;
private final float loadAverageLastMinute;
private final long freePhysicalMemory;
private final long totalCpuIdleTimeInJiffies;
private final long[] cpuUtilizationInJiffies;
public Measurement(long heapMemoryUsed, long heapMemoryCommitted, long nonHeapMemoryUsed,
long nonHeapMemoryCommitted, float loadAverageLastMinute, long freePhysicalMemory,
long totalCpuIdleTimeInJiffies, long[] cpuUtilizationInJiffies) {
super();
timeInNanos = System.nanoTime();
this.heapMemoryUsed = heapMemoryUsed;
this.heapMemoryCommitted = heapMemoryCommitted;
this.nonHeapMemoryUsed = nonHeapMemoryUsed;
this.nonHeapMemoryCommitted = nonHeapMemoryCommitted;
this.loadAverageLastMinute = loadAverageLastMinute;
this.freePhysicalMemory = freePhysicalMemory;
this.totalCpuIdleTimeInJiffies = totalCpuIdleTimeInJiffies;
this.cpuUtilizationInJiffies = cpuUtilizationInJiffies;
}
/**
* Returns the time of the measurement in jiffies.
*/
public long getTimeInJiffies() {
return timeInNanos / 10000000;
}
/**
* Returns the time of the measurement in ms.
*/
public long getTimeInMs() {
return timeInNanos / 1000000;
}
/**
* Returns the amount of used heap memory in bytes at the time of
* measurement.
*
* @see MemoryMXBean#getHeapMemoryUsage()
*/
public long getHeapMemoryUsed() {
return heapMemoryUsed;
}
/**
* Returns the amount of used non heap memory in bytes at the time of
* measurement.
*
* @see MemoryMXBean#getNonHeapMemoryUsage()
*/
public long getHeapMemoryCommitted() {
return heapMemoryCommitted;
}
/**
* Returns the amount of memory in bytes that is committed for the Java
* virtual machine to use for the heap at the time of measurement.
*
* @see MemoryMXBean#getHeapMemoryUsage()
*/
public long getNonHeapMemoryUsed() {
return nonHeapMemoryUsed;
}
/**
* Returns the amount of memory in bytes that is committed for the Java
* virtual machine to use for non heap memory at the time of measurement.
*
* @see MemoryMXBean#getNonHeapMemoryUsage()
*/
public long getNonHeapMemoryCommitted() {
return nonHeapMemoryCommitted;
}
/**
* Returns the system load average for the last minute at the time of
* measurement.
*
* @see OperatingSystemMXBean#getSystemLoadAverage()
*/
public float getLoadAverageLastMinute() {
return loadAverageLastMinute;
}
/**
* Returns the free physical memmory in bytes at the time of measurement.
*/
public long getFreePhysicalMemory() {
return freePhysicalMemory;
}
/**
* Returns the current total cpu idle since system boot in jiffies.
*/
public long getTotalCpuIdleTimeInJiffies() {
return totalCpuIdleTimeInJiffies;
}
/**
* Returns the current cpu utilization of the current process in jiffies.
* The returned array contains the following information: The 1st entry is
* the number of jiffies that the process has executed in user mode, and the
* 2nd entry is the number of jiffies that the process has executed in
* kernel mode. Reads /proc/self/stat to obtain this information.
*/
public long[] getCpuUtilizationInJiffies() {
return cpuUtilizationInJiffies;
}
/**
* Returns the current cpu utilization of the current process in ms. The
* returned array contains the following information: The 1st entry is the
* number of ms that the process has executed in user mode, and the 2nd
* entry is the number of ms that the process has executed in kernel mode.
* Reads /proc/self/stat to obtain this information.
*/
public long[] getCpuUtilizationInMs() {
return new long[] {cpuUtilizationInJiffies[0] * 10, cpuUtilizationInJiffies[1] * 10};
}
}
}