package water.util; import java.io.*; import java.util.ArrayList; import java.util.regex.Matcher; import java.util.regex.Pattern; /** * Linux /proc file reader. * * Read tick information for the system and the current process in order to provide * stats on the cloud page about CPU utilization. * * Tick counts are monotonically increasing since boot. * * Find definitions of /proc file info here. * http://man7.org/linux/man-pages/man5/proc.5.html */ public class LinuxProcFileReader { private String _systemData; private String _processData; private String _processStatus; private String _pid; private long _systemIdleTicks = -1; private long _systemTotalTicks = -1; private long _processTotalTicks = -1; private long _processRss = -1; private int _processCpusAllowed = -1; private int _processNumOpenFds = -1; private ArrayList<long[]> _cpuTicks = null; /** * Constructor. */ public LinuxProcFileReader() { } /** * @return ticks the system was idle. in general: idle + busy == 100% */ public long getSystemIdleTicks() { assert _systemIdleTicks > 0; return _systemIdleTicks; } /** * @return ticks the system was up. */ public long getSystemTotalTicks() { assert _systemTotalTicks > 0; return _systemTotalTicks; } /** * @return ticks this process was running. */ public long getProcessTotalTicks() { assert _processTotalTicks > 0; return _processTotalTicks; } /** * Array of ticks. * [cpu number][tick type] * * tick types are: * * [0] user ticks * [1] system ticks * [2] other ticks (i/o) * [3] idle ticks * * @return ticks array for each cpu of the system. */ public long[][] getCpuTicks() { assert _cpuTicks != null; return _cpuTicks.toArray(new long[0][0]); } /** * @return resident set size (RSS) of this process. */ public long getProcessRss() { assert _processRss > 0; return _processRss; } static private boolean isOSNameMatch(final String osName, final String osNamePrefix) { if (osName == null) { return false; } return osName.startsWith(osNamePrefix); } private static boolean getOSMatchesName(final String osNamePrefix) { String osName = System.getProperty("os.name"); return isOSNameMatch(osName, osNamePrefix); } private static boolean IS_OS_LINUX() { return getOSMatchesName("Linux") || getOSMatchesName("LINUX"); } /** * @return number of CPUs allowed by this process. */ public int getProcessCpusAllowed() { if (! IS_OS_LINUX()) { return Runtime.getRuntime().availableProcessors(); } // _processCpusAllowed is not available on CentOS 5 and earlier. // In this case, just return availableProcessors. if (_processCpusAllowed < 0) { return Runtime.getRuntime().availableProcessors(); } return _processCpusAllowed; } /** * @return number of currently open fds of this process. */ public int getProcessNumOpenFds() { assert _processNumOpenFds > 0; return _processNumOpenFds; } /** * @return process id for this node as a String. */ public String getProcessID() { return _pid; } /** * Read and parse data from /proc/stat and /proc/<pid>/stat. * If this doesn't work for some reason, the values will be -1. */ public void read() { String pid = "-1"; try { pid = getProcessId(); _pid = pid; } catch (Exception ignore) {} File f = new File ("/proc/stat"); if (! f.exists()) { return; } try { readSystemProcFile(); readProcessProcFile(pid); readProcessNumOpenFds(pid); readProcessStatusFile(pid); parseSystemProcFile(_systemData); parseProcessProcFile(_processData); parseProcessStatusFile(_processStatus); } catch (Exception ignore) {} } /** * @return true if all the values are ok to use; false otherwise. */ public boolean valid() { return ((_systemIdleTicks >= 0) && (_systemTotalTicks >= 0) && (_processTotalTicks >= 0) && (_processNumOpenFds >= 0)); } /** * @return number of set bits in hexadecimal string (chars must be 0-F) */ public static int numSetBitsHex(String s) { // Look-up table for num set bits in 4-bit char final int[] bits_set = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; int nset = 0; for(int i = 0; i < s.length(); i++) { Character ch = s.charAt(i); if (ch == ',') { continue; } int x = Integer.parseInt(ch.toString(), 16); nset += bits_set[x]; } return nset; } private static String getProcessId() throws Exception { // Note: may fail in some JVM implementations // therefore fallback has to be provided // something like '<pid>@<hostname>', at least in SUN / Oracle JVMs final String jvmName = java.lang.management.ManagementFactory.getRuntimeMXBean().getName(); final int index = jvmName.indexOf('@'); if (index < 1) { // part before '@' empty (index = 0) / '@' not found (index = -1) throw new Exception ("Can't get process Id"); } return Long.toString(Long.parseLong(jvmName.substring(0, index))); } private String readFile(File f) throws Exception { char[] buffer = new char[16 * 1024]; FileReader fr = new FileReader(f); int bytesRead = 0; while (true) { int n = fr.read(buffer, bytesRead, buffer.length - bytesRead); if (n < 0) { fr.close(); return new String (buffer, 0, bytesRead); } else if (n == 0) { // This is weird. fr.close(); throw new Exception("LinuxProcFileReader readFile read 0 bytes"); } bytesRead += n; if (bytesRead >= buffer.length) { fr.close(); throw new Exception("LinuxProcFileReader readFile unexpected buffer full"); } } } private void readSystemProcFile() { try { _systemData = readFile(new File("/proc/stat")); } catch (Exception ignore) {} } /** * @param s String containing contents of proc file. */ private void parseSystemProcFile(String s) { if (s == null) return; try { BufferedReader reader = new BufferedReader(new StringReader(s)); String line = reader.readLine(); // Read aggregate cpu values { Pattern p = Pattern.compile("cpu\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+).*"); Matcher m = p.matcher(line); boolean b = m.matches(); if (!b) { return; } long systemUserTicks = Long.parseLong(m.group(1)); long systemNiceTicks = Long.parseLong(m.group(2)); long systemSystemTicks = Long.parseLong(m.group(3)); _systemIdleTicks = Long.parseLong(m.group(4)); _systemTotalTicks = systemUserTicks + systemNiceTicks + systemSystemTicks + _systemIdleTicks; } // Read individual cpu values _cpuTicks = new ArrayList<long[]>(); line = reader.readLine(); while (line != null) { Pattern p = Pattern.compile("cpu(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+)\\s+(\\d+).*"); Matcher m = p.matcher(line); boolean b = m.matches(); if (! b) { break; } // Copying algorithm from http://gee.cs.oswego.edu/dl/code/ // See perfbar.c in gtk_perfbar package. // int cpuNum = Integer.parseInt(m.group(1)); long cpuUserTicks = 0; long cpuSystemTicks = 0; long cpuOtherTicks = 0; long cpuIdleTicks = 0; cpuUserTicks += Long.parseLong(m.group(2)); cpuOtherTicks += Long.parseLong(m.group(3)); cpuSystemTicks += Long.parseLong(m.group(4)); cpuIdleTicks += Long.parseLong(m.group(5)); cpuOtherTicks += Long.parseLong(m.group(6)); cpuSystemTicks += Long.parseLong(m.group(7)); cpuSystemTicks += Long.parseLong(m.group(8)); long[] oneCpuTicks = {cpuUserTicks, cpuSystemTicks, cpuOtherTicks, cpuIdleTicks}; _cpuTicks.add(oneCpuTicks); line = reader.readLine(); } } catch (Exception ignore) {} } private void readProcessProcFile(String pid) { try { String s = "/proc/" + pid + "/stat"; _processData = readFile(new File(s)); } catch (Exception ignore) {} } private void parseProcessProcFile(String s) { if (s == null) return; try { BufferedReader reader = new BufferedReader(new StringReader(s)); String line = reader.readLine(); Pattern p = Pattern.compile( "(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)" + "\\s+" + "(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)" + "\\s+" + "(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)" + "\\s+" + "(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)" + "\\s+" + "(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)\\s+(\\S+)" + ".*"); Matcher m = p.matcher(line); boolean b = m.matches(); if (! b) { return; } long processUserTicks = Long.parseLong(m.group(14)); long processSystemTicks = Long.parseLong(m.group(15)); _processTotalTicks = processUserTicks + processSystemTicks; _processRss = Long.parseLong(m.group(24)); } catch (Exception ignore) {} } private void readProcessNumOpenFds(String pid) { try { String s = "/proc/" + pid + "/fd"; File f = new File(s); String[] arr = f.list(); if (arr != null) { _processNumOpenFds = arr.length; } } catch (Exception ignore) {} } private void readProcessStatusFile(String pid) { try { String s = "/proc/" + pid + "/status"; _processStatus = readFile(new File(s)); } catch (Exception ignore) {} } private void parseProcessStatusFile(String s) { if(s == null) return; try { Pattern p = Pattern.compile("Cpus_allowed:\\s+([A-Fa-f0-9,]+)"); Matcher m = p.matcher(s); boolean b = m.find(); if (! b) { return; } _processCpusAllowed = numSetBitsHex(m.group(1)); } catch (Exception ignore) {} } /** * Main is purely for command-line testing. */ public static void main(String[] args) { final String sysTestData = "cpu 43559117 24094 1632164 1033740407 245624 29 200080 0 0 0\n"+ "cpu0 1630761 1762 62861 31960072 40486 15 10614 0 0 0\n"+ "cpu1 1531923 86 62987 32118372 13190 0 6806 0 0 0\n"+ "cpu2 1436788 332 66513 32210723 10867 0 6772 0 0 0\n"+ "cpu3 1428700 1001 64574 32223156 8751 0 6811 0 0 0\n"+ "cpu4 1424410 152 62649 32232602 6552 0 6836 0 0 0\n"+ "cpu5 1427172 1478 58744 32233938 5471 0 6708 0 0 0\n"+ "cpu6 1418433 348 60957 32241807 5301 0 6639 0 0 0\n"+ "cpu7 1404882 182 60640 32258150 3847 0 6632 0 0 0\n"+ "cpu8 1485698 3593 67154 32101739 38387 0 9016 0 0 0\n"+ "cpu9 1422404 1601 66489 32193865 15133 0 8800 0 0 0\n"+ "cpu10 1383939 3386 69151 32233567 11219 0 8719 0 0 0\n"+ "cpu11 1376904 3051 65256 32246197 8307 0 8519 0 0 0\n"+ "cpu12 1381437 1496 68003 32237894 6966 0 8676 0 0 0\n"+ "cpu13 1376250 1527 66598 32247951 7020 0 8554 0 0 0\n"+ "cpu14 1364352 1573 65520 32262764 5093 0 8531 0 0 0\n"+ "cpu15 1359076 1176 64380 32269336 5219 0 8593 0 0 0\n"+ "cpu16 1363844 6 29612 32344252 4830 2 4366 0 0 0\n"+ "cpu17 1477797 1019 70211 32190189 6278 0 3731 0 0 0\n"+ "cpu18 1285849 30 29219 32428612 3549 0 3557 0 0 0\n"+ "cpu19 1272308 0 27306 32445340 2089 0 3541 0 0 0\n"+ "cpu20 1326369 5 29152 32386824 2458 0 4416 0 0 0\n"+ "cpu21 1320883 28 31886 32384709 2327 1 4869 0 0 0\n"+ "cpu22 1259498 1 26954 32458931 2247 0 3511 0 0 0\n"+ "cpu23 1279464 0 26694 32439550 1914 0 3571 0 0 0\n"+ "cpu24 1229977 19 32308 32471217 4191 0 4732 0 0 0\n"+ "cpu25 1329079 92 79253 32324092 5267 0 4821 0 0 0\n"+ "cpu26 1225922 30 34837 32475220 4000 0 4711 0 0 0\n"+ "cpu27 1261848 56 43928 32397341 3552 0 5625 0 0 0\n"+ "cpu28 1226707 20 36281 32463498 3935 4 5943 0 0 0\n"+ "cpu29 1379751 19 35593 32317723 2872 4 5913 0 0 0\n"+ "cpu30 1247661 0 32636 32455845 2033 0 4775 0 0 0\n"+ "cpu31 1219016 10 33804 32484916 2254 0 4756 0 0 0\n"+ "intr 840450413 1194 0 0 0 0 0 0 0 1 0 0 0 0 0 0 0 55 0 0 0 0 0 0 45 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 593665 88058 57766 41441 62426 61320 39848 39787 522984 116724 99144 95021 113975 99093 78676 78144 0 168858 168858 168858 162 2986764 4720950 3610168 5059579 3251008 2765017 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 00 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0\n"+ "ctxt 1506565570\n"+ "btime 1385196580\n"+ "processes 1226464\n"+ "procs_running 21\n"+ "procs_blocked 0\n"+ "softirq 793917930 0 156954983 77578 492842649 1992553 0 7758971 51856558 228040 82206598\n"; final String procTestData = "16790 (java) S 1 16789 16789 0 -1 4202496 6714145 0 0 0 4773058 5391 0 0 20 0 110 0 33573283 64362651648 6467228 18446744073709551615 1073741824 1073778376 140734614041280 140734614032416 140242897981768 0 0 3 16800972 18446744073709551615 0 0 17 27 0 0 0 0 0\n"; LinuxProcFileReader lpfr = new LinuxProcFileReader(); lpfr.parseSystemProcFile(sysTestData); lpfr.parseProcessProcFile(procTestData); System.out.println("System idle ticks: " + lpfr.getSystemIdleTicks()); System.out.println("System total ticks: " + lpfr.getSystemTotalTicks()); System.out.println("Process total ticks: " + lpfr.getProcessTotalTicks()); System.out.println("Process RSS: " + lpfr.getProcessRss()); System.out.println("Number of cpus: " + lpfr.getCpuTicks().length); } }