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