/** * Is any of the subprocesses in the process-tree alive? Used only in tests. * * @return true if any of the processes in the process-tree is alive, false otherwise. */ private static boolean isAnyProcessInTreeAlive(ProcfsBasedProcessTree processTree) { for (String pId : processTree.getCurrentProcessIDs()) { if (isAlive(pId)) { return true; } } return false; }
/** * Verifies ProcfsBasedProcessTree.checkPidPgrpidForMatch() in case of 'constructProcessInfo() * returning null' by not writing stat file for the mock process * * @throws IOException if there was a problem setting up the fake procfs directories or files. */ @Test public void testDestroyProcessTree() throws IOException { // test process String pid = "100"; // create the fake procfs root directory. File procfsRootDir = new File(TEST_ROOT_DIR, "proc"); try { setupProcfsRootDir(procfsRootDir); // crank up the process tree class. createProcessTree(pid, procfsRootDir.getAbsolutePath()); // Let us not create stat file for pid 100. Assert.assertTrue( ProcfsBasedProcessTree.checkPidPgrpidForMatch(pid, procfsRootDir.getAbsolutePath())); } finally { FileUtil.fullyDelete(procfsRootDir); } }
@Test public void testProcessTree() throws Exception { try { if (!ProcfsBasedProcessTree.isAvailable()) { System.out.println("ProcfsBasedProcessTree is not available on this system. Not testing"); return; } } catch (Exception e) { LOG.info(StringUtils.stringifyException(e)); return; } // create shell script Random rm = new Random(); File tempFile = new File(TEST_ROOT_DIR, getClass().getName() + "_shellScript_" + rm.nextInt() + ".sh"); tempFile.deleteOnExit(); shellScript = TEST_ROOT_DIR + File.separator + tempFile.getName(); // create pid file tempFile = new File(TEST_ROOT_DIR, getClass().getName() + "_pidFile_" + rm.nextInt() + ".pid"); tempFile.deleteOnExit(); pidFile = TEST_ROOT_DIR + File.separator + tempFile.getName(); lowestDescendant = TEST_ROOT_DIR + File.separator + "lowestDescendantPidFile"; // write to shell-script try { FileWriter fWriter = new FileWriter(shellScript); fWriter.write( "# rogue task\n" + "sleep 1\n" + "echo hello\n" + "if [ $1 -ne 0 ]\n" + "then\n" + " sh " + shellScript + " $(($1-1))\n" + "else\n" + " echo $$ > " + lowestDescendant + "\n" + " while true\n do\n" + " sleep 5\n" + " done\n" + "fi"); fWriter.close(); } catch (IOException ioe) { LOG.info("Error: " + ioe); return; } Thread t = new RogueTaskThread(); t.start(); String pid = getRogueTaskPID(); LOG.info("Root process pid: " + pid); ProcfsBasedProcessTree p = createProcessTree(pid); p.getProcessTree(); // initialize LOG.info("ProcessTree: " + p.toString()); File leaf = new File(lowestDescendant); // wait till lowest descendant process of Rougue Task starts execution while (!leaf.exists()) { try { Thread.sleep(500); } catch (InterruptedException ie) { break; } } p.getProcessTree(); // reconstruct LOG.info("ProcessTree: " + p.toString()); // Get the process-tree dump String processTreeDump = p.getProcessTreeDump(); // destroy the process and all its subprocesses destroyProcessTree(pid); if (isSetsidAvailable()) { // whole processtree should be gone Assert.assertFalse("Proceesses in process group live", isAnyProcessInTreeAlive(p)); } else { // process should be gone Assert.assertFalse("ProcessTree must have been gone", isAlive(pid)); } LOG.info("Process-tree dump follows: \n" + processTreeDump); Assert.assertTrue( "Process-tree dump doesn't start with a proper header", processTreeDump.startsWith( "\t|- PID PPID PGRPID SESSID CMD_NAME " + "USER_MODE_TIME(MILLIS) SYSTEM_TIME(MILLIS) VMEM_USAGE(BYTES) " + "RSSMEM_USAGE(PAGES) FULL_CMD_LINE\n")); for (int i = N; i >= 0; i--) { String cmdLineDump = "\\|- [0-9]+ [0-9]+ [0-9]+ [0-9]+ \\(sh\\)" + " [0-9]+ [0-9]+ [0-9]+ [0-9]+ sh " + shellScript + " " + i; Pattern pat = Pattern.compile(cmdLineDump); Matcher mat = pat.matcher(processTreeDump); Assert.assertTrue( "Process-tree dump doesn't contain the cmdLineDump of " + i + "th process!", mat.find()); } // Not able to join thread sometimes when forking with large N. try { t.join(2000); LOG.info("RogueTaskThread successfully joined."); } catch (InterruptedException ie) { LOG.info("Interrupted while joining RogueTaskThread."); } // ProcessTree is gone now. Any further calls should be sane. p.getProcessTree(); Assert.assertFalse("ProcessTree must have been gone", isAlive(pid)); Assert.assertTrue( "Cumulative vmem for the gone-process is " + p.getCumulativeVmem() + " . It should be zero.", p.getCumulativeVmem() == 0); Assert.assertTrue(p.toString().equals("[ ]")); }
/** * Test the correctness of process-tree dump. * * @throws IOException */ @Test public void testProcessTreeDump() throws IOException { String[] pids = {"100", "200", "300", "400", "500", "600"}; File procfsRootDir = new File(TEST_ROOT_DIR, "proc"); try { setupProcfsRootDir(procfsRootDir); setupPidDirs(procfsRootDir, pids); int numProcesses = pids.length; // Processes 200, 300, 400 and 500 are descendants of 100. 600 is not. ProcessStatInfo[] procInfos = new ProcessStatInfo[numProcesses]; procInfos[0] = new ProcessStatInfo( new String[] {"100", "proc1", "1", "100", "100", "100000", "100", "1000", "200"}); procInfos[1] = new ProcessStatInfo( new String[] {"200", "proc2", "100", "100", "100", "200000", "200", "2000", "400"}); procInfos[2] = new ProcessStatInfo( new String[] {"300", "proc3", "200", "100", "100", "300000", "300", "3000", "600"}); procInfos[3] = new ProcessStatInfo( new String[] {"400", "proc4", "200", "100", "100", "400000", "400", "4000", "800"}); procInfos[4] = new ProcessStatInfo( new String[] {"500", "proc5", "400", "100", "100", "400000", "400", "4000", "800"}); procInfos[5] = new ProcessStatInfo( new String[] {"600", "proc6", "1", "1", "1", "400000", "400", "4000", "800"}); String[] cmdLines = new String[numProcesses]; cmdLines[0] = "proc1 arg1 arg2"; cmdLines[1] = "proc2 arg3 arg4"; cmdLines[2] = "proc3 arg5 arg6"; cmdLines[3] = "proc4 arg7 arg8"; cmdLines[4] = "proc5 arg9 arg10"; cmdLines[5] = "proc6 arg11 arg12"; writeStatFiles(procfsRootDir, pids, procInfos); writeCmdLineFiles(procfsRootDir, pids, cmdLines); ProcfsBasedProcessTree processTree = createProcessTree("100", procfsRootDir.getAbsolutePath()); // build the process tree. processTree.getProcessTree(); // Get the process-tree dump String processTreeDump = processTree.getProcessTreeDump(); LOG.info("Process-tree dump follows: \n" + processTreeDump); Assert.assertTrue( "Process-tree dump doesn't start with a proper header", processTreeDump.startsWith( "\t|- PID PPID PGRPID SESSID CMD_NAME " + "USER_MODE_TIME(MILLIS) SYSTEM_TIME(MILLIS) VMEM_USAGE(BYTES) " + "RSSMEM_USAGE(PAGES) FULL_CMD_LINE\n")); for (int i = 0; i < 5; i++) { ProcessStatInfo p = procInfos[i]; Assert.assertTrue( "Process-tree dump doesn't contain the cmdLineDump of process " + p.pid, processTreeDump.contains( "\t|- " + p.pid + " " + p.ppid + " " + p.pgrpId + " " + p.session + " (" + p.name + ") " + p.utime + " " + p.stime + " " + p.vmem + " " + p.rssmemPage + " " + cmdLines[i])); } // 600 should not be in the dump ProcessStatInfo p = procInfos[5]; Assert.assertFalse( "Process-tree dump shouldn't contain the cmdLineDump of process " + p.pid, processTreeDump.contains( "\t|- " + p.pid + " " + p.ppid + " " + p.pgrpId + " " + p.session + " (" + p.name + ") " + p.utime + " " + p.stime + " " + p.vmem + " " + cmdLines[5])); } finally { FileUtil.fullyDelete(procfsRootDir); } }
/** * Tests that cumulative memory is computed only for processes older than a given age. * * @throws IOException if there was a problem setting up the fake procfs directories or files. */ @Test public void testMemForOlderProcesses() throws IOException { // initial list of processes String[] pids = {"100", "200", "300", "400"}; // create the fake procfs root directory. File procfsRootDir = new File(TEST_ROOT_DIR, "proc"); try { setupProcfsRootDir(procfsRootDir); setupPidDirs(procfsRootDir, pids); // create stat objects. // assuming 100, 200 and 400 are in tree, 300 is not. ProcessStatInfo[] procInfos = new ProcessStatInfo[4]; procInfos[0] = new ProcessStatInfo(new String[] {"100", "proc1", "1", "100", "100", "100000", "100"}); procInfos[1] = new ProcessStatInfo(new String[] {"200", "proc2", "100", "100", "100", "200000", "200"}); procInfos[2] = new ProcessStatInfo(new String[] {"300", "proc3", "1", "300", "300", "300000", "300"}); procInfos[3] = new ProcessStatInfo(new String[] {"400", "proc4", "100", "100", "100", "400000", "400"}); writeStatFiles(procfsRootDir, pids, procInfos); // crank up the process tree class. ProcfsBasedProcessTree processTree = createProcessTree("100", procfsRootDir.getAbsolutePath()); // build the process tree. processTree.getProcessTree(); // verify cumulative memory Assert.assertEquals( "Cumulative memory does not match", 700000L, processTree.getCumulativeVmem()); // write one more process as child of 100. String[] newPids = {"500"}; setupPidDirs(procfsRootDir, newPids); ProcessStatInfo[] newProcInfos = new ProcessStatInfo[1]; newProcInfos[0] = new ProcessStatInfo(new String[] {"500", "proc5", "100", "100", "100", "500000", "500"}); writeStatFiles(procfsRootDir, newPids, newProcInfos); // check memory includes the new process. processTree.getProcessTree(); Assert.assertEquals( "Cumulative vmem does not include new process", 1200000L, processTree.getCumulativeVmem()); long cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ? 1200L * ProcfsBasedProcessTree.PAGE_SIZE : 0L; Assert.assertEquals( "Cumulative rssmem does not include new process", cumuRssMem, processTree.getCumulativeRssmem()); // however processes older than 1 iteration will retain the older value Assert.assertEquals( "Cumulative vmem shouldn't have included new process", 700000L, processTree.getCumulativeVmem(1)); cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ? 700L * ProcfsBasedProcessTree.PAGE_SIZE : 0L; Assert.assertEquals( "Cumulative rssmem shouldn't have included new process", cumuRssMem, processTree.getCumulativeRssmem(1)); // one more process newPids = new String[] {"600"}; setupPidDirs(procfsRootDir, newPids); newProcInfos = new ProcessStatInfo[1]; newProcInfos[0] = new ProcessStatInfo(new String[] {"600", "proc6", "100", "100", "100", "600000", "600"}); writeStatFiles(procfsRootDir, newPids, newProcInfos); // refresh process tree processTree.getProcessTree(); // processes older than 2 iterations should be same as before. Assert.assertEquals( "Cumulative vmem shouldn't have included new processes", 700000L, processTree.getCumulativeVmem(2)); cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ? 700L * ProcfsBasedProcessTree.PAGE_SIZE : 0L; Assert.assertEquals( "Cumulative rssmem shouldn't have included new processes", cumuRssMem, processTree.getCumulativeRssmem(2)); // processes older than 1 iteration should not include new process, // but include process 500 Assert.assertEquals( "Cumulative vmem shouldn't have included new processes", 1200000L, processTree.getCumulativeVmem(1)); cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ? 1200L * ProcfsBasedProcessTree.PAGE_SIZE : 0L; Assert.assertEquals( "Cumulative rssmem shouldn't have included new processes", cumuRssMem, processTree.getCumulativeRssmem(1)); // no processes older than 3 iterations, this should be 0 Assert.assertEquals( "Getting non-zero vmem for processes older than 3 iterations", 0L, processTree.getCumulativeVmem(3)); Assert.assertEquals( "Getting non-zero rssmem for processes older than 3 iterations", 0L, processTree.getCumulativeRssmem(3)); } finally { FileUtil.fullyDelete(procfsRootDir); } }
/** * A basic test that creates a few process directories and writes stat files. Verifies that the * cpu time and memory is correctly computed. * * @throws IOException if there was a problem setting up the fake procfs directories or files. */ @Test public void testCpuAndMemoryForProcessTree() throws IOException { // test processes String[] pids = {"100", "200", "300", "400"}; // 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 are in tree and 400 is not. ProcessStatInfo[] procInfos = new ProcessStatInfo[4]; procInfos[0] = new ProcessStatInfo( new String[] {"100", "proc1", "1", "100", "100", "100000", "100", "1000", "200"}); procInfos[1] = new ProcessStatInfo( new String[] {"200", "proc2", "100", "100", "100", "200000", "200", "2000", "400"}); procInfos[2] = new ProcessStatInfo( new String[] {"300", "proc3", "200", "100", "100", "300000", "300", "3000", "600"}); procInfos[3] = new ProcessStatInfo( new String[] {"400", "proc4", "1", "400", "400", "400000", "400", "4000", "800"}); writeStatFiles(procfsRootDir, pids, procInfos); // crank up the process tree class. ProcfsBasedProcessTree processTree = createProcessTree("100", procfsRootDir.getAbsolutePath()); // build the process tree. processTree.getProcessTree(); // verify cumulative memory Assert.assertEquals( "Cumulative virtual memory does not match", 600000L, processTree.getCumulativeVmem()); // verify rss memory long cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ? 600L * ProcfsBasedProcessTree.PAGE_SIZE : 0L; Assert.assertEquals( "Cumulative rss memory does not match", cumuRssMem, processTree.getCumulativeRssmem()); // verify cumulative cpu time long cumuCpuTime = ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS > 0 ? 7200L * ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS : 0L; Assert.assertEquals( "Cumulative cpu time does not match", cumuCpuTime, processTree.getCumulativeCpuTime()); // test the cpu time again to see if it cumulates procInfos[0] = new ProcessStatInfo( new String[] {"100", "proc1", "1", "100", "100", "100000", "100", "2000", "300"}); procInfos[1] = new ProcessStatInfo( new String[] {"200", "proc2", "100", "100", "100", "200000", "200", "3000", "500"}); writeStatFiles(procfsRootDir, pids, procInfos); // build the process tree. processTree.getProcessTree(); // verify cumulative cpu time again cumuCpuTime = ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS > 0 ? 9400L * ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS : 0L; Assert.assertEquals( "Cumulative cpu time does not match", cumuCpuTime, processTree.getCumulativeCpuTime()); } finally { FileUtil.fullyDelete(procfsRootDir); } }