Performance monitor

From JPCT
Revision as of 22:04, 3 February 2013 by Thomas. (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

By this code you can record CPU core load and VM memory usage. The code is tested and works on Android and should works also on Linux, but not on Windows.

/
/**
 * Performance monitor provide you record of CPU cores load and memory usage. Based on <a
 * href="http://kernel.org/doc/Documentation/filesystems/proc.txt">kernel documentation</a>.
 * 
 * @author <a href="http://www.youtube.com/ChladekTomas">Tomáš Chládek</a>
 * @author <a href="http://makingmoneywithandroid.com/forum/member.php?action=profile&uid=1">david</a>
 */
public class PerformanceMonitor extends Thread {
	private RandomAccessFile readStream;
	private final int[][] recordCore;
	private final long[] coreLoadSum;
	private final int[] recordMemory;
	private long memoryUsageCount;
	private final int recordCount;
	private int recordIndex;
	private final long[] totalLast, idleLast;
	private final int updateInterval;
	private final long maxMemory;
	private final int coreCount;
	private final int[] coreLoadMean;
	private int memoryUsageMean;

	private final PerformanceRecord record;
	private int recordPackageIndex;
	private int recordsToSend;

	/**
	 * Creates a new PerformanceMonitor. To start recording you have to call start() method. Number of records is recordTime /
	 * updateInterval.
	 * 
	 * @param recordTime
	 *            record length of CPU cores load and memory usage [ms]
	 * @param updateInterval
	 *            interval between updates [ms]
	 */
	public PerformanceMonitor(int recordTime, int updateInterval) {
		recordsToSend = coreCount = getNumCores();
		recordCount = recordTime / updateInterval;
		recordCore = new int[recordCount][coreCount];
		recordMemory = new int[recordCount];
		this.updateInterval = updateInterval;
		totalLast = new long[coreCount];
		idleLast = new long[coreCount];
		coreLoadSum = new long[coreCount];
		coreLoadMean = new int[coreCount];
		maxMemory = Runtime.getRuntime().maxMemory() / 100;
		record = new PerformanceRecord(coreCount);
	}

	@Override
	public void run() {
		try {
			while (true) {
				recordIndex++;
				if (recordIndex == recordCount)
					recordIndex = 0;
				readCoreLoad();
				readMemoryUsage();
				Thread.sleep(updateInterval);
			}
		} catch (InterruptedException e) {
		}
	}

	/**
	 * Gets the number of physical cores. This includes all cores, not just the ones running at time when method is called.
	 * 
	 * @return number of cores
	 */
	public int getCoreCount() {
		return coreCount;
	}

	/**
	 * Gets the last core load.
	 * 
	 * @param index
	 *            which core do you want?
	 * @return core load [0...100] or -1 if index is out of range [0...PerformanceMonitor.getCoreCount()]
	 */
	public int getLastCoreLoad(int index) {
		if (index >= 0 && index < coreCount)
			return recordCore[recordIndex][index];
		return -1;
	}

	/**
	 * Gets the last memory usage.
	 * 
	 * @return memory usage [0...100]
	 */
	public int getLastMemoryUsage() {
		return recordMemory[recordIndex];
	}

	public PerformanceRecord getNextRecord() {
		recordPackageIndex++;
		if (recordPackageIndex == recordCount)
			recordPackageIndex = 0;
		record.coreLoad = recordCore[recordPackageIndex];
		record.memoryUsage = recordMemory[recordPackageIndex];
		record.recordNumb = recordCount - recordsToSend;
		recordsToSend--;
		return record;
	}

	public boolean hasMoreRecords() {
		if (recordPackageIndex == -1)
			recordPackageIndex = recordIndex;
		if (recordsToSend > 0)
			return true;
		recordPackageIndex = -1;
		recordsToSend = recordCount;
		return false;
	}

	private void readCoreLoad() {
		try {
			readStream = new RandomAccessFile("/proc/stat", "r");
			readStream.readLine();
			int currCoreCount = Runtime.getRuntime().availableProcessors();
			for (int i = 0; i < currCoreCount; i++) {
				String line = readStream.readLine();
				String[] toks = line.split(" ");
				long total = Long.parseLong(toks[1]) + Long.parseLong(toks[2]) + Long.parseLong(toks[3]) + Long.parseLong(toks[5])
						+ Long.parseLong(toks[6]) + Long.parseLong(toks[7]);
				long idle = Long.parseLong(toks[4]);
				int load = (int) ((total - totalLast[i]) * 100f / (total - totalLast[i] + idle - idleLast[i]));
				if (load < 0) // avoid crazy values, when is turn on or turn of CPU core
					load = 0;
				else if (load > 100)
					load = 100;
				coreLoadSum[i] -= recordCore[recordIndex][i];
				recordCore[recordIndex][i] = load;
				totalLast[i] = total;
				idleLast[i] = idle;
				coreLoadSum[i] += load;
				coreLoadMean[i] = (int) (coreLoadSum[i] / recordCount);
			}
			readStream.close();
			if (currCoreCount < coreCount)
				for (int i = currCoreCount; i < coreCount; i++) {
					coreLoadSum[i] -= recordCore[recordIndex][i];
					recordCore[recordIndex][i] = 0;
					coreLoadMean[i] = (int) (coreLoadSum[i] / recordCount);
				}
		} catch (IOException e) {
		}
	}

	private void readMemoryUsage() {
		int memUsage = (int) ((Runtime.getRuntime().totalMemory() - Runtime.getRuntime().freeMemory()) / maxMemory);
		memoryUsageCount -= recordMemory[recordIndex];
		recordMemory[recordIndex] = memUsage;
		memoryUsageCount += memUsage;
		memoryUsageMean = (int) (memoryUsageCount / recordCount);
	}

	/**
	 * Gets the number of cores available in this device, across all processors. Requires: Ability to peruse the filesystem at
	 * "/sys/devices/system/cpu"
	 * 
	 * @return The number of cores, or 1 if failed to get result
	 * 
	 * @author david
	 */
	private int getNumCores() {
		class CpuFilter implements FileFilter { // Private Class to display only CPU devices in the directory listing
			@Override
			public boolean accept(File pathname) {
				if (Pattern.matches("cpu[0-9]", pathname.getName())) // Check if filename is "cpu", followed by a single digit number
					return true;
				return false;
			}
		}
		try {
			File dir = new File("/sys/devices/system/cpu/"); // Get directory containing CPU info
			File[] files = dir.listFiles(new CpuFilter()); // Filter to only list the devices we care about
			return files.length; // Return the number of cores (virtual CPU devices)
		} catch (Exception e) {
			return 1;
		}
	}

	public class PerformanceRecord {
		protected int[] coreLoad;
		protected int memoryUsage;
		protected int recordNumb;

		protected PerformanceRecord(int coreCount) {
			coreLoad = new int[coreCount];
		}

		/**
		 * Gets the core load.
		 * 
		 * @param index
		 *            which core load do you want?
		 * @return core load [0...100] or -1 if index is out of range [0...PerformanceMonitor.getCoreCount()]
		 */
		public int getCoreLoad(int index) {
			if (index >= 0 && index < coreCount)
				return coreLoad[index];
			return -1;
		}

		/**
		 * Gets memory usage.
		 * 
		 * @return memory usage [0...100]
		 */
		public int getMemoryUsage() {
			return memoryUsage;
		}

		/**
		 * Number of this record.
		 * 
		 * @return
		 */
		public int getRecordNumb() {
			return recordNumb;
		}

		/**
		 * Gets mean of core load.
		 * 
		 * @param index
		 *            which mean of core do you want? [0...getCoreCount()]
		 * @return core load [0...100]
		 */
		public int getCoreLoadMean(int index) {
			return coreLoadMean[index];
		}

		/**
		 * Gets the mean of memory usage.
		 * 
		 * @return memory usage [0...100]
		 */
		public int getMemoryUsageMean() {
			return memoryUsageMean;
		}
	}
}