/******************************************************************************* * Copyright 2006 - 2012 Vienna University of Technology, * Department of Software Technology and Interactive Systems, IFS * * 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 at.tuwien.minimee.util; import java.io.RandomAccessFile; import java.util.ArrayList; import java.util.List; import java.util.StringTokenizer; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * This class can parse a file written by the Linux program 'top' and fit * the information into the data structure {@link ExecutionFootprintList}. * * 'top' writes a log file with entries as can be seen below every x seconds. * * top - 15:45:06 up 2 days, 23:43, 2 users, load average: 0.19, 0.31, 0.26 * Tasks: 1 total, 1 running, 0 sleeping, 0 stopped, 0 zombie * Cpu(s): 0.8%us, 0.2%sy, 0.0%ni, 98.8%id, 0.1%wa, 0.0%hi, 0.0%si, 0.0%st * Mem: 2036960k total, 1853784k used, 183176k free, 74072k buffers * Swap: 4192956k total, 5396k used, 4187560k free, 813216k cached * * PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND * 3758 kulovits 20 0 32400 20m 3452 R 93 1.0 0:00.46 gs * * @author kulovits */ public class TopParser { private Logger log = LoggerFactory.getLogger(this.getClass()); private String file; private ExecutionFootprint currentExecutionFootprint = null; private ExecutionFootprintList list = new ExecutionFootprintList(); private Integer monitoredPid = null; private RandomAccessFile input = null; public TopParser(String file) { this.file = file; } public static void main (String[] args) { TopParser p = new TopParser("/tmp/profile_1234906043659516000/top.log"); p.parse(); System.out.println(p.getList().toString()); } public void parse() { try { input = new RandomAccessFile(file, "r"); try { monitoredPid = findPid(); // couldn't determine PID if (monitoredPid == null) { return; } String line = null; while (( line = input.readLine()) != null){ parseLine(line); } } finally { input.close(); } // list.debugToConsole(); } catch (Exception e) { log.error("Failed to parse Top.",e); } } /** * The process ID is in the last line of the file and looks like follows: * monitored_pid= 6738 * * @param input * @return * @throws Exception */ private Integer findPid() throws Exception { Integer pid = new Integer(0); // we open the file RandomAccessFile f = new RandomAccessFile(file, "r"); try { long size = f.length(); f.seek(size-2); // we search the file reverse for '=' byte[] b = new byte[1]; for (long i = size-2; i>= 0; i--) { f.seek(i); f.read(b); if (b[0] == '=') { break; } } String line = f.readLine().trim(); pid = new Integer(line); } finally { // this is important, RandomAccessFile doesn't close the file handle by default! // if close isn't called, you'll get very soon 'too many open files' f.close(); } return pid; } private void parseLine(String line) { StringTokenizer tokenizer = new StringTokenizer(line, " :"); if (!tokenizer.hasMoreTokens()) { return; } String start = tokenizer.nextToken(); if (start.compareTo("top") == 0) { if (currentExecutionFootprint != null) { cleanUp(currentExecutionFootprint); list.add(currentExecutionFootprint); } currentExecutionFootprint = new ExecutionFootprint(); parseTopLine(tokenizer); } else if (start.compareTo("Tasks") == 0) { parseTasksLine(tokenizer); } else if (start.compareTo("Cpu(s)") == 0) { parseCpusLine(line); } else if (start.compareTo("Mem") == 0) { parseMemLine(tokenizer); } else if (start.compareTo("Swap") == 0) { parseSwapLine(tokenizer); } else if (start.compareTo("PID") == 0) { // Header line parseHeaderLine(tokenizer); // we read the header line // ExecutionFootprintList fp = tempReadPerformanceData(); } else if (start.startsWith("monitored_pid=")) { // do nothing } else { try { Integer pid = new Integer(start.trim()); // if (pid.equals(monitoredPid)) { // currentExecutionFootprint.setPid(pid); ProcessExecutionFootprint pfp = parseProcessLine(tokenizer); pfp.setPid(pid); currentExecutionFootprint.getProcesses().add(pfp); // } } catch(NumberFormatException e) { log.error("Failed to parse line " + line,e); } } } private void cleanUp(ExecutionFootprint fp) { List<ProcessExecutionFootprint> processes = new ArrayList<ProcessExecutionFootprint>(); ProcessExecutionFootprint root = null; for (ProcessExecutionFootprint pfp : fp.getProcesses()) { if (pfp.getPid() == monitoredPid.intValue()) { root = pfp; break; } } // we didn't find any process with PID 'monitoredPid' if (root == null) { // we remove all processes from list fp.getProcesses().clear(); return; } processes.add(root); ProcessExecutionFootprint pfp = root; while (pfp.getPpid() != 1) { ProcessExecutionFootprint prev = pfp; for (ProcessExecutionFootprint iter : fp.getProcesses()) { if (iter.getPpid() == pfp.getPid()) { pfp = iter; break; } } // we didn't find a child if (prev == pfp) { break; } if (pfp.getPid() != 1) { processes.add(pfp); } } fp.setProcesses(processes); } private void parseTopLine(StringTokenizer tokenizer) { } private void parseTasksLine(StringTokenizer tokenizer) { } private void parseCpusLine(String line) { int index; index = line.indexOf("%us"); if (index != -1) { int lastIndex = line.lastIndexOf(' ', index); // this is the case when 'Cpu(s):100.0%us,' if (lastIndex == -1) { lastIndex = line.lastIndexOf(':', index) + 1; } String s = line.substring(lastIndex, index); currentExecutionFootprint.getSystemFootprint().setTotalCpusUser(new Double(s)); } index = line.indexOf("%sy"); if (index != -1) { String s = line.substring(line.lastIndexOf(',', index)+1, index); currentExecutionFootprint.getSystemFootprint().setTotalCpusSystem(new Double(s)); } index = line.indexOf("%id"); if (index != -1) { String s = line.substring(line.lastIndexOf(',', index)+1, index); currentExecutionFootprint.getSystemFootprint().setTotalCpusIdle(new Double(s)); } } private void parseMemLine(StringTokenizer tokenizer) { int column = 1; Double d = null; for (column = 1; tokenizer.hasMoreTokens() && column < 4; column++) { String strColumn = tokenizer.nextToken().trim(); switch(column) { case 1: // total mem available (e.g. 2036960k) d = parseSizeColumn(strColumn); currentExecutionFootprint.getSystemFootprint().setTotalMemoryAvailable(d); break; case 2: // this is just the text: 'total,' break; case 3: d = parseSizeColumn(strColumn); currentExecutionFootprint.getSystemFootprint().setTotalMemoryAvailable(d); break; } } } private void parseSwapLine(StringTokenizer tokenizer) { } /** * Doesn't do anything right now, maybe do some validation * @param tokenizer */ private void parseHeaderLine(StringTokenizer tokenizer) { } private ProcessExecutionFootprint parseProcessLine(StringTokenizer tokenizer) { ProcessExecutionFootprint pfp = new ProcessExecutionFootprint(); int column = 1; for (column = 1; tokenizer.hasMoreTokens(); column++) { String strColumn = tokenizer.nextToken(" "); switch(column) { case 1: // USER break; case 2: // PR break; case 3: // NI break; case 4: // VIRT Double virt = parseSizeColumn(strColumn); pfp.setVirtualMemory(virt); break; case 5: // RES Double res = parseSizeColumn(strColumn); pfp.setResidentSize(res); break; case 6: // SHR Double shr = parseSizeColumn(strColumn); pfp.setSharedMemory(shr); break; case 7: // S break; case 8: // %CPU Double pCPU = new Double(strColumn); pfp.setCpu(pCPU); break; case 9: // %MEM Double pMem = new Double(strColumn); pfp.setPMem(pMem); break; case 10: // TIME+ // mm:ss.hh String[] tokens = strColumn.split("[:|.]"); if (tokens.length == 3) { long cpuTimeUsed = (new Long(tokens[0]))*60*1000; cpuTimeUsed += (new Long(tokens[1]))*1000; cpuTimeUsed += (new Long(tokens[2]))*10; pfp.setCpuTimeUsed(cpuTimeUsed); } break; case 11: // PPID pfp.setPpid(new Integer(strColumn)); break; case 12: pfp.setCommand(strColumn); break; default: continue; } } return pfp; } private Double parseSizeColumn(String strColumn) { strColumn = strColumn.trim(); Double virt = null; if (strColumn.charAt(strColumn.length()-1) == 'g') { // we have giga byte virt = new Double(strColumn.substring(0, strColumn.length()-1)); virt *= (1024 * 1024); } else if (strColumn.charAt(strColumn.length()-1) == 'm') { // we have mega byte virt = new Double(strColumn.substring(0, strColumn.length()-1)); virt *= 1024; } else if (strColumn.charAt(strColumn.length()-1) == 'k') { // we have kilo bytes virt = new Double(strColumn.substring(0, strColumn.length()-1)); } else { virt = new Double(strColumn); } return virt; } public ExecutionFootprintList getList() { return list; } }