/*********************************************************************************************************************** * 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.instance; import java.io.BufferedReader; import java.io.FileReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import eu.stratosphere.configuration.ConfigConstants; import eu.stratosphere.configuration.GlobalConfiguration; import eu.stratosphere.util.OperatingSystem; /** * A factory to construct {@link HardwareDescription} objects. In particular, * the factory can automatically generate a {@link HardwareDescription} object * from the system it is executed on. * <p> * This class is thread-safe. */ public class HardwareDescriptionFactory { /** * The log object used to report errors. */ private static final Log LOG = LogFactory.getLog(HardwareDescriptionFactory.class); /** * The path to the interface to extract memory information under Linux. */ private static final String LINUX_MEMORY_INFO_PATH = "/proc/meminfo"; /** * The regular expression used to extract the size of the physical memory * under Linux. */ private static final Pattern LINUX_MEMORY_REGEX = Pattern.compile("^MemTotal:\\s*(\\d+)\\s+kB$"); /** * Private constructor, so class cannot be instantiated. */ private HardwareDescriptionFactory() {} /** * Extracts a hardware description object from the system. * * @return the hardware description object or <code>null</code> if at least * one value for the hardware description cannot be determined */ public static HardwareDescription extractFromSystem() { int numberOfCPUCores = Runtime.getRuntime().availableProcessors(); long sizeOfPhysicalMemory = getSizeOfPhysicalMemory(); if (sizeOfPhysicalMemory < 0) { sizeOfPhysicalMemory = 1; } long sizeOfFreeMemory = getSizeOfFreeMemory(); return new HardwareDescription(numberOfCPUCores, sizeOfPhysicalMemory, sizeOfFreeMemory); } /** * Constructs a new hardware description object. * * @param numberOfCPUCores * the number of CPU cores available to the JVM on the compute * node * @param sizeOfPhysicalMemory * the size of physical memory in bytes available on the compute * node * @param sizeOfFreeMemory * the size of free memory in bytes available to the JVM on the * compute node * @return the hardware description object */ public static HardwareDescription construct(int numberOfCPUCores,long sizeOfPhysicalMemory, long sizeOfFreeMemory) { return new HardwareDescription(numberOfCPUCores, sizeOfPhysicalMemory, sizeOfFreeMemory); } /** * Returns the size of free memory in bytes available to the JVM. * * @return the size of the free memory in bytes available to the JVM or <code>-1</code> if the size cannot be * determined */ private static long getSizeOfFreeMemory() { float fractionToUse = GlobalConfiguration.getFloat( ConfigConstants.TASK_MANAGER_MEMORY_FRACTION_KEY, ConfigConstants.DEFAULT_MEMORY_MANAGER_MEMORY_FRACTION); Runtime r = Runtime.getRuntime(); long max = r.maxMemory(); long total = r.totalMemory(); long free = r.freeMemory(); long available = max - total + free; return (long) (fractionToUse * available); } /** * Returns the size of the physical memory in bytes. * * @return the size of the physical memory in bytes or <code>-1</code> if * the size could not be determined */ private static long getSizeOfPhysicalMemory() { switch (OperatingSystem.getCurrentOperatingSystem()) { case LINUX: return getSizeOfPhysicalMemoryForLinux(); case WINDOWS: return getSizeOfPhysicalMemoryForWindows(); case MAC_OS: return getSizeOfPhysicalMemoryForMac(); case FREE_BSD: return getSizeOfPhysicalMemoryForFreeBSD(); case UNKNOWN: LOG.error("Cannot determine size of physical memory for unknown operating system"); return -1; default: LOG.error("Unrecognized OS"); return -1; } } /** * Returns the size of the physical memory in bytes on a Linux-based * operating system. * * @return the size of the physical memory in bytes or <code>-1</code> if * the size could not be determined */ @SuppressWarnings("resource") private static long getSizeOfPhysicalMemoryForLinux() { BufferedReader lineReader = null; try { lineReader = new BufferedReader(new FileReader(LINUX_MEMORY_INFO_PATH)); String line = null; while ((line = lineReader.readLine()) != null) { Matcher matcher = LINUX_MEMORY_REGEX.matcher(line); if (matcher.matches()) { String totalMemory = matcher.group(1); return Long.parseLong(totalMemory) * 1024L; // Convert from kilobyte to byte } } // expected line did not come LOG.error("Cannot determine the size of the physical memory using '/proc/meminfo'. Unexpected format."); return -1; } catch (NumberFormatException e) { LOG.error("Cannot determine the size of the physical memory using '/proc/meminfo'. Unexpected format."); return -1; } catch (IOException e) { LOG.error("Cannot determine the size of the physical memory using '/proc/meminfo': " + e.getMessage(), e); return -1; } finally { // Make sure we always close the file handle try { if (lineReader != null) { lineReader.close(); } } catch (Throwable t) {} } } /** * Returns the size of the physical memory in bytes on a Mac OS-based * operating system * * @return the size of the physical memory in bytes or <code>-1</code> if * the size could not be determined */ private static long getSizeOfPhysicalMemoryForMac() { BufferedReader bi = null; try { Process proc = Runtime.getRuntime().exec("sysctl hw.memsize"); bi = new BufferedReader( new InputStreamReader(proc.getInputStream())); String line; while ((line = bi.readLine()) != null) { if (line.startsWith("hw.memsize")) { long memsize = Long.parseLong(line.split(":")[1].trim()); bi.close(); proc.destroy(); return memsize; } } } catch (Exception e) { LOG.error(e); return -1; } finally { if (bi != null) { try { bi.close(); } catch (IOException ioe) { } } } return -1; } /** * Returns the size of the physical memory in bytes on FreeBSD. * * @return the size of the physical memory in bytes or <code>-1</code> if * the size could not be determined */ private static long getSizeOfPhysicalMemoryForFreeBSD() { BufferedReader bi = null; try { Process proc = Runtime.getRuntime().exec("sysctl hw.physmem"); bi = new BufferedReader(new InputStreamReader(proc.getInputStream())); String line; while ((line = bi.readLine()) != null) { if (line.startsWith("hw.physmem")) { long memsize = Long.parseLong(line.split(":")[1].trim()); bi.close(); proc.destroy(); return memsize; } } LOG.error("Cannot determine the size of the physical memory using 'sysctl hw.physmem'."); return -1; } catch (Exception e) { LOG.error("Cannot determine the size of the physical memory using 'sysctl hw.physmem': " + e.getMessage(), e); return -1; } finally { if (bi != null) { try { bi.close(); } catch (IOException ioe) { } } } } /** * Returns the size of the physical memory in bytes on Windows. * * @return the size of the physical memory in bytes or <code>-1</code> if * the size could not be determined */ private static long getSizeOfPhysicalMemoryForWindows() { BufferedReader bi = null; try { Process proc = Runtime.getRuntime().exec("wmic memorychip get capacity"); bi = new BufferedReader(new InputStreamReader(proc.getInputStream())); String line = bi.readLine(); if (line == null) { return -1L; } if (!line.startsWith("Capacity")) { return -1L; } long sizeOfPhyiscalMemory = 0L; while ((line = bi.readLine()) != null) { if (line.isEmpty()) { continue; } line = line.replaceAll(" ", ""); sizeOfPhyiscalMemory += Long.parseLong(line); } return sizeOfPhyiscalMemory; } catch (Exception e) { LOG.error("Cannot determine the size of the physical memory using 'wmic memorychip': " + e.getMessage(), e); return -1L; } finally { if (bi != null) { try { bi.close(); } catch (Throwable t) {} } } } }