/**
* 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.util;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Random;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.hadoop.util.Shell.ExitCodeException;
import org.apache.hadoop.util.Shell.ShellCommandExecutor;
import junit.framework.TestCase;
public class TestProcfsBasedProcessTree extends TestCase {
private static final Log LOG = LogFactory
.getLog(TestProcfsBasedProcessTree.class);
private ShellCommandExecutor shexec = null;
private String pidFile;
private String shellScript;
private static final int N = 10; // Controls the RogueTask
private static final int memoryLimit = 15000; // kilobytes
private static final long PROCESSTREE_RECONSTRUCTION_INTERVAL =
ProcfsBasedProcessTree.DEFAULT_SLEEPTIME_BEFORE_SIGKILL; // msec
private class RogueTaskThread extends Thread {
public void run() {
try {
String args[] = { "bash", "-c",
"echo $$ > " + pidFile + "; sh " + shellScript + " " + N + ";" };
shexec = new ShellCommandExecutor(args);
shexec.execute();
} catch (ExitCodeException ee) {
LOG.info("Shell Command exit with a non-zero exit code. " + 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 ProcfsBasedProcessTree.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(this.getName() + "_shellScript_" + rm.nextInt()
+ ".sh");
tempFile.deleteOnExit();
shellScript = tempFile.getName();
// create pid file
tempFile = new File(this.getName() + "_pidFile_" + rm.nextInt() + ".pid");
tempFile.deleteOnExit();
pidFile = tempFile.getName();
// write to shell-script
try {
FileWriter fWriter = new FileWriter(shellScript);
fWriter.write(
"# rogue task\n" +
"sleep 10\n" +
"echo hello\n" +
"if [ $1 -ne 0 ]\n" +
"then\n" +
" sh " + shellScript + " $(($1-1))\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);
p = p.getProcessTree(); // initialize
try {
while (true) {
LOG.info("ProcessTree: " + p.toString());
long mem = p.getCumulativeVmem();
LOG.info("Memory usage: " + mem + "kB.");
if (mem > memoryLimit) {
p.destroy();
break;
}
Thread.sleep(PROCESSTREE_RECONSTRUCTION_INTERVAL);
p = p.getProcessTree(); // reconstruct
}
} catch (InterruptedException ie) {
LOG.info("Interrupted.");
}
assertEquals(false, p.isAlive()); // processtree should should be gone
// 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.");
}
}
}