|
@@ -1,677 +0,0 @@
|
|
|
-/**
|
|
|
- * Licensed to the Apache Software Foundation (ASF) under one
|
|
|
- * or more contributor license agreements. See the NOTICE file
|
|
|
- * distributed with this work for additional information
|
|
|
- * regarding copyright ownership. The ASF licenses this file
|
|
|
- * to you 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 org.apache.hadoop.mapreduce.util;
|
|
|
-
|
|
|
-import java.io.BufferedReader;
|
|
|
-import java.io.BufferedWriter;
|
|
|
-import java.io.File;
|
|
|
-import java.io.FileNotFoundException;
|
|
|
-import java.io.FileReader;
|
|
|
-import java.io.FileWriter;
|
|
|
-import java.io.IOException;
|
|
|
-import java.util.Arrays;
|
|
|
-import java.util.Random;
|
|
|
-import java.util.Vector;
|
|
|
-import java.util.regex.Matcher;
|
|
|
-import java.util.regex.Pattern;
|
|
|
-
|
|
|
-import org.apache.commons.logging.Log;
|
|
|
-import org.apache.commons.logging.LogFactory;
|
|
|
-import org.apache.hadoop.fs.FileUtil;
|
|
|
-import org.apache.hadoop.fs.Path;
|
|
|
-import org.apache.hadoop.util.StringUtils;
|
|
|
-import org.apache.hadoop.util.Shell.ExitCodeException;
|
|
|
-import org.apache.hadoop.util.Shell.ShellCommandExecutor;
|
|
|
-
|
|
|
-import junit.framework.TestCase;
|
|
|
-
|
|
|
-/**
|
|
|
- * A JUnit test to test ProcfsBasedProcessTree.
|
|
|
- */
|
|
|
-public class TestProcfsBasedProcessTree extends TestCase {
|
|
|
-
|
|
|
- private static final Log LOG = LogFactory
|
|
|
- .getLog(TestProcfsBasedProcessTree.class);
|
|
|
- private static String TEST_ROOT_DIR = new Path(System.getProperty(
|
|
|
- "test.build.data", "/tmp")).toString().replace(' ', '+');
|
|
|
-
|
|
|
- private ShellCommandExecutor shexec = null;
|
|
|
- private String pidFile, lowestDescendant;
|
|
|
- private String shellScript;
|
|
|
- private static final int N = 6; // Controls the RogueTask
|
|
|
-
|
|
|
- private class RogueTaskThread extends Thread {
|
|
|
- public void run() {
|
|
|
- try {
|
|
|
- Vector<String> args = new Vector<String>();
|
|
|
- if(ProcessTree.isSetsidAvailable) {
|
|
|
- args.add("setsid");
|
|
|
- }
|
|
|
- args.add("bash");
|
|
|
- args.add("-c");
|
|
|
- args.add(" echo $$ > " + pidFile + "; sh " +
|
|
|
- shellScript + " " + N + ";") ;
|
|
|
- shexec = new ShellCommandExecutor(args.toArray(new String[0]));
|
|
|
- shexec.execute();
|
|
|
- } catch (ExitCodeException ee) {
|
|
|
- LOG.info("Shell Command exit with a non-zero exit code. This is" +
|
|
|
- " expected as we are killing the subprocesses of the" +
|
|
|
- " task intentionally. " + ee);
|
|
|
- } catch (IOException ioe) {
|
|
|
- LOG.info("Error executing shell command " + ioe);
|
|
|
- } finally {
|
|
|
- LOG.info("Exit code: " + shexec.getExitCode());
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private String getRogueTaskPID() {
|
|
|
- File f = new File(pidFile);
|
|
|
- while (!f.exists()) {
|
|
|
- try {
|
|
|
- Thread.sleep(500);
|
|
|
- } catch (InterruptedException ie) {
|
|
|
- break;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // read from pidFile
|
|
|
- return getPidFromPidFile(pidFile);
|
|
|
- }
|
|
|
-
|
|
|
- public void testProcessTree() {
|
|
|
-
|
|
|
- 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, this.getName() + "_shellScript_" +
|
|
|
- rm.nextInt() + ".sh");
|
|
|
- tempFile.deleteOnExit();
|
|
|
- shellScript = TEST_ROOT_DIR + File.separator + tempFile.getName();
|
|
|
-
|
|
|
- // create pid file
|
|
|
- tempFile = new File(TEST_ROOT_DIR, this.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 = new ProcfsBasedProcessTree(pid,
|
|
|
- ProcessTree.isSetsidAvailable,
|
|
|
- ProcessTree.DEFAULT_SLEEPTIME_BEFORE_SIGKILL);
|
|
|
- p.updateProcessTree(); // 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.updateProcessTree(); // reconstruct
|
|
|
- LOG.info("ProcessTree: " + p.toString());
|
|
|
-
|
|
|
- // Get the process-tree dump
|
|
|
- String processTreeDump = p.getProcessTreeDump();
|
|
|
-
|
|
|
- // destroy the process and all its subprocesses
|
|
|
- p.destroy(true/*in the background*/);
|
|
|
-
|
|
|
- if(ProcessTree.isSetsidAvailable) {// whole processtree should be gone
|
|
|
- assertEquals(false, p.isAnyProcessInTreeAlive());
|
|
|
- }
|
|
|
- else {// process should be gone
|
|
|
- assertFalse("ProcessTree must have been gone", p.isAlive());
|
|
|
- }
|
|
|
-
|
|
|
- LOG.info("Process-tree dump follows: \n" + processTreeDump);
|
|
|
- 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);
|
|
|
- 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.updateProcessTree();
|
|
|
- assertFalse("ProcessTree must have been gone", p.isAlive());
|
|
|
- assertTrue("Cumulative vmem for the gone-process is "
|
|
|
- + p.getCumulativeVmem() + " . It should be zero.", p
|
|
|
- .getCumulativeVmem() == 0);
|
|
|
- assertTrue(p.toString().equals("[ ]"));
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Get PID from a pid-file.
|
|
|
- *
|
|
|
- * @param pidFileName
|
|
|
- * Name of the pid-file.
|
|
|
- * @return the PID string read from the pid-file. Returns null if the
|
|
|
- * pidFileName points to a non-existing file or if read fails from the
|
|
|
- * file.
|
|
|
- */
|
|
|
- public static String getPidFromPidFile(String pidFileName) {
|
|
|
- BufferedReader pidFile = null;
|
|
|
- FileReader fReader = null;
|
|
|
- String pid = null;
|
|
|
-
|
|
|
- try {
|
|
|
- fReader = new FileReader(pidFileName);
|
|
|
- pidFile = new BufferedReader(fReader);
|
|
|
- } catch (FileNotFoundException f) {
|
|
|
- LOG.debug("PidFile doesn't exist : " + pidFileName);
|
|
|
- return pid;
|
|
|
- }
|
|
|
-
|
|
|
- try {
|
|
|
- pid = pidFile.readLine();
|
|
|
- } catch (IOException i) {
|
|
|
- LOG.error("Failed to read from " + pidFileName);
|
|
|
- } finally {
|
|
|
- try {
|
|
|
- if (fReader != null) {
|
|
|
- fReader.close();
|
|
|
- }
|
|
|
- try {
|
|
|
- if (pidFile != null) {
|
|
|
- pidFile.close();
|
|
|
- }
|
|
|
- } catch (IOException i) {
|
|
|
- LOG.warn("Error closing the stream " + pidFile);
|
|
|
- }
|
|
|
- } catch (IOException i) {
|
|
|
- LOG.warn("Error closing the stream " + fReader);
|
|
|
- }
|
|
|
- }
|
|
|
- return pid;
|
|
|
- }
|
|
|
-
|
|
|
- public static class ProcessStatInfo {
|
|
|
- // sample stat in a single line : 3910 (gpm) S 1 3910 3910 0 -1 4194624
|
|
|
- // 83 0 0 0 0 0 0 0 16 0 1 0 7852 2408448 88 4294967295 134512640
|
|
|
- // 134590050 3220521392 3220520036 10975138 0 0 4096 134234626
|
|
|
- // 4294967295 0 0 17 1 0 0
|
|
|
- String pid;
|
|
|
- String name;
|
|
|
- String ppid;
|
|
|
- String pgrpId;
|
|
|
- String session;
|
|
|
- String vmem = "0";
|
|
|
- String rssmemPage = "0";
|
|
|
- String utime = "0";
|
|
|
- String stime = "0";
|
|
|
-
|
|
|
- public ProcessStatInfo(String[] statEntries) {
|
|
|
- pid = statEntries[0];
|
|
|
- name = statEntries[1];
|
|
|
- ppid = statEntries[2];
|
|
|
- pgrpId = statEntries[3];
|
|
|
- session = statEntries[4];
|
|
|
- vmem = statEntries[5];
|
|
|
- if (statEntries.length > 6) {
|
|
|
- rssmemPage = statEntries[6];
|
|
|
- }
|
|
|
- if (statEntries.length > 7) {
|
|
|
- utime = statEntries[7];
|
|
|
- stime = statEntries[8];
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- // construct a line that mimics the procfs stat file.
|
|
|
- // all unused numerical entries are set to 0.
|
|
|
- public String getStatLine() {
|
|
|
- return String.format("%s (%s) S %s %s %s 0 0 0" +
|
|
|
- " 0 0 0 0 %s %s 0 0 0 0 0 0 0 %s %s 0 0" +
|
|
|
- " 0 0 0 0 0 0 0 0" +
|
|
|
- " 0 0 0 0 0",
|
|
|
- pid, name, ppid, pgrpId, session,
|
|
|
- utime, stime, vmem, rssmemPage);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 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.
|
|
|
- */
|
|
|
- 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 =
|
|
|
- new ProcfsBasedProcessTree("100", true, 100L,
|
|
|
- procfsRootDir.getAbsolutePath());
|
|
|
- // build the process tree.
|
|
|
- processTree.updateProcessTree();
|
|
|
-
|
|
|
- // verify cumulative memory
|
|
|
- assertEquals("Cumulative virtual memory does not match", 600000L,
|
|
|
- processTree.getCumulativeVmem());
|
|
|
-
|
|
|
- // verify rss memory
|
|
|
- long cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ?
|
|
|
- 600L * ProcfsBasedProcessTree.PAGE_SIZE : 0L;
|
|
|
- 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;
|
|
|
- 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.updateProcessTree();
|
|
|
-
|
|
|
- // verify cumulative cpu time again
|
|
|
- cumuCpuTime = ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS > 0 ?
|
|
|
- 9400L * ProcfsBasedProcessTree.JIFFY_LENGTH_IN_MILLIS : 0L;
|
|
|
- assertEquals("Cumulative cpu time does not match",
|
|
|
- cumuCpuTime, processTree.getCumulativeCpuTime());
|
|
|
- } 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.
|
|
|
- */
|
|
|
- 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 =
|
|
|
- new ProcfsBasedProcessTree("100", true, 100L,
|
|
|
- procfsRootDir.getAbsolutePath());
|
|
|
- // build the process tree.
|
|
|
- processTree.updateProcessTree();
|
|
|
-
|
|
|
- // verify cumulative memory
|
|
|
- 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.updateProcessTree();
|
|
|
- assertEquals("Cumulative vmem does not include new process",
|
|
|
- 1200000L, processTree.getCumulativeVmem());
|
|
|
- long cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ?
|
|
|
- 1200L * ProcfsBasedProcessTree.PAGE_SIZE : 0L;
|
|
|
- assertEquals("Cumulative rssmem does not include new process",
|
|
|
- cumuRssMem, processTree.getCumulativeRssmem());
|
|
|
-
|
|
|
- // however processes older than 1 iteration will retain the older value
|
|
|
- assertEquals("Cumulative vmem shouldn't have included new process",
|
|
|
- 700000L, processTree.getCumulativeVmem(1));
|
|
|
- cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ?
|
|
|
- 700L * ProcfsBasedProcessTree.PAGE_SIZE : 0L;
|
|
|
- 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.updateProcessTree();
|
|
|
-
|
|
|
- // processes older than 2 iterations should be same as before.
|
|
|
- assertEquals("Cumulative vmem shouldn't have included new processes",
|
|
|
- 700000L, processTree.getCumulativeVmem(2));
|
|
|
- cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ?
|
|
|
- 700L * ProcfsBasedProcessTree.PAGE_SIZE : 0L;
|
|
|
- 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
|
|
|
- assertEquals("Cumulative vmem shouldn't have included new processes",
|
|
|
- 1200000L, processTree.getCumulativeVmem(1));
|
|
|
- cumuRssMem = ProcfsBasedProcessTree.PAGE_SIZE > 0 ?
|
|
|
- 1200L * ProcfsBasedProcessTree.PAGE_SIZE : 0L;
|
|
|
- assertEquals("Cumulative rssmem shouldn't have included new processes",
|
|
|
- cumuRssMem, processTree.getCumulativeRssmem(1));
|
|
|
-
|
|
|
- // no processes older than 3 iterations, this should be 0
|
|
|
- assertEquals("Getting non-zero vmem for processes older than 3 iterations",
|
|
|
- 0L, processTree.getCumulativeVmem(3));
|
|
|
- assertEquals("Getting non-zero rssmem for processes older than 3 iterations",
|
|
|
- 0L, processTree.getCumulativeRssmem(3));
|
|
|
- } finally {
|
|
|
- FileUtil.fullyDelete(procfsRootDir);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * 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.
|
|
|
- */
|
|
|
- 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.
|
|
|
- ProcfsBasedProcessTree processTree = new ProcfsBasedProcessTree(
|
|
|
- pid, true, 100L, procfsRootDir.getAbsolutePath());
|
|
|
-
|
|
|
- // Let us not create stat file for pid 100.
|
|
|
- assertTrue(ProcfsBasedProcessTree.checkPidPgrpidForMatch(
|
|
|
- pid, procfsRootDir.getAbsolutePath()));
|
|
|
- } finally {
|
|
|
- FileUtil.fullyDelete(procfsRootDir);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Test the correctness of process-tree dump.
|
|
|
- *
|
|
|
- * @throws IOException
|
|
|
- */
|
|
|
- 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 =
|
|
|
- new ProcfsBasedProcessTree("100", true, 100L, procfsRootDir
|
|
|
- .getAbsolutePath());
|
|
|
- // build the process tree.
|
|
|
- processTree.updateProcessTree();
|
|
|
-
|
|
|
- // Get the process-tree dump
|
|
|
- String processTreeDump = processTree.getProcessTreeDump();
|
|
|
-
|
|
|
- LOG.info("Process-tree dump follows: \n" + processTreeDump);
|
|
|
- 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];
|
|
|
- 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];
|
|
|
- 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);
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Create a directory to mimic the procfs file system's root.
|
|
|
- * @param procfsRootDir root directory to create.
|
|
|
- * @throws IOException if could not delete the procfs root directory
|
|
|
- */
|
|
|
- public static void setupProcfsRootDir(File procfsRootDir) {
|
|
|
- // cleanup any existing process root dir.
|
|
|
- if (procfsRootDir.exists()) {
|
|
|
- assertTrue(FileUtil.fullyDelete(procfsRootDir));
|
|
|
- }
|
|
|
-
|
|
|
- // create afresh
|
|
|
- assertTrue(procfsRootDir.mkdirs());
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Create PID directories under the specified procfs root directory
|
|
|
- * @param procfsRootDir root directory of procfs file system
|
|
|
- * @param pids the PID directories to create.
|
|
|
- * @throws IOException If PID dirs could not be created
|
|
|
- */
|
|
|
- public static void setupPidDirs(File procfsRootDir, String[] pids)
|
|
|
- throws IOException {
|
|
|
- for (String pid : pids) {
|
|
|
- File pidDir = new File(procfsRootDir, pid);
|
|
|
- pidDir.mkdir();
|
|
|
- if (!pidDir.exists()) {
|
|
|
- throw new IOException ("couldn't make process directory under " +
|
|
|
- "fake procfs");
|
|
|
- } else {
|
|
|
- LOG.info("created pid dir");
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Write stat files under the specified pid directories with data
|
|
|
- * setup in the corresponding ProcessStatInfo objects
|
|
|
- * @param procfsRootDir root directory of procfs file system
|
|
|
- * @param pids the PID directories under which to create the stat file
|
|
|
- * @param procs corresponding ProcessStatInfo objects whose data should be
|
|
|
- * written to the stat files.
|
|
|
- * @throws IOException if stat files could not be written
|
|
|
- */
|
|
|
- public static void writeStatFiles(File procfsRootDir, String[] pids,
|
|
|
- ProcessStatInfo[] procs) throws IOException {
|
|
|
- for (int i=0; i<pids.length; i++) {
|
|
|
- File statFile =
|
|
|
- new File(new File(procfsRootDir, pids[i]),
|
|
|
- ProcfsBasedProcessTree.PROCFS_STAT_FILE);
|
|
|
- BufferedWriter bw = null;
|
|
|
- try {
|
|
|
- FileWriter fw = new FileWriter(statFile);
|
|
|
- bw = new BufferedWriter(fw);
|
|
|
- bw.write(procs[i].getStatLine());
|
|
|
- LOG.info("wrote stat file for " + pids[i] +
|
|
|
- " with contents: " + procs[i].getStatLine());
|
|
|
- } finally {
|
|
|
- // not handling exception - will throw an error and fail the test.
|
|
|
- if (bw != null) {
|
|
|
- bw.close();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private static void writeCmdLineFiles(File procfsRootDir, String[] pids,
|
|
|
- String[] cmdLines)
|
|
|
- throws IOException {
|
|
|
- for (int i = 0; i < pids.length; i++) {
|
|
|
- File statFile =
|
|
|
- new File(new File(procfsRootDir, pids[i]),
|
|
|
- ProcfsBasedProcessTree.PROCFS_CMDLINE_FILE);
|
|
|
- BufferedWriter bw = null;
|
|
|
- try {
|
|
|
- bw = new BufferedWriter(new FileWriter(statFile));
|
|
|
- bw.write(cmdLines[i]);
|
|
|
- LOG.info("wrote command-line file for " + pids[i] + " with contents: "
|
|
|
- + cmdLines[i]);
|
|
|
- } finally {
|
|
|
- // not handling exception - will throw an error and fail the test.
|
|
|
- if (bw != null) {
|
|
|
- bw.close();
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
- }
|
|
|
-}
|