/*********************************************************************************************************************** * Copyright (C) 2010-2013 by the Stratosphere project (http://stratosphere.eu) * * 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 eu.stratosphere.nephele.profiling.impl; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.util.regex.Matcher; import java.util.regex.Pattern; import eu.stratosphere.nephele.instance.InstanceConnectionInfo; import eu.stratosphere.nephele.profiling.ProfilingException; import eu.stratosphere.nephele.profiling.impl.types.InternalInstanceProfilingData; import eu.stratosphere.util.StringUtils; public class InstanceProfiler { static final String PROC_MEMINFO = "/proc/meminfo"; static final String PROC_STAT = "/proc/stat"; static final String PROC_NET_DEV = "/proc/net/dev"; private static final String LOOPBACK_INTERFACE_NAME = "lo"; private static final Pattern CPU_PATTERN = Pattern .compile("^cpu\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+).+$"); private static final Pattern NETWORK_PATTERN = Pattern .compile("^\\s*(\\w+):\\s*(\\d+)\\s+\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+\\d+\\s+(\\d+).+$"); private static final Pattern MEMORY_PATTERN = Pattern.compile("^\\w+:\\s*(\\d+)\\s+kB$"); private static final int PERCENT = 100; private final InstanceConnectionInfo instanceConnectionInfo; private long lastTimestamp = 0; // CPU related variables private long lastCpuUser = 0; private long lastCpuNice = 0; private long lastCpuSys = 0; private long lastCpuIdle = 0; private long lastCpuIOWait = 0; private long lastCpuIrq = 0; private long lastCpuSoftirq = 0; // Network related variables private long lastReceivedBytes = 0; private long lastTramsmittedBytes = 0; private long firstTimestamp; public InstanceProfiler(InstanceConnectionInfo instanceConnectionInfo) throws ProfilingException { this.instanceConnectionInfo = instanceConnectionInfo; this.firstTimestamp = System.currentTimeMillis(); // Initialize counters by calling generateProfilingData once and ignore the return value generateProfilingData(this.firstTimestamp); } InternalInstanceProfilingData generateProfilingData(long timestamp) throws ProfilingException { final long profilingInterval = timestamp - lastTimestamp; final InternalInstanceProfilingData profilingData = new InternalInstanceProfilingData( this.instanceConnectionInfo, (int) profilingInterval); updateCPUUtilization(profilingData); updateMemoryUtilization(profilingData); updateNetworkUtilization(profilingData); // Update timestamp this.lastTimestamp = timestamp; return profilingData; } private void updateMemoryUtilization(InternalInstanceProfilingData profilingData) throws ProfilingException { BufferedReader in = null; try { in = new BufferedReader(new FileReader(PROC_MEMINFO)); long freeMemory = 0; long totalMemory = 0; long bufferedMemory = 0; long cachedMemory = 0; long cachedSwapMemory = 0; int count = 0; String output; while ((output = in.readLine()) != null) { switch (count) { case 0: // Total memory totalMemory = extractMemoryValue(output); break; case 1: // Free memory freeMemory = extractMemoryValue(output); break; case 2: // Buffers bufferedMemory = extractMemoryValue(output); break; case 3: // Cache cachedMemory = extractMemoryValue(output); break; case 4: cachedSwapMemory = extractMemoryValue(output); break; default: break; } ++count; } profilingData.setTotalMemory(totalMemory); profilingData.setFreeMemory(freeMemory); profilingData.setBufferedMemory(bufferedMemory); profilingData.setCachedMemory(cachedMemory); profilingData.setCachedSwapMemory(cachedSwapMemory); } catch (IOException ioe) { throw new ProfilingException("Error while reading network utilization: " + StringUtils.stringifyException(ioe)); } finally { if (in != null) { try { in.close(); } catch (IOException e) { } } } } private long extractMemoryValue(String line) throws ProfilingException { final Matcher matcher = MEMORY_PATTERN.matcher(line); if (!matcher.matches()) { throw new ProfilingException("Cannot extract memory data for profiling from line " + line); } return Long.parseLong(matcher.group(1)); } private void updateNetworkUtilization(InternalInstanceProfilingData profilingData) throws ProfilingException { BufferedReader in = null; try { in = new BufferedReader(new FileReader(PROC_NET_DEV)); long receivedSum = 0; long transmittedSum = 0; String output; while ((output = in.readLine()) != null) { final Matcher networkMatcher = NETWORK_PATTERN.matcher(output); if (!networkMatcher.matches()) { continue; } /* * Extract information according to * http://linuxdevcenter.com/pub/a/linux/2000/11/16/LinuxAdmin.html */ if (LOOPBACK_INTERFACE_NAME.equals(networkMatcher.group(1))) { continue; } receivedSum += Long.parseLong(networkMatcher.group(2)); transmittedSum += Long.parseLong(networkMatcher.group(3)); } in.close(); in = null; profilingData.setReceivedBytes(receivedSum - this.lastReceivedBytes); profilingData.setTransmittedBytes(transmittedSum - this.lastTramsmittedBytes); // Store values for next call this.lastReceivedBytes = receivedSum; this.lastTramsmittedBytes = transmittedSum; } catch (IOException ioe) { throw new ProfilingException("Error while reading network utilization: " + StringUtils.stringifyException(ioe)); } catch (NumberFormatException nfe) { throw new ProfilingException("Error while reading network utilization: " + StringUtils.stringifyException(nfe)); } finally { if (in != null) { try { in.close(); } catch (IOException e) { } } } } private void updateCPUUtilization(InternalInstanceProfilingData profilingData) throws ProfilingException { BufferedReader in = null; try { in = new BufferedReader(new FileReader(PROC_STAT)); final String output = in.readLine(); if (output == null) { throw new ProfilingException("Cannot read CPU utilization, return value is null"); } in.close(); in = null; final Matcher cpuMatcher = CPU_PATTERN.matcher(output); if (!cpuMatcher.matches()) { throw new ProfilingException("Cannot extract CPU utilization from output \"" + output + "\""); } /* * Extract the information from the read line according to * http://www.linuxhowtos.org/System/procstat.htm */ final long cpuUser = Long.parseLong(cpuMatcher.group(1)); final long cpuNice = Long.parseLong(cpuMatcher.group(2)); final long cpuSys = Long.parseLong(cpuMatcher.group(3)); final long cpuIdle = Long.parseLong(cpuMatcher.group(4)); final long cpuIOWait = Long.parseLong(cpuMatcher.group(5)); final long cpuIrq = Long.parseLong(cpuMatcher.group(6)); final long cpuSoftirq = Long.parseLong(cpuMatcher.group(7)); // Calculate deltas final long deltaCpuUser = cpuUser - this.lastCpuUser; final long deltaCpuNice = cpuNice - this.lastCpuNice; final long deltaCpuSys = cpuSys - this.lastCpuSys; final long deltaCpuIdle = cpuIdle - this.lastCpuIdle; final long deltaCpuIOWait = cpuIOWait - this.lastCpuIOWait; final long deltaCpuIrq = cpuIrq - this.lastCpuIrq; final long deltaCpuSoftirq = cpuSoftirq - this.lastCpuSoftirq; final long deltaSum = deltaCpuUser + deltaCpuNice + deltaCpuSys + deltaCpuIdle + deltaCpuIOWait + deltaCpuIrq + deltaCpuSoftirq; // TODO: Fix deltaSum = 0 situation // Set the percentage values for the profiling data object /* * profilingData.setIdleCPU((int)((deltaCpuIdle*PERCENT)/deltaSum)); * profilingData.setUserCPU((int)((deltaCpuUser*PERCENT)/deltaSum)); * profilingData.setSystemCPU((int)((deltaCpuSys*PERCENT)/deltaSum)); * profilingData.setIoWaitCPU((int)((deltaCpuIOWait*PERCENT)/deltaSum)); * profilingData.setHardIrqCPU((int)((deltaCpuIrq*PERCENT)/deltaSum)); * profilingData.setSoftIrqCPU((int)((deltaCpuSoftirq*PERCENT)/deltaSum)); */ // TODO: bad quick fix if (deltaSum > 0) { profilingData.setIdleCPU((int) ((deltaCpuIdle * PERCENT) / deltaSum)); profilingData.setUserCPU((int) ((deltaCpuUser * PERCENT) / deltaSum)); profilingData.setSystemCPU((int) ((deltaCpuSys * PERCENT) / deltaSum)); profilingData.setIoWaitCPU((int) ((deltaCpuIOWait * PERCENT) / deltaSum)); profilingData.setHardIrqCPU((int) ((deltaCpuIrq * PERCENT) / deltaSum)); profilingData.setSoftIrqCPU((int) ((deltaCpuSoftirq * PERCENT) / deltaSum)); } else { profilingData.setIdleCPU((int) (deltaCpuIdle)); profilingData.setUserCPU((int) (deltaCpuUser)); profilingData.setSystemCPU((int) (deltaCpuSys)); profilingData.setIoWaitCPU((int) (deltaCpuIOWait)); profilingData.setHardIrqCPU((int) (deltaCpuIrq)); profilingData.setSoftIrqCPU((int) (deltaCpuSoftirq)); } // Store values for next call this.lastCpuUser = cpuUser; this.lastCpuNice = cpuNice; this.lastCpuSys = cpuSys; this.lastCpuIdle = cpuIdle; this.lastCpuIOWait = cpuIOWait; this.lastCpuIrq = cpuIrq; this.lastCpuSoftirq = cpuSoftirq; } catch (IOException ioe) { throw new ProfilingException("Error while reading CPU utilization: " + StringUtils.stringifyException(ioe)); } catch (NumberFormatException nfe) { throw new ProfilingException("Error while reading CPU utilization: " + StringUtils.stringifyException(nfe)); } finally { if (in != null) { try { in.close(); } catch (IOException e) { } } } } /** * @return InternalInstanceProfilingData ProfilingData for the instance from execution-start to currentTime * @throws ProfilingException */ public InternalInstanceProfilingData generateCheckpointProfilingData() throws ProfilingException { final long profilingInterval = System.currentTimeMillis() - this.firstTimestamp; final InternalInstanceProfilingData profilingData = new InternalInstanceProfilingData( this.instanceConnectionInfo, (int) profilingInterval); updateCPUUtilization(profilingData); updateMemoryUtilization(profilingData); updateNetworkUtilization(profilingData); return profilingData; } }