diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ProcfsBasedProcessTree.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ProcfsBasedProcessTree.java index 8bf54b5ddc8..84984729d88 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ProcfsBasedProcessTree.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/main/java/org/apache/hadoop/yarn/util/ProcfsBasedProcessTree.java @@ -130,6 +130,7 @@ public static MemInfo getMemInfoByName(String name) { protected Map processTree = new HashMap(); + private long jiffies = UNAVAILABLE; public ProcfsBasedProcessTree(String pid) { this(pid, PROCFS, SystemClock.getInstance()); @@ -264,6 +265,26 @@ public void updateProcessTree() { } } + if (JIFFY_LENGTH_IN_MILLIS > 0) { + long incJiffies = 0; + boolean isAvailable = false; + for (ProcessInfo p : processTree.values()) { + if (p != null) { + isAvailable = true; + incJiffies += p.getDtime(); + } + } + if (isAvailable) { + // reset cpuTime to 0 instead of UNAVAILABLE + if (jiffies == UNAVAILABLE) { + jiffies = 0L; + } + jiffies += incJiffies; + } + cpuTimeTracker.updateElapsedJiffies( + BigInteger.valueOf(jiffies), clock.getTime()); + } + LOG.debug("{}", this); if (smapsEnabled) { @@ -423,37 +444,10 @@ public long getCumulativeCpuTime() { if (JIFFY_LENGTH_IN_MILLIS < 0) { return UNAVAILABLE; } - long incJiffies = 0; - boolean isAvailable = false; - for (ProcessInfo p : processTree.values()) { - if (p != null) { - // data is available - isAvailable = true; - incJiffies += p.getDtime(); - } - } - if (isAvailable) { - // reset cpuTime to 0 instead of UNAVAILABLE - if (cpuTime == UNAVAILABLE) { - cpuTime = 0L; - } - cpuTime += incJiffies * JIFFY_LENGTH_IN_MILLIS; - } + cpuTime = jiffies * JIFFY_LENGTH_IN_MILLIS; return cpuTime; } - private BigInteger getTotalProcessJiffies() { - BigInteger totalStime = BigInteger.ZERO; - long totalUtime = 0; - for (ProcessInfo p : processTree.values()) { - if (p != null) { - totalUtime += p.getUtime(); - totalStime = totalStime.add(p.getStime()); - } - } - return totalStime.add(BigInteger.valueOf(totalUtime)); - } - /** * Get the CPU usage by all the processes in the process-tree in Unix. * Note: UNAVAILABLE will be returned in case when CPU usage is not @@ -464,10 +458,9 @@ private BigInteger getTotalProcessJiffies() { */ @Override public float getCpuUsagePercent() { - BigInteger processTotalJiffies = getTotalProcessJiffies(); - LOG.debug("Process {} jiffies:{}", pid, processTotalJiffies); - cpuTimeTracker.updateElapsedJiffies(processTotalJiffies, - clock.getTime()); + if (JIFFY_LENGTH_IN_MILLIS < 0) { + return UNAVAILABLE; + } return cpuTimeTracker.getCpuTrackerUsagePercent(); } diff --git a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestProcfsBasedProcessTree.java b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestProcfsBasedProcessTree.java index 8065f40eb43..eeaa3ef0021 100644 --- a/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestProcfsBasedProcessTree.java +++ b/hadoop-yarn-project/hadoop-yarn/hadoop-yarn-common/src/test/java/org/apache/hadoop/yarn/util/TestProcfsBasedProcessTree.java @@ -989,4 +989,153 @@ private static void writeCmdLineFiles(File procfsRootDir, String[] pids, } } } + + @Test(timeout = 30000) + public void testCpuCumulative() throws IOException { + + // test processes + String[] pids = { "100", "200", "300", "400", "500" }; + ControlledClock testClock = new ControlledClock(); + testClock.setTime(0); + // create the fake procfs root directory. + File procfsRootDir = new File(TEST_ROOT_DIR, "proc"); + + try { + setupProcfsRootDir(procfsRootDir); + setupPidDirs(procfsRootDir, pids); + + // create stat objects. + // assuming processes 100, 200, 300, 500 are in tree and 400 is not. + ProcessStatInfo[] procInfos = new ProcessStatInfo[5]; + procInfos[0] = + new ProcessStatInfo(new String[]{"100", "proc1", "1", "100", "100", + "100000", "100", "1000", "200"}); + procInfos[1] = + new ProcessStatInfo(new String[]{"200", "process two", "100", "100", + "100", "200000", "200", "2000", "400"}); + procInfos[2] = + new ProcessStatInfo(new String[]{"300", "proc(3)", "200", "100", + "100", "300000", "300", "3000", "600"}); + procInfos[3] = + new ProcessStatInfo(new String[]{"400", "proc4", "1", "400", "400", + "400000", "400", "4000", "800"}); + procInfos[4] = + new ProcessStatInfo(new String[]{"500", "proc5", "200", "500", "500", + "100", "50000", "5000", "1000"}); + writeStatFiles(procfsRootDir, pids, procInfos, null); + + // crank up the process tree class. + Configuration conf = new Configuration(); + ProcfsBasedProcessTree processTree = + createProcessTree("100", procfsRootDir.getAbsolutePath(), testClock); + processTree.setConf(conf); + // build the process tree. + processTree.updateProcessTree(); + + // verify cumulative cpu time + long cumuCpuTime = + ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS > 0 + ? 13200L * ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS : 0L; + Assert.assertEquals("Cumulative cpu time does not match", cumuCpuTime, + processTree.getCumulativeCpuTime()); + Assert.assertEquals("Cumulative cpu time does not match", cumuCpuTime, + processTree.getCumulativeCpuTime()); + + } finally { + FileUtil.fullyDelete(procfsRootDir); + } + } + + @Test(timeout = 30000) + public void testCpuAfterSubprocessDestroyed() throws IOException { + + // test processes + String[] pids = { "100", "200", "300", "400", "500" }; + ControlledClock testClock = new ControlledClock(); + testClock.setTime(0); + // create the fake procfs root directory. + File procfsRootDir = new File(TEST_ROOT_DIR, "proc"); + + try { + setupProcfsRootDir(procfsRootDir); + setupPidDirs(procfsRootDir, pids); + + // create stat objects. + // assuming processes 100, 200, 300, 500 are in tree and 400 is not. + ProcessStatInfo[] procInfos = new ProcessStatInfo[5]; + procInfos[0] = + new ProcessStatInfo(new String[]{"100", "proc1", "1", "100", "100", + "100000", "100", "1000", "200"}); + procInfos[1] = + new ProcessStatInfo(new String[]{"200", "process two", "100", "100", + "100", "200000", "200", "2000", "400"}); + procInfos[2] = + new ProcessStatInfo(new String[]{"300", "proc(3)", "200", "100", + "100", "300000", "300", "3000", "600"}); + procInfos[3] = + new ProcessStatInfo(new String[]{"400", "proc4", "1", "400", "400", + "400000", "400", "4000", "800"}); + procInfos[4] = + new ProcessStatInfo(new String[]{"500", "proc5", "200", "500", "500", + "100", "50000", "5000", "1000"}); + writeStatFiles(procfsRootDir, pids, procInfos, null); + + // crank up the process tree class. + Configuration conf = new Configuration(); + ProcfsBasedProcessTree processTree = + createProcessTree("100", procfsRootDir.getAbsolutePath(), testClock); + processTree.setConf(conf); + // build the process tree. + processTree.updateProcessTree(); + + // verify cumulative cpu time + long cumuCpuTime = + ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS > 0 + ? 13200L * ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS : 0L; + + // verify CPU usage + Assert.assertEquals("Percent CPU time should be set to -1 initially", + -1.0, processTree.getCpuUsagePercent(), + 0.01); + + // test the cpu time again to see if it cumulates + String[] pids2 = { "100", "200"}; + setupProcfsRootDir(procfsRootDir); + setupPidDirs(procfsRootDir, pids2); + ProcessStatInfo[] procInfos2 = new ProcessStatInfo[2]; + procInfos2[0] = + new ProcessStatInfo(new String[]{"100", "proc1", "1", "100", "100", + "100000", "100", "2000", "300"}); + procInfos2[1] = + new ProcessStatInfo(new String[]{"200", "process two", "100", "100", + "100", "200000", "200", "3000", "500"}); + writeStatFiles(procfsRootDir, pids2, procInfos2, null); + + long elapsedTimeBetweenUpdatesMsec = 200000; + testClock.setTime(elapsedTimeBetweenUpdatesMsec); + // build the process tree. + processTree.updateProcessTree(); + + // verify cumulative cpu time again + long prevCumuCpuTime = cumuCpuTime; + cumuCpuTime = + ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS > 0 + ? 15400L * ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS : 0L; + + double expectedCpuUsagePercent = + (ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS > 0) ? + (cumuCpuTime - prevCumuCpuTime) * 100.0 / + elapsedTimeBetweenUpdatesMsec : 0; + // expectedCpuUsagePercent is given by (154000L - 132000) * 100/ + // 200000; + // which in this case is 11. Lets verify that first + Assert.assertEquals(11, expectedCpuUsagePercent, 0.001); + Assert.assertEquals("Percent CPU time is not correct expected " + + expectedCpuUsagePercent, expectedCpuUsagePercent, + processTree.getCpuUsagePercent(), + 0.01); + } finally { + FileUtil.fullyDelete(procfsRootDir); + } + } }