public class CPUFormulaDVFS implements CPUFormulasInterface {

	/**
	 * Thermal Design Power of the CPU (given by constructors)
	 */
	private double TDP;

	/**
	 * TDP factor for CMOS formula
	 * By default, it is at 0.7
	 */
	private double TDPFactor = 0.7;

	/**
	 * The CPU sensor used to collect data from the CPU
	 */
	private CPUSensorsInterface cpuSensor;

	/**
	 * The map of CPU frequencies and the corresponding CPU voltage
	 * <Frequency, Voltage>
	 */
	private Map<Double, Double> frequenciesVoltages;

	/**
	 * Array with the maximum frequency and voltage supported by CPU
	 */
	private Double[] frequencyVoltageMax;

	/**
	 * The map of CPU frequencies and their relevant CPU time
	 */
	private Map<Double, Double> frequenciesTimes;

	/**
	 * The map of CPU frequencies and their power consumption
	 */
	private Map<Double, Double> frequenciesPower;

	/**
	 * Constructor
	 * @param TDP The TDP of the CPU
	 * @param TDPFactor The TDP factor of the CMOS formula
	 * @param cpuSensor The CPU sensor
	 * @param frequenciesVoltages Map of CPU frequencies and their voltage
	 */
	public CPUFormulaDVFS(double TDP, double TDPFactor, CPUSensorsInterface cpuSensor, Map<Double, Double> frequenciesVoltages) {
		this.TDP = TDP;
		this.TDPFactor = TDPFactor;
		this.cpuSensor = cpuSensor;
		this.frequenciesVoltages = frequenciesVoltages;
		this.frequenciesTimes = new HashMap<Double, Double>();
		this.frequenciesPower = new HashMap<Double, Double>();
		this.compute();
	}

	private void compute() {
		this.frequencyVoltageMax = this.getMaxFrequencyVoltage();
	}

	/**
	 * Calculate the maximum frenquency and voltage of the CPU
	 * @return an array with 0 the maximum frequency and 1 the maximum voltage
	 */
	private Double[] getMaxFrequencyVoltage() {
		Double frequency = 0.0, voltage = 0.0;
		Double[] result = new Double[2];
		for (Map.Entry<Double, Double> entry : this.frequenciesVoltages.entrySet()) {
			if (entry.getKey() > frequency) {
				frequency = entry.getKey();
				voltage = entry.getValue();
			}
		}
		result[0] = frequency;
		result[1] = voltage;
		return result;
	}

	@Override
	public double getCPUPower() {
		// (TDP * TDPFactor) / (FrequencyMax * VoltageMax^2)
		double constant = (this.TDP * this.TDPFactor) / (this.frequencyVoltageMax[0] * Math.pow(this.frequencyVoltageMax[1], 2));

		Double powerFrequency;
		for (Map.Entry<Double, Double> entry : this.frequenciesVoltages.entrySet()) {
			powerFrequency = constant * entry.getKey() * (Math.pow(entry.getValue(), 2));
			this.frequenciesPower.put(entry.getKey(), powerFrequency);
		}

		// Fill or get from sensors the frequencies times
		this.frequenciesTimes= this.cpuSensor.getTimeInFrequencies();
		double totalTimes = 0.0;
		for (Map.Entry<Double, Double> entry : this.frequenciesTimes.entrySet()) {
			totalTimes += entry.getValue();
		}

		// Total power
		double totalPower = 0.0;
		for (Map.Entry<Double, Double> entry : this.frequenciesPower.entrySet()) {
			totalPower += entry.getValue() * this.frequenciesTimes.get(entry.getKey());
		}

		// Power CPU
		if (totalPower == 0)
			return 0;
		else
			return (totalPower / totalTimes) * this.cpuSensor.getProcessCPUUsagePercentage();
	}

} public class CPUFormulaMaxFrequency implements CPUFormulasInterface {

	/**
	 * Thermal Design Power of the CPU (given by constructors)
	 */
	private double TDP;

	/**
	 * TDP factor for CMOS formula
	 * By default, it is at 0.7
	 */
	private double TDPFactor = 0.7;

	/**
	 * The CPU sensor used to collect data from the CPU
	 */
	private CPUSensorsInterface cpuSensor;

	/**
	 * Constructor
	 * @param TDP The TDP of the CPU
	 * @param TDPFactor The TDP factor of the CMOS formula
	 * @param cpuSensor The CPU sensor
	 */
	public CPUFormulaMaxFrequency(double TDP, double TDPFactor, CPUSensorsInterface cpuSensor) {
		this.TDP = TDP;
		this.TDPFactor = TDPFactor;
		this.cpuSensor = cpuSensor;
	}

	@Override
	public double getCPUPower() {
		return (this.TDP * this.TDPFactor * this.cpuSensor.getProcessCPUUsagePercentage());
	}

} public interface CPUFormulasInterface {

	/**
	 * Get CPU power for CPU for pid
	 * @return CPU power for CPU for pid
	 */
	public double getCPUPower();

} public interface DiskFormulasInterface {

	/**
	 * Get disk power for writing and reading to disk for pid
	 * @return Disk power for writing and reading to disk for pid
	 */
	double getDiskPower();

} public class DiskFormulasProc implements DiskFormulasInterface {

	/**
	 * Disk read and write power, and read and write rate
	 * Data from configuration file (from hardware specifications)
	 */
	private Double diskReadPower, diskReadRate, diskWritePower, diskWriteRate;

	/**
	 * Power for reading/writing one byte
	 */
	private Double powerPerReadByte, powerPerWriteByte;

	/**
	 * Disk sensor
	 */
	private DiskSensorsInterface diskSensor;

	/**
	 * Constructor
	 * @param diskReadPower Disk read power
	 * @param diskReadRate Disk read rate
	 * @param diskWritePower Disk write power
	 * @param diskWriteRate Disk write rate
	 * @param diskSensor Disk sensor
	 */
	public DiskFormulasProc(Double diskReadPower, Double diskReadRate, Double diskWritePower, Double diskWriteRate, DiskSensorsInterface diskSensor) {
		this.diskReadPower = diskReadPower;
		this.diskReadRate = diskReadRate;
		this.diskWritePower = diskWritePower;
		this.diskWriteRate = diskWriteRate;
		this.diskSensor = diskSensor;
	}

	@Override
	public double getDiskPower() {
		this.powerPerReadByte = this.diskReadPower / (this.diskReadRate * 1000000);
		this.powerPerWriteByte = this.diskWritePower / (this.diskWriteRate * 1000000);

		Double[] readWriteBytes = this.diskSensor.getProcesDiskReadWriteBytes();
		return (readWriteBytes[0] * this.powerPerReadByte) + (readWriteBytes[1] * this.powerPerWriteByte);
	}

} public class CPUSensorDVFS implements CPUSensorsInterface {

	/**
	 * Number of cores in CPU, collected from sigar
	 */
	private int numberOfCores;

	/**
	 * Process PID to monitor
	 */
	private int pid;

	/**
	 * Map of CPU frequencies and the CPU time they spend in each
	 */
	private Map<Double, Double> timeInFrequencies;

	/**
	 * Previous CPU total time
	 * Previous CPU PID time
	 */
	private Long previousTotalTime, previousPIDTime;

	/**
	 * Check if monitoring has already started or not
	 * Used in calculating cpu percentage for the first time
	 */
	private boolean firstRun;

	/**
	 * Path to the time_in_state file where CPU time data is stored
	 * Linux-systems only
	 */
	private String timeInStatePath = "/sys/devices/system/cpu/cpu%?/cpufreq/stats/time_in_state";

	/**
	 * Constructor
	 * @param pid Process PID to monitor
	 * @param frequenciesMap The map of frequencies and voltage
	 */
	public CPUSensorDVFS(int pid, Map<Double, Double> frequenciesMap) {
		this.pid = pid;
		this.timeInFrequencies = frequenciesMap;
		this.numberOfCores = Runtime.getRuntime().availableProcessors();
		this.previousPIDTime = 0L;
		this.previousTotalTime = 0L;
		this.firstRun = true;
	}

	/**
	 * Get CPU total time
	 * @return CPU total time
	 */
	private Long getTotalTime() {
		Long result = 0L;

		try (BufferedReader br = new BufferedReader(new FileReader("/proc/stat"))) {
			// Read first line where total cpu time is, and split it
			// Example of line: cpu  586994 19195 123650 2813135 107565 3 2017 0 0 0
			String[] firstLine = br.readLine().split(" ");

			// Sum up all values, except first one which is "cpu" (index 0)
			// Index 1 is another space, so start at index 2
			for (int i=2; i<11; i++) {
				result += Long.valueOf(firstLine[i]);
			}
		} catch (Exception e) {
			Agent.LOGGER.log(Level.WARNING, e.getMessage());
		}

		return result;
	}

	/**
	 * Get CPU PID time
	 * @return CPU PID time
	 */
	private Long getPIDTime() {
		Long result = 0L;

		try (BufferedReader br = new BufferedReader(new FileReader("/proc/" + this.pid + "/stat"))) {
			// Read first line where cpu time is, and split it
			// We only need utime and stime (user and system time)
			// Example of line: 25152 (java) S 12564 1685 1685 0 -1 1077960704 155132 412 478 2 11617 1816 0 0 20 0 61 0 2001362 3813126144 99139 18446744073709551615 4194304 4196724 140736365379696 140736365362368 140056419567211 0 0 4096 16796879 18446744073709551615 0 0 17 2 0 0 3 0 0 6294960 6295616 13131776 140736365387745 140736365388341 140736365388341 140736365391821 0
			String[] firstLine = br.readLine().split(" ");

			// Get utime and stime and sum them together
			// utime is at index 13, stime at index 14
			result = Long.valueOf(firstLine[13]) + Long.valueOf(firstLine[14]);
		} catch (Exception e) {
			Agent.LOGGER.log(Level.WARNING, e.getMessage());
		}

		return result;
	}

	@Override
	public double getProcessCPUUsagePercentage() {
		Long totalTime = this.getTotalTime();
		Long pidTime = this.getPIDTime();

		Long totalDiff = totalTime - this.previousTotalTime;
		Long pidDiff = pidTime - this.previousPIDTime;

		double result = 0.0;

		if (!this.firstRun)
			result = ((double) pidDiff) / totalDiff; // / this.numberOfCores;
		else
			this.firstRun = false;

		this.previousPIDTime = pidTime;
		this.previousTotalTime = totalTime;

		return result;
	}

	@Override
	public Map<Double, Double> getTimeInFrequencies() {
		Map<Double, Double> timeInFrequenciesUpdated = new HashMap<Double, Double>();
		Map<Double, Double> timeInFrequenciesNew = this.getTimeInFrequenciesFromPath();

		for (Map.Entry<Double, Double> entry : this.timeInFrequencies.entrySet()) {
			Double frequency = entry.getKey();
			Double timeFreq = entry.getValue();
			if (timeInFrequenciesNew.get(frequency) != null)
				timeInFrequenciesUpdated.put(frequency, timeFreq - timeInFrequenciesNew.get(frequency));
			else
				timeInFrequenciesUpdated.put(frequency, timeFreq);
		}

		return timeInFrequenciesUpdated;
	}

	/**
	 * Calculate CPU time for each frequency
	 * This function only report the data contained in @timeInStatePath file
	 * This means, this is an aggregated data since start of CPU monitoring by Linux (and not for monitoring cycle)
	 * @return Map of CPU frequencies and the CPU time spend for each
	 */
	private Map<Double, Double> getTimeInFrequenciesFromPath() {
		Map<Double, Double> timeInFrequenciesCache = new HashMap<Double, Double>();

		for (int i = 0; i<this.numberOfCores; i++) {
			String timeInStatePathReal = timeInStatePath.replace("%?", "" + i);

			try (BufferedReader br = new BufferedReader(new FileReader(timeInStatePathReal))) {
				String currentLine = "";
				while ((currentLine = br.readLine()) != null) {
					String[] freqAndtime = currentLine.split(" ");
					Double frequency = Double.valueOf(freqAndtime[0]);
					Double timeFreq = Double.valueOf(freqAndtime[1]);

					if (timeInFrequenciesCache.containsKey(frequency))
						timeInFrequenciesCache.put(frequency, timeInFrequenciesCache.get(frequency) + timeFreq);
					else
						timeInFrequenciesCache.put(frequency, timeFreq);
				}
			} catch (IOException e) {
				Agent.LOGGER.log(Level.WARNING, e.getMessage());
			}
		}

		return timeInFrequenciesCache;
	}
} public class CPUSensorSigarMaxFrequency implements CPUSensorsInterface {

	/**
	 * Sigar object
	 */
	private Sigar sigar;

	/**
	 * Number of cores in CPU, collected from sigar
	 */
	private int numberOfCores;

	/**
	 * Process PID to monitor
	 */
	private int pid;

	/**
	 * Constructor
	 * @param pid Process PID to monitor
	 */
	public CPUSensorSigarMaxFrequency(int pid) {
		this.sigar = new Sigar();
		this.pid = pid;
		this.populateSigar();
	}

	/**
	 * Calculate number of cores in CPU
	 */
	private void populateSigar() {
		try {
			this.numberOfCores = this.sigar.getCpuInfoList()[0].getTotalCores();
		} catch (SigarException e) {
			Agent.LOGGER.log(Level.WARNING, e.getMessage());
		}
	}

	@Override
	public double getProcessCPUUsagePercentage() {
		try {
			return (this.sigar.getProcCpu(this.pid).getPercent() / this.numberOfCores);
		} catch (SigarException e) {
			Agent.LOGGER.log(Level.WARNING, e.getMessage());
			return 0.0;
		}
	}

	@Override
	public Map<Double, Double> getTimeInFrequencies() {
		return null;
	}

} public interface CPUSensorsInterface {

	/**
	 * Calculate percentage of CPU usage for PID
	 * @return the percentage of CPU usage by PID
	 */
	double getProcessCPUUsagePercentage();

	/**
	 * Calculate CPU time for each frequency
	 * @return Map of CPU frequencies and the CPU time spend for each
	 */
	Map<Double, Double> getTimeInFrequencies();

} public class DiskSensorProc implements DiskSensorsInterface {

	/**
	 * Process PID to monitor
	 */
	private int pid;

	/**
	 * Number of bytes read and written by PID to disk
	 */
	private Double readBytes, writeBytes;

	/**
	 * Path to the io file where disk data is stored
	 * Linux-systems only
	 */
	private String diskIOPath;

	/**
	 * Constructor
	 * @param pid Process PID to monitor
	 */
	public DiskSensorProc(int pid) {
		this.pid = pid;
		this.readBytes = 0.0;
		this.writeBytes = 0.0;
		this.diskIOPath = "/proc/" + pid + "/io";
	}

	@Override
	public Double[] getProcesDiskReadWriteBytes() {
		Double[] results = new Double[2];

		try {
			List<String> allLines = Files.readAllLines(Paths.get(this.diskIOPath), StandardCharsets.UTF_8);

			results[0] = Double.valueOf(allLines.get(4).split(" ")[1]) - this.readBytes;
			results[1] = Double.valueOf(allLines.get(5).split(" ")[1]) - Double.valueOf(allLines.get(6).split(" ")[1]) - this.writeBytes;

			this.readBytes = Double.valueOf(allLines.get(4).split(" ")[1]);
			this.writeBytes = Double.valueOf(allLines.get(5).split(" ")[1]) - Double.valueOf(allLines.get(6).split(" ")[1]);
		} catch (IOException e) {
			e.printStackTrace();
		}

		return results;
	}

} public interface DiskSensorsInterface {

	/**
	 * Get number of bytes read and written by pid
	 * @return An array with number of bytes read and written
	 * double[0] = read
	 * double[1] = write
	 */
	public Double[] getProcesDiskReadWriteBytes();

}
import jalen.formulas.cpu.CPUFormulaDVFS;
import jalen.formulas.cpu.CPUFormulaMaxFrequency;
import jalen.formulas.cpu.CPUFormulasInterface;
import jalen.formulas.disk.DiskFormulasInterface;
import jalen.formulas.disk.DiskFormulasProc;
import jalen.sensors.cpu.CPUSensorDVFS;
import jalen.sensors.cpu.CPUSensorSigarMaxFrequency;
import jalen.sensors.cpu.CPUSensorsInterface;
import jalen.sensors.disk.DiskSensorProc;
import jalen.sensors.disk.DiskSensorsInterface;
import java.io.*;
import java.lang.instrument.Instrumentation;
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;
import java.lang.reflect.Field;
import java.util.*;
import java.util.logging.ConsoleHandler;
import java.util.logging.Level;
import java.util.logging.Logger;
/*
 * Copyright (c) 2014, Inria, University Lille 1.
 * All rights reserved. This program and the accompanying materials
 * are made available under the terms of the GNU Affero General Public License v3.0
 * which accompanies this distribution, and is available at
 * https://www.gnu.org/licenses/agpl-3.0.html
 *
 * Author : Adel Noureddine
 */

package jalen;




public final class Agent {

	// Samples statistics

	// List of samplerList collected during the execution of the program
	private static List<SamplerList> samplerList = new ArrayList<>();

	private static Map<Long, Long> lastCPUTime = new HashMap<>();

	// List of method data
	private static Map<String, Double> methNetCPUEnergy = new HashMap<>();
	private static Map<String, Double> methNetLibraryCPUEnergy = new HashMap<>();

	private static Map<String, Integer> methNetCalls = new HashMap<>();
	private static Map<String, Integer> methNetLibraryCalls = new HashMap<>();

	private static Map<String, Double> methNetDiskEnergy = new HashMap<>();
	private static Map<String, Double> methNetLibraryDiskEnergy = new HashMap<>();

	private static Map<String, String> methNet = new HashMap<>();
	private static Map<String, String> methNetLibrary = new HashMap<>();

	private static String filterMethodName = "";
	private static String userDir = System.getProperty("user.dir");

	// For cycle time
	protected static Long lastCPUComputationTime = System.currentTimeMillis();
	private static int appCycleDuration = 500; // In milliseconds
	private static int jalenCycleDuration = 10; // In milliseconds

	// Sensors and formulas
	private static CPUSensorsInterface cpuSensor;
	protected static CPUFormulasInterface cpuFormula;
	private static DiskSensorsInterface diskSensor;