/*
* Copyright 2009 Sun Microsystems, Inc. All Rights Reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
* CA 95054 USA or visit www.sun.com if you need additional information or
* have any questions.
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.List;
import java.util.Timer;
import java.util.TimerTask;
import java.util.jar.Attributes;
import java.util.jar.JarFile;
import java.util.jar.Manifest;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author ksrini
* @author A. Sundararajan (removed csv stuff for now)
*
* Readme:
* Here are the steps needed to run the visagebtrace.
*
* 1. Download the btrace binary bundle from https://btrace.dev.java.net/
* 2. Extract it to BTRACE_HOME
* 3. Run this class using % java options VisageBTraceRunner options
* In order to the following environment variables or properties must be set,
* this is to facilitate testing of arbitrary SDK and test specimens.
* a. BUILD_DIR or build.dir : clean location for the build files
* b. VISAGE_HOME or visage.home : location of visage sdk
* c. BTRACE_HOME or btrace.home : location of btrace distro
* d. BASE_DIR or base.dir : location of the visagebtrace directory
*
* Options:
* Must set --jar pointer to a jar with a main-class
* Optional parameters:
* --main : an alternate main-entry point to use
* --duration : how long to run the specimen for in mSecs
* --interval : the snapshot collection interval in mSecs
*
* Notes:
* VM parameters to the test specimen may be passed as follows:
* % java -DVisageBTraceRunner.vmoptions="-Xmx512m, -Xss128, -Xfoobar=XX" VisageBTraceRunner options
*
* Files:
* a. visage/visage-compiler/buildtools/visagebtrace/btrace_scripts/*.java
* Various btrace script files that can be used to trace Visage apps.
*
* b. visage/visage-compiler/buildtools/visagebtrace/src/VisageBTraceRunner.java
* This is simply a runner script takes care of compile the script,
* running btrace on the application, killing the application.
*
* c. Output files in build directory:
* <btrace-script-class>.class.btrace : btrace output
*/
public class VisageBTraceRunner {
static final Logger logger = Logger.getLogger(VisageBTraceRunner.class.getName());
static final boolean isWindows = System.getProperty("os.name").startsWith("Windows");
static boolean debug = true;
// Paths we need
static final String BUILD_DIR = System.getProperty("build.dir",
System.getenv("BUILD_DIR"));
static final String VISAGE_HOME = System.getProperty("visage.home",
System.getenv("VISAGE_HOME"));
static final String BTRACE_HOME = System.getProperty("btrace.home",
System.getenv("BTRACE_HOME"));
static final String BASE_DIR = System.getProperty("base.dir",
System.getenv("BASE_DIR"));
static final String VISAGEBTRACERUNNER_NAME = "VisageTrackerRunner";
static final String BTRACE_CLIENT_JAR = BTRACE_HOME + "/build/btrace-client.jar";
static final String VISAGERT_JAR = VISAGE_HOME + "/lib/shared/visagert.jar";
static final String BTRACE_COMPILER = "com.sun.btrace.compiler.Compiler";
static final String BTRACE_AGENT_OPT = "-javaagent:" + BTRACE_HOME +
"/build/btrace-agent.jar=unsafe=true,script=";
static String JAVA_HOME = null;
static String TOOLS_JAR = null;
static String BTRACE_COMPILE_CP = null;
static String JAVA_EXE = null;
static String VISAGE_EXE = null ;
static final String SDK_DIR = "sdk";
static final String JPSMARKER="JPSMARKER=";
// Command line arguments
static String btraceScript = null;
static String appClasspath = null;
static String mainClass = null;
static int interval = 5*1000;
static int duration = 2*60*1000;
static final String msg[] = { "--script your_btrace_script",
"--jar path_to_your_jar",
"(optional) --main entry-point",
"(optional) --interval " + interval + " (in msecs)",
"(optional) --duration " + duration + " (in msecs)"};
public static void main(String... args) {
init(args);
doRun();
System.exit(0);
}
static void init(String[] args) {
logger.setLevel(Level.parse(System.getProperty("log.level", "ALL")));
File javaHome = null;
String javaHomeEnv = System.getenv("JAVA_HOME");
if (javaHomeEnv != null) {
javaHome = new File(javaHomeEnv);
} else {
javaHome = new File(System.getProperty("java.home"));
}
if (javaHome.getName().endsWith("jre")) {
javaHome = javaHome.getParentFile();
}
JAVA_HOME=javaHome.getAbsolutePath();
boolean mustExit = false;
if (BTRACE_HOME == null) {
logger.severe("BTRACE_HOME is null");
mustExit = true;
}
if (VISAGE_HOME == null) {
logger.severe("VISAGE_HOME is null");
mustExit = true;
}
if (BASE_DIR == null) {
logger.severe("BASE_DIR is null");
mustExit = true;
}
if (BUILD_DIR == null) {
logger.severe("BUILD_DIR is null");
mustExit = true;
}
if (args != null && args.length > 0) {
for (int n = 0 ; n < args.length ; n++) {
if (args[n].equals("--script")) {
btraceScript = args[++n];
} else if (args[n].equals("--jar")) {
appClasspath = args[++n];
} else if (args[n].equals("--main")) {
mainClass = args[++n];
} else if (args[n].equals("--interval")) {
interval = Integer.parseInt(args[++n]);
} else if (args[n].equals("--duration")) {
duration = Integer.parseInt(args[++n]);
}
}
}
if (btraceScript == null) {
logger.severe("--script your_btrace_script must be specified");
usage(true);
}
if (appClasspath == null) {
logger.severe("--jar classpath_of_your_app must be specified");
usage(true);
}
if (mustExit) {
System.exit(1);
}
if (mainClass == null && appClasspath.endsWith(".jar")) {
mainClass = getMainClassFromJar(appClasspath);
}
if (mainClass == null) {
usage(true);
}
JAVA_EXE = getExe("java").getAbsolutePath();
TOOLS_JAR = JAVA_HOME + "/lib/tools.jar";
BTRACE_COMPILE_CP = BTRACE_CLIENT_JAR + File.pathSeparator + TOOLS_JAR;
VISAGE_EXE = getExe("visage").getAbsolutePath();
}
static String getMainClassFromJar(String jarfilename) {
JarFile jf = null;
String mainclassname = null;
try {
jf = new JarFile(jarfilename);
Manifest mf = jf.getManifest();
if (mf != null) {
Attributes attr = mf.getMainAttributes();
if (attr != null) {
mainclassname = attr.getValue("Main-Class");
}
}
} catch (IOException ioe) {
logger.severe("Processing: " + jarfilename + ":" + ioe.getMessage());
} finally {
if (jf != null) {
try {
jf.close();
} catch (IOException ignore) { /* swallow the exception */ }
}
}
return mainclassname;
}
static void usage(boolean mustExit) {
String out = "";
for (String x: msg) {
out = out.concat(x + "\n");
}
logger.severe(out);
if (mustExit) {
System.exit(1);
}
}
static void doRun() {
List<String> cmdsList = new ArrayList<String>();
// compile the btrace script
cmdsList.add(JAVA_EXE);
cmdsList.add("-cp");
cmdsList.add(BTRACE_COMPILE_CP);
cmdsList.add(BTRACE_COMPILER);
cmdsList.add("-unsafe");
cmdsList.add("-classpath");
cmdsList.add(VISAGERT_JAR);
cmdsList.add(btraceScript + ".java");
doExec(cmdsList);
// The btrace compiler does not return the exit codes properly, and the
// btrace launcher will continue if the script file is non-existent, so
// we test for the existence of the resultant class file for success.
File clsFile = new File(BUILD_DIR, btraceScript + ".class");
if (!clsFile.exists()) {
throw new RuntimeException(btraceScript + ".class not found");
}
// run the visagebtrace script and application
cmdsList.clear();
cmdsList.add(VISAGE_EXE);
cmdsList.add("-D" + VISAGEBTRACERUNNER_NAME + ".interval=" + interval);
String vmProps = System.getProperty(VISAGEBTRACERUNNER_NAME + ".vmoptions");
// ant could pass the property itself if undefined, ignore it.
if (vmProps != null && !vmProps.contains(VISAGEBTRACERUNNER_NAME + ".vmoptions")) {
String vmOpts[] = vmProps.split(",\\s");
for (String x : vmOpts) {
cmdsList.add(x);
}
}
cmdsList.add(BTRACE_AGENT_OPT +
clsFile.getAbsolutePath());
cmdsList.add("-cp");
cmdsList.add(TOOLS_JAR + File.pathSeparator + appClasspath);
cmdsList.add(mainClass);
startTestApplication(cmdsList);
}
static void killTestApplication() {
List<String> cmdsList = new ArrayList<String>();
String appId = getAppPid(VISAGEBTRACERUNNER_NAME);
cmdsList.clear();
if (isWindows) {
cmdsList.add("taskkill");
cmdsList.add("-PID");
} else {
cmdsList.add("kill");
cmdsList.add("-15");
}
cmdsList.add(appId);
doExec(cmdsList);
}
static List<String> doExec(String... cmds) {
List<String> cmdsList = new ArrayList<String>();
for (String x : cmds) {
cmdsList.add(x);
}
return doExec(cmdsList);
}
static String getAppPid(String appName) {
File jpsExe = getExe("jps");
List<String> output = doExec(jpsExe.getAbsolutePath(), "-l", "-v");
for (String x : output) {
if (x.contains(JPSMARKER + appName)) {
String fld[] = x.split("\\s");
return fld[0];
}
}
throw new RuntimeException("Error: could not find the JPSMARKER");
}
static List<String> doExec(List<String> cmds) {
if (debug) {
System.out.println("----Execution args----");
System.out.println("CWD=" + BUILD_DIR);
for (String x : cmds) {
System.out.print(x + " ");
}
System.out.println("");
}
List<String> outputList = new ArrayList<String>();
ProcessBuilder pb = new ProcessBuilder(cmds);
pb = pb.directory(new File(BUILD_DIR));
FileReader fr = null;
BufferedReader rdr = null;
try {
Process p = pb.start();
pb.redirectErrorStream(true);
rdr = new BufferedReader(new InputStreamReader(p.getInputStream()));
// note: its a good idea to read the whole stream, half baked
// reads can cause undesired side-effects.
String in = rdr.readLine();
if (debug) {
System.out.println("---output---");
}
while (in != null) {
if (debug) {
System.out.println(in + " ");
}
System.out.println("");
outputList.add(in);
in = rdr.readLine();
}
p.waitFor();
p.exitValue();
if (p.exitValue() != 0) {
System.out.println("Error: Unexpected exit value " +
p.exitValue());
return null;
}
return outputList;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex.getMessage());
} finally {
if (fr != null) {
try {
fr.close();
} catch (IOException ioe) {
throw new RuntimeException("Error while closing file " + ioe);
}
}
}
}
static void startTestApplication(List<String> cmds) {
if (debug) {
System.out.println("----Test-Execution args----");
for (String x : cmds) {
System.out.println(x);
}
}
ProcessBuilder pb = new ProcessBuilder(cmds);
pb = pb.directory(new File(BUILD_DIR));
pb.redirectErrorStream(true);
FileReader fr = null;
BufferedReader rdr = null;
try {
final Process p = pb.start();
rdr = new BufferedReader(new InputStreamReader(p.getInputStream()));
// note: its a good idea to read the whole stream, half baked
// reads can cause undesired side-effects.
Timer timer = new Timer();
timer.schedule(new TimerTask() {
public void run() {
if (debug) {
System.out.println("----Destroying test process----");
}
killTestApplication();
p.destroy();
}
}, duration);
String in = rdr.readLine();
if (debug) {
System.out.println("---output---");
}
while (in != null) {
// if (debug) {
// System.out.println(in + " ");
// }
// System.out.println("");
in = rdr.readLine();
}
p.waitFor();
return;
} catch (Exception ex) {
ex.printStackTrace();
throw new RuntimeException(ex.getMessage());
}
}
static File getExe(String location, String exename) {
File bindir = new File(location, "bin");
File outFile = (isWindows)
? new File(bindir, exename + ".exe")
: new File(bindir, exename);
if (outFile.exists()) {
return outFile;
}
return null;
}
static File getExe(String exename) {
File outFile = getExe(SDK_DIR, exename);
if (outFile != null) return outFile;
outFile = getExe(JAVA_HOME, exename);
if (outFile != null) return outFile;
outFile = getExe(VISAGE_HOME, exename);
if (outFile != null) return outFile;
throw new RuntimeException("Error: could not find " + exename);
}
}