package org.myrobotlab.runtime; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import org.myrobotlab.codec.CodecUtils; import org.myrobotlab.logging.LoggerFactory; import org.myrobotlab.logging.Logging; import org.myrobotlab.logging.LoggingFactory; import org.slf4j.Logger; public class ProcParser { static public final String CPU = "CPU"; static public final String MEMORY = "MEMORY"; static public final String DISK = "DISK"; static public final String NETWORK = "NETWORK"; /* * The pid of the process - not thread safe */ static private int processPid = -1; /* * The line which the number of cpu cores is located in /proc/cpuinfo in * kernel 2.6.32-34-generic. */ public static final int cpucoresline = 12; /* * Constant access path string. Those with 'pid' before are in /proc/[pid]/; * Those with 'net' are in /proc/net/. Those without are directly in /proc/. */ public static final String pidStatmPath = "/proc/#/statm"; public static final String pidStatPath = "/proc/#/stat"; public static final String statPath = "/proc/stat"; public static final String cpuinfoPath = "/proc/cpuinfo"; public static final String meminfoPath = "/proc/meminfo"; public static final String netdevPath = "/proc/net/dev"; public static final String partitionsPath = "/proc/partitions"; public static final String diskstatsPath = "/proc/diskstats"; public static final String EMPTY = ""; public static final String COLON = ":"; public static final String SPACE = " "; public static final String SHARP = "#"; public static final String LINE_SEPARATOR = "line.separator"; public final static Logger log = LoggerFactory.getLogger(ProcParser.class); public static Integer getArmInstructionVersion() { String[] tempData = null; String[] tempFile = null; Integer ret = 6; // Parse /proc/cpuinfo to obtain how many cores the CPU has. String cpuInfo = getContents(cpuinfoPath); if (cpuInfo != null) { tempFile = cpuInfo.split(System.getProperty(LINE_SEPARATOR)); for (String line : tempFile) { if (line.contains("Processor")) { tempData = line.split(COLON); break; } } if (tempData == null) { log.error("proc data not found - not a Linux system?"); return null; } if (tempData.length == 2) { String idata = tempData[1]; int pos0 = idata.indexOf("ARMv"); if (pos0 > 0) { String vdata = idata.substring(pos0 + 4, pos0 + 5); try { ret = Integer.parseInt(vdata); } catch (Exception e) { Logging.logError(e); } } } } return ret; } /** * Fetch the entire contents of a text file, and return it in a String. This * style of implementation does not throw Exceptions to the caller. * * @param path * is a file which already exists and can be read. * @throws IOException */ static private synchronized String getContents(String path) { // ...checks on aFile are elided StringBuilder contents = new StringBuilder(); try { // use buffering, reading one line at a time // FileReader always assumes default encoding is OK! BufferedReader input = new BufferedReader(new FileReader(new File(path))); try { String line = null; // not declared within while loop /* * readLine is a bit quirky : it returns the content of a line MINUS the * newline. it returns null only for the END of the stream. it returns * an empty String if two newlines appear in a row. */ while ((line = input.readLine()) != null) { contents.append(line); contents.append(System.getProperty(LINE_SEPARATOR)); } } finally { input.close(); } } catch (IOException e) { Logging.logError(e); } return contents.toString(); } /** * * @param _processPid * @param _memberValues * @throws IOException */ public static ArrayList<String> getCpuUsage() { BufferedReader br = null; ArrayList<String> data = new ArrayList<String>(); String[] tempData; try { int numberOfCores = getNumberofCores(); // Parse /proc/stat file and fill the member values list // We gonna parse de first line (total) and each line corresponding // to // one core // Line example: cpu0 311689 2102 654770 6755602 32431 38 4127 0 0 0 br = getStream(statPath); // read a dummy line just for skip the total cpu line br.readLine(); for (int core = 1; core <= numberOfCores; core++) { data.add(String.valueOf(core)); tempData = br.readLine().split(SPACE); // Adds the first 9 fields. for (int field = 1; field < 10; field++) { data.add(tempData[field]); } } br.close(); } catch (IOException e) { Logging.logError(e); } return data; } /** * * @param _memberValues */ public static ArrayList<String> getDiskUsage() { ArrayList<String> partitionData = getPartitionUsage(); ArrayList<String> data = new ArrayList<String>(); String[] tempData = null; String[] tempFile = null; tempFile = getContents(diskstatsPath).split(System.getProperty(LINE_SEPARATOR)); ArrayList<String> tempPart = getPartitionNames(partitionData); // Parse /proc/diskstats to obtain disk statistics for (String line : tempFile) { for (String partition : tempPart) { if (line.contains(SPACE + partition + SPACE)) { // split(SPACE); tempData = line.split(SPACE); // adds the rest of the disk statistics data.addAll(Arrays.asList(tempData)); data.removeAll(Collections.singleton(EMPTY)); } } } return data; } /** * Get memory usage information. Files: /proc/[pid]/statm /proc/[pid]/stat * * @param _processPid * @param _memberValues */ public static ArrayList<String> getMemoryUsage(int _processPid) { BufferedReader br = null; ArrayList<String> data = new ArrayList<String>(); String[] tempData = null; try { // Parse /proc/[pid]/statm file and fill the member values list with // its contents (all) tempData = getContents(pidStatmPath.replace(SHARP, String.valueOf(_processPid))).trim().split(SPACE); data.addAll(Arrays.asList(tempData)); // Parse /proc/[pid]/stat file and fill the member values list just // with values 22, 23 and 24 (vsize, resident set size and resident // set size limit). tempData = getContents(pidStatPath.replace(SHARP, String.valueOf(_processPid))).trim().split(SPACE); data.add(tempData[22]); data.add(tempData[23]); data.add(tempData[24]); // Parse /proc/meminfo file for the system memory information. br = getStream(meminfoPath); for (int i = 0; i < 4; i++) { tempData = br.readLine().trim().split(SPACE); for (String s : tempData) { if (s.length() != 0 && CodecUtils.tryParseInt(s)) { data.add(s); } } } br.close(); } catch (IOException e) { Logging.logError(e); } return data; } /** * * @param _memberValues */ public static ArrayList<String> getNetworkUsage() { ArrayList<String> data = new ArrayList<String>(); String[] tempData = null; String[] tempFile = null; tempFile = getContents(netdevPath).split(System.getProperty(LINE_SEPARATOR)); // Skip the first two lines (headers) for (int i = 2; i < tempFile.length; i++) { // Parse /proc/net/dev to obtain network statistics. // Line e.g.: // lo: 4852 43 0 0 0 0 0 0 4852 43 0 0 0 0 0 0 tempData = tempFile[i].replace(COLON, SPACE).split(SPACE); data.addAll(Arrays.asList(tempData)); data.removeAll(Collections.singleton(EMPTY)); } return data; } /** * DEPRECATE - why do this? Java can do this? */ public static int getNumberofCores() throws FileNotFoundException, IOException, NumberFormatException { String[] tempData = null; String[] tempFile = null; // Parse /proc/cpuinfo to obtain how many cores the CPU has. tempFile = getContents(cpuinfoPath).split(System.getProperty(LINE_SEPARATOR)); for (String line : tempFile) { if (line.contains("cpu cores")) { tempData = line.split(COLON); break; } } return Integer.parseInt(tempData[1].trim()); } /* * Create a list with the partitions name to be used to find their statistics * in /proc/diskstats file */ public static ArrayList<String> getPartitionNames(ArrayList<String> data) { ArrayList<String> partitionsName = new ArrayList<String>(); for (String string : data) { if (!CodecUtils.tryParseInt(string)) { partitionsName.add(string); } } return partitionsName; } /** * * @param _memberValues */ public static ArrayList<String> getPartitionUsage() { ArrayList<String> data = new ArrayList<String>(); String[] tempData = null; String[] tempFile = null; tempFile = getContents(partitionsPath).split(System.getProperty(LINE_SEPARATOR)); // parse the disk partitions for (int i = 2; i < tempFile.length; i++) { tempData = tempFile[i].split(SPACE); data.addAll(Arrays.asList(tempData)); data.removeAll(Collections.singleton(EMPTY)); } return data; } /** * Opens a stream from a existing file and return it. This style of * implementation does not throw Exceptions to the caller. * * @param path * is a file which already exists and can be read. * @throws IOException */ private synchronized static BufferedReader getStream(String _path) throws IOException { BufferedReader br = null; File file = new File(_path); FileReader fileReader = null; try { fileReader = new FileReader(file); br = new BufferedReader(fileReader); } catch (IOException e) { Logging.logError(e); } return br; } /** * Gathers the usage statistic from the /proc file system for CPU, Memory, * Disk and Network */ static public ArrayList<String> getUsage(String uType) { if ((uType == null) || (processPid < 0)) { throw new IllegalArgumentException(); } ArrayList<String> usageData = null; String type = uType.toUpperCase(); if (type.equals("CPU")) { usageData = getCpuUsage(); } else if (type.equals("MEMORY")) { usageData = getMemoryUsage(processPid); } else if (type.equals("DISK")) { usageData = getDiskUsage(); } else if (type.equals("NETWORK")) { usageData = getNetworkUsage(); } return usageData; } public static void main(String[] args) { LoggingFactory.init("DEBUG"); Integer v = ProcParser.getArmInstructionVersion(); log.info("{}", v); log.info("{}", v); } public int setPid(int pid) { processPid = pid; return pid; } }// end ProcInfoParser