jdk/test/sun/tools/jstatd/JstatdTest.java
changeset 21410 5f3e83a059c4
child 21627 5e31d6573ec2
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/test/sun/tools/jstatd/JstatdTest.java	Wed Oct 30 13:44:40 2013 +0100
@@ -0,0 +1,356 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+import java.io.File;
+import java.net.UnknownHostException;
+import java.rmi.RemoteException;
+import java.rmi.registry.LocateRegistry;
+import java.rmi.registry.Registry;
+import java.util.Arrays;
+
+import static jdk.testlibrary.Asserts.*;
+import jdk.testlibrary.JDKToolLauncher;
+import jdk.testlibrary.OutputAnalyzer;
+import jdk.testlibrary.ProcessThread;
+import jdk.testlibrary.TestThread;
+import jdk.testlibrary.Utils;
+
+/**
+ * The base class for tests of jstatd.
+ *
+ * The test sequence for TestJstatdDefaults for example is:
+ * <pre>
+ * {@code
+ * // start jstatd process
+ * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy
+ *
+ * // run jps and verify its output
+ * jps -J-XX:+UsePerfData hostname
+ *
+ * // run jstat and verify its output
+ * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname 250 5
+ *
+ * // stop jstatd process and verify that no unexpected exceptions have been thrown
+ * }
+ * </pre>
+ */
+public final class JstatdTest {
+
+    /**
+     * jstat gcutil option: takes JSTAT_GCUTIL_SAMPLES samples at
+     * JSTAT_GCUTIL_INTERVAL_MS millisecond intervals
+     */
+    private static final int JSTAT_GCUTIL_SAMPLES = 5;
+    private static final int JSTAT_GCUTIL_INTERVAL_MS = 250;
+    private static final String JPS_OUTPUT_REGEX = "^\\d+\\s*.*";
+
+    private boolean useDefaultPort = true;
+    private String port;
+    private String serverName;
+    private String jstatdPid;
+    private boolean withExternalRegistry = false;
+
+    public void setServerName(String serverName) {
+        this.serverName = serverName;
+    }
+
+    public void setUseDefaultPort(boolean useDefaultPort) {
+        this.useDefaultPort = useDefaultPort;
+    }
+
+    public void setWithExternalRegistry(boolean withExternalRegistry) {
+        this.withExternalRegistry = withExternalRegistry;
+    }
+
+    /**
+     * Parse pid from jps output
+     */
+    private String parsePid(String tool, OutputAnalyzer output) throws Exception {
+        String[] lines = output.getOutput().split(Utils.NEW_LINE);
+        String pid = null;
+        int count = 0;
+        String processName = tool;
+        if (tool == "rmiregistry") {
+            processName = "registryimpl";
+        }
+        for (String line : lines) {
+            if (line.toLowerCase().matches("^\\d+\\s{1}" + processName + "$")) {
+                pid = line.split(" ")[0];
+                count++;
+            }
+        }
+        if (count > 1) {
+            throw new Exception("Expected one " + tool
+                    + " process, got " + count + ". Test will be canceled.");
+        }
+
+        return pid;
+    }
+
+    private String getToolPid(String tool)
+            throws Exception {
+        OutputAnalyzer output = runJps();
+        return parsePid(tool, output);
+    }
+
+    private String waitOnTool(String tool, TestThread thread) throws Throwable {
+        while (true) {
+            String pid = getToolPid(tool);
+
+            if (pid != null) {
+                System.out.println(tool + " pid: " + pid);
+                return pid;
+            }
+
+            Throwable t = thread.getUncaught();
+            if (t != null) {
+                if (t.getMessage().contains(
+                        "java.rmi.server.ExportException: Port already in use")) {
+                    System.out.println("Port already in use. Trying to restart with a new one...");
+                    Thread.sleep(100);
+                    return null;
+                } else {
+                    // Something unexpected has happened
+                    throw new Throwable(t);
+                }
+            }
+
+            System.out.println("Waiting until " + tool + " is running...");
+            Thread.sleep(100);
+        }
+    }
+
+    private void log(String caption, String... cmd) {
+        System.out.println(Utils.NEW_LINE + caption + ":");
+        System.out.println(Arrays.toString(cmd).replace(",", ""));
+    }
+
+    private String getDestination() throws UnknownHostException {
+        String option = Utils.getHostname();
+        if (port != null) {
+            option += ":" + port;
+        }
+        if (serverName != null) {
+            option += "/" + serverName;
+        }
+        return option;
+    }
+
+    /**
+     * Depending on test settings command line can look like:
+     *
+     * jps -J-XX:+UsePerfData hostname
+     * jps -J-XX:+UsePerfData hostname:port
+     * jps -J-XX:+UsePerfData hostname/serverName
+     * jps -J-XX:+UsePerfData hostname:port/serverName
+     */
+    private OutputAnalyzer runJps() throws Exception {
+        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jps");
+        launcher.addVMArg("-XX:+UsePerfData");
+        launcher.addToolArg(getDestination());
+
+        String[] cmd = launcher.getCommand();
+        log("Start jps", cmd);
+
+        ProcessBuilder processBuilder = new ProcessBuilder(cmd);
+        OutputAnalyzer output = new OutputAnalyzer(processBuilder.start());
+        System.out.println(output.getOutput());
+
+        return output;
+    }
+
+    /**
+     * Verifies output form jps contains pids and programs' name information.
+     * The function will discard any lines that come before the first line with pid.
+     * This can happen if the JVM outputs a warning message for some reason
+     * before running jps.
+     *
+     * The output can look like:
+     * 35536 Jstatd
+     * 35417 Main
+     * 31103 org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar
+     */
+    private void verifyJpsOutput(OutputAnalyzer output) throws Exception {
+        output.shouldHaveExitValue(0);
+        assertFalse(output.getOutput().isEmpty(), "Output should not be empty");
+
+        boolean foundFirstLineWithPid = false;
+        String[] lines = output.getOutput().split(Utils.NEW_LINE);
+        for (String line : lines) {
+            if (!foundFirstLineWithPid) {
+                foundFirstLineWithPid = line.matches(JPS_OUTPUT_REGEX);
+                continue;
+            }
+            assertTrue(line.matches(JPS_OUTPUT_REGEX),
+                    "Output does not match the pattern" + Utils.NEW_LINE + line);
+        }
+        assertTrue(foundFirstLineWithPid, "Invalid output");
+    }
+
+    /**
+     * Depending on test settings command line can look like:
+     *
+     * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname 250 5
+     * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname:port 250 5
+     * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname/serverName 250 5
+     * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname:port/serverName 250 5
+     */
+    private OutputAnalyzer runJstat() throws Exception {
+        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jstat");
+        launcher.addVMArg("-XX:+UsePerfData");
+        launcher.addVMArg("-Duser.language=en");
+        launcher.addToolArg("-gcutil");
+        launcher.addToolArg(jstatdPid + "@" + getDestination());
+        launcher.addToolArg(Integer.toString(JSTAT_GCUTIL_INTERVAL_MS));
+        launcher.addToolArg(Integer.toString(JSTAT_GCUTIL_SAMPLES));
+
+        String[] cmd = launcher.getCommand();
+        log("Start jstat", cmd);
+
+        ProcessBuilder processBuilder = new ProcessBuilder(cmd);
+        OutputAnalyzer output = new OutputAnalyzer(processBuilder.start());
+        System.out.println(output.getOutput());
+
+        return output;
+    }
+
+    private void verifyJstatOutput(OutputAnalyzer output)
+            throws Exception {
+        output.shouldHaveExitValue(0);
+        assertFalse(output.getOutput().isEmpty(), "Output should not be empty");
+
+        JstatGCUtilParser gcUtilParser = new JstatGCUtilParser(
+                output.getOutput());
+        gcUtilParser.parse(JSTAT_GCUTIL_SAMPLES);
+    }
+
+    private void runToolsAndVerify() throws Exception {
+        OutputAnalyzer output = runJps();
+        verifyJpsOutput(output);
+
+        output = runJstat();
+        verifyJstatOutput(output);
+    }
+
+    private Registry startRegistry()
+            throws InterruptedException, RemoteException {
+        Registry registry = null;
+        try {
+            System.out.println("Start rmiregistry on port " + port);
+            registry = LocateRegistry
+                    .createRegistry(Integer.parseInt(port));
+        } catch (RemoteException e) {
+            if (e.getMessage().contains("Port already in use")) {
+                System.out.println("Port already in use. Trying to restart with a new one...");
+                Thread.sleep(100);
+                return null;
+            } else {
+                throw e;
+            }
+        }
+        return registry;
+    }
+
+    private void cleanUpThread(ProcessThread thread) throws Throwable {
+        if (thread != null) {
+            thread.stopProcess();
+            thread.joinAndThrow();
+        }
+    }
+
+    /**
+     * Depending on test settings command line can look like:
+     *
+     * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy
+     * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -p port
+     * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -n serverName
+     * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -p port -n serverName
+     */
+    private String[] getJstatdCmd() throws UnknownHostException {
+        JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jstatd");
+        launcher.addVMArg("-XX:+UsePerfData");
+        String testSrc = System.getProperty("test.src");
+        File policy = new File(testSrc, "all.policy");
+        launcher.addVMArg("-Djava.security.policy=" + policy.getAbsolutePath());
+        if (port != null) {
+            launcher.addToolArg("-p");
+            launcher.addToolArg(port);
+        }
+        if (serverName != null) {
+            launcher.addToolArg("-n");
+            launcher.addToolArg(serverName);
+        }
+
+        String[] cmd = launcher.getCommand();
+        log("Start jstatd", cmd);
+        return cmd;
+    }
+
+    private ProcessThread tryToSetupJstatdProcess() throws Throwable {
+        ProcessThread jstatdThread = new ProcessThread("Jstatd-Thread",
+                getJstatdCmd());
+        try {
+            jstatdThread.start();
+            // Make sure jstatd is up and running
+            jstatdPid = waitOnTool("jstatd", jstatdThread);
+            if (jstatdPid == null) {
+                // The port is already in use. Cancel and try with new one.
+                jstatdThread.stopProcess();
+                jstatdThread.join();
+                return null;
+            }
+        } catch (Throwable t) {
+            // Something went wrong in the product - clean up!
+            cleanUpThread(jstatdThread);
+            throw t;
+        }
+
+        return jstatdThread;
+    }
+
+    public void doTest() throws Throwable {
+        ProcessThread jstatdThread = null;
+        try {
+            while (jstatdThread == null) {
+                if (!useDefaultPort || withExternalRegistry) {
+                    port = Integer.toString(Utils.getFreePort());
+                }
+
+                if (withExternalRegistry) {
+                    Registry registry = startRegistry();
+                    if (registry == null) {
+                        // The port is already in use. Cancel and try with new one.
+                        continue;
+                    }
+                }
+
+                jstatdThread = tryToSetupJstatdProcess();
+            }
+
+            runToolsAndVerify();
+        } finally {
+            cleanUpThread(jstatdThread);
+        }
+    }
+
+}