jdk/test/sun/tools/jstatd/JstatdTest.java
changeset 21410 5f3e83a059c4
child 21627 5e31d6573ec2
equal deleted inserted replaced
21409:246ccfb4a0f0 21410:5f3e83a059c4
       
     1 /*
       
     2  * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
       
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
       
     4  *
       
     5  * This code is free software; you can redistribute it and/or modify it
       
     6  * under the terms of the GNU General Public License version 2 only, as
       
     7  * published by the Free Software Foundation.
       
     8  *
       
     9  * This code is distributed in the hope that it will be useful, but WITHOUT
       
    10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
       
    11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
       
    12  * version 2 for more details (a copy is included in the LICENSE file that
       
    13  * accompanied this code).
       
    14  *
       
    15  * You should have received a copy of the GNU General Public License version
       
    16  * 2 along with this work; if not, write to the Free Software Foundation,
       
    17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
       
    18  *
       
    19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
       
    20  * or visit www.oracle.com if you need additional information or have any
       
    21  * questions.
       
    22  */
       
    23 
       
    24 import java.io.File;
       
    25 import java.net.UnknownHostException;
       
    26 import java.rmi.RemoteException;
       
    27 import java.rmi.registry.LocateRegistry;
       
    28 import java.rmi.registry.Registry;
       
    29 import java.util.Arrays;
       
    30 
       
    31 import static jdk.testlibrary.Asserts.*;
       
    32 import jdk.testlibrary.JDKToolLauncher;
       
    33 import jdk.testlibrary.OutputAnalyzer;
       
    34 import jdk.testlibrary.ProcessThread;
       
    35 import jdk.testlibrary.TestThread;
       
    36 import jdk.testlibrary.Utils;
       
    37 
       
    38 /**
       
    39  * The base class for tests of jstatd.
       
    40  *
       
    41  * The test sequence for TestJstatdDefaults for example is:
       
    42  * <pre>
       
    43  * {@code
       
    44  * // start jstatd process
       
    45  * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy
       
    46  *
       
    47  * // run jps and verify its output
       
    48  * jps -J-XX:+UsePerfData hostname
       
    49  *
       
    50  * // run jstat and verify its output
       
    51  * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname 250 5
       
    52  *
       
    53  * // stop jstatd process and verify that no unexpected exceptions have been thrown
       
    54  * }
       
    55  * </pre>
       
    56  */
       
    57 public final class JstatdTest {
       
    58 
       
    59     /**
       
    60      * jstat gcutil option: takes JSTAT_GCUTIL_SAMPLES samples at
       
    61      * JSTAT_GCUTIL_INTERVAL_MS millisecond intervals
       
    62      */
       
    63     private static final int JSTAT_GCUTIL_SAMPLES = 5;
       
    64     private static final int JSTAT_GCUTIL_INTERVAL_MS = 250;
       
    65     private static final String JPS_OUTPUT_REGEX = "^\\d+\\s*.*";
       
    66 
       
    67     private boolean useDefaultPort = true;
       
    68     private String port;
       
    69     private String serverName;
       
    70     private String jstatdPid;
       
    71     private boolean withExternalRegistry = false;
       
    72 
       
    73     public void setServerName(String serverName) {
       
    74         this.serverName = serverName;
       
    75     }
       
    76 
       
    77     public void setUseDefaultPort(boolean useDefaultPort) {
       
    78         this.useDefaultPort = useDefaultPort;
       
    79     }
       
    80 
       
    81     public void setWithExternalRegistry(boolean withExternalRegistry) {
       
    82         this.withExternalRegistry = withExternalRegistry;
       
    83     }
       
    84 
       
    85     /**
       
    86      * Parse pid from jps output
       
    87      */
       
    88     private String parsePid(String tool, OutputAnalyzer output) throws Exception {
       
    89         String[] lines = output.getOutput().split(Utils.NEW_LINE);
       
    90         String pid = null;
       
    91         int count = 0;
       
    92         String processName = tool;
       
    93         if (tool == "rmiregistry") {
       
    94             processName = "registryimpl";
       
    95         }
       
    96         for (String line : lines) {
       
    97             if (line.toLowerCase().matches("^\\d+\\s{1}" + processName + "$")) {
       
    98                 pid = line.split(" ")[0];
       
    99                 count++;
       
   100             }
       
   101         }
       
   102         if (count > 1) {
       
   103             throw new Exception("Expected one " + tool
       
   104                     + " process, got " + count + ". Test will be canceled.");
       
   105         }
       
   106 
       
   107         return pid;
       
   108     }
       
   109 
       
   110     private String getToolPid(String tool)
       
   111             throws Exception {
       
   112         OutputAnalyzer output = runJps();
       
   113         return parsePid(tool, output);
       
   114     }
       
   115 
       
   116     private String waitOnTool(String tool, TestThread thread) throws Throwable {
       
   117         while (true) {
       
   118             String pid = getToolPid(tool);
       
   119 
       
   120             if (pid != null) {
       
   121                 System.out.println(tool + " pid: " + pid);
       
   122                 return pid;
       
   123             }
       
   124 
       
   125             Throwable t = thread.getUncaught();
       
   126             if (t != null) {
       
   127                 if (t.getMessage().contains(
       
   128                         "java.rmi.server.ExportException: Port already in use")) {
       
   129                     System.out.println("Port already in use. Trying to restart with a new one...");
       
   130                     Thread.sleep(100);
       
   131                     return null;
       
   132                 } else {
       
   133                     // Something unexpected has happened
       
   134                     throw new Throwable(t);
       
   135                 }
       
   136             }
       
   137 
       
   138             System.out.println("Waiting until " + tool + " is running...");
       
   139             Thread.sleep(100);
       
   140         }
       
   141     }
       
   142 
       
   143     private void log(String caption, String... cmd) {
       
   144         System.out.println(Utils.NEW_LINE + caption + ":");
       
   145         System.out.println(Arrays.toString(cmd).replace(",", ""));
       
   146     }
       
   147 
       
   148     private String getDestination() throws UnknownHostException {
       
   149         String option = Utils.getHostname();
       
   150         if (port != null) {
       
   151             option += ":" + port;
       
   152         }
       
   153         if (serverName != null) {
       
   154             option += "/" + serverName;
       
   155         }
       
   156         return option;
       
   157     }
       
   158 
       
   159     /**
       
   160      * Depending on test settings command line can look like:
       
   161      *
       
   162      * jps -J-XX:+UsePerfData hostname
       
   163      * jps -J-XX:+UsePerfData hostname:port
       
   164      * jps -J-XX:+UsePerfData hostname/serverName
       
   165      * jps -J-XX:+UsePerfData hostname:port/serverName
       
   166      */
       
   167     private OutputAnalyzer runJps() throws Exception {
       
   168         JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jps");
       
   169         launcher.addVMArg("-XX:+UsePerfData");
       
   170         launcher.addToolArg(getDestination());
       
   171 
       
   172         String[] cmd = launcher.getCommand();
       
   173         log("Start jps", cmd);
       
   174 
       
   175         ProcessBuilder processBuilder = new ProcessBuilder(cmd);
       
   176         OutputAnalyzer output = new OutputAnalyzer(processBuilder.start());
       
   177         System.out.println(output.getOutput());
       
   178 
       
   179         return output;
       
   180     }
       
   181 
       
   182     /**
       
   183      * Verifies output form jps contains pids and programs' name information.
       
   184      * The function will discard any lines that come before the first line with pid.
       
   185      * This can happen if the JVM outputs a warning message for some reason
       
   186      * before running jps.
       
   187      *
       
   188      * The output can look like:
       
   189      * 35536 Jstatd
       
   190      * 35417 Main
       
   191      * 31103 org.eclipse.equinox.launcher_1.3.0.v20120522-1813.jar
       
   192      */
       
   193     private void verifyJpsOutput(OutputAnalyzer output) throws Exception {
       
   194         output.shouldHaveExitValue(0);
       
   195         assertFalse(output.getOutput().isEmpty(), "Output should not be empty");
       
   196 
       
   197         boolean foundFirstLineWithPid = false;
       
   198         String[] lines = output.getOutput().split(Utils.NEW_LINE);
       
   199         for (String line : lines) {
       
   200             if (!foundFirstLineWithPid) {
       
   201                 foundFirstLineWithPid = line.matches(JPS_OUTPUT_REGEX);
       
   202                 continue;
       
   203             }
       
   204             assertTrue(line.matches(JPS_OUTPUT_REGEX),
       
   205                     "Output does not match the pattern" + Utils.NEW_LINE + line);
       
   206         }
       
   207         assertTrue(foundFirstLineWithPid, "Invalid output");
       
   208     }
       
   209 
       
   210     /**
       
   211      * Depending on test settings command line can look like:
       
   212      *
       
   213      * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname 250 5
       
   214      * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname:port 250 5
       
   215      * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname/serverName 250 5
       
   216      * jstat -J-XX:+UsePerfData -J-Duser.language=en -gcutil pid@hostname:port/serverName 250 5
       
   217      */
       
   218     private OutputAnalyzer runJstat() throws Exception {
       
   219         JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jstat");
       
   220         launcher.addVMArg("-XX:+UsePerfData");
       
   221         launcher.addVMArg("-Duser.language=en");
       
   222         launcher.addToolArg("-gcutil");
       
   223         launcher.addToolArg(jstatdPid + "@" + getDestination());
       
   224         launcher.addToolArg(Integer.toString(JSTAT_GCUTIL_INTERVAL_MS));
       
   225         launcher.addToolArg(Integer.toString(JSTAT_GCUTIL_SAMPLES));
       
   226 
       
   227         String[] cmd = launcher.getCommand();
       
   228         log("Start jstat", cmd);
       
   229 
       
   230         ProcessBuilder processBuilder = new ProcessBuilder(cmd);
       
   231         OutputAnalyzer output = new OutputAnalyzer(processBuilder.start());
       
   232         System.out.println(output.getOutput());
       
   233 
       
   234         return output;
       
   235     }
       
   236 
       
   237     private void verifyJstatOutput(OutputAnalyzer output)
       
   238             throws Exception {
       
   239         output.shouldHaveExitValue(0);
       
   240         assertFalse(output.getOutput().isEmpty(), "Output should not be empty");
       
   241 
       
   242         JstatGCUtilParser gcUtilParser = new JstatGCUtilParser(
       
   243                 output.getOutput());
       
   244         gcUtilParser.parse(JSTAT_GCUTIL_SAMPLES);
       
   245     }
       
   246 
       
   247     private void runToolsAndVerify() throws Exception {
       
   248         OutputAnalyzer output = runJps();
       
   249         verifyJpsOutput(output);
       
   250 
       
   251         output = runJstat();
       
   252         verifyJstatOutput(output);
       
   253     }
       
   254 
       
   255     private Registry startRegistry()
       
   256             throws InterruptedException, RemoteException {
       
   257         Registry registry = null;
       
   258         try {
       
   259             System.out.println("Start rmiregistry on port " + port);
       
   260             registry = LocateRegistry
       
   261                     .createRegistry(Integer.parseInt(port));
       
   262         } catch (RemoteException e) {
       
   263             if (e.getMessage().contains("Port already in use")) {
       
   264                 System.out.println("Port already in use. Trying to restart with a new one...");
       
   265                 Thread.sleep(100);
       
   266                 return null;
       
   267             } else {
       
   268                 throw e;
       
   269             }
       
   270         }
       
   271         return registry;
       
   272     }
       
   273 
       
   274     private void cleanUpThread(ProcessThread thread) throws Throwable {
       
   275         if (thread != null) {
       
   276             thread.stopProcess();
       
   277             thread.joinAndThrow();
       
   278         }
       
   279     }
       
   280 
       
   281     /**
       
   282      * Depending on test settings command line can look like:
       
   283      *
       
   284      * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy
       
   285      * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -p port
       
   286      * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -n serverName
       
   287      * jstatd -J-XX:+UsePerfData -J-Djava.security.policy=all.policy -p port -n serverName
       
   288      */
       
   289     private String[] getJstatdCmd() throws UnknownHostException {
       
   290         JDKToolLauncher launcher = JDKToolLauncher.createUsingTestJDK("jstatd");
       
   291         launcher.addVMArg("-XX:+UsePerfData");
       
   292         String testSrc = System.getProperty("test.src");
       
   293         File policy = new File(testSrc, "all.policy");
       
   294         launcher.addVMArg("-Djava.security.policy=" + policy.getAbsolutePath());
       
   295         if (port != null) {
       
   296             launcher.addToolArg("-p");
       
   297             launcher.addToolArg(port);
       
   298         }
       
   299         if (serverName != null) {
       
   300             launcher.addToolArg("-n");
       
   301             launcher.addToolArg(serverName);
       
   302         }
       
   303 
       
   304         String[] cmd = launcher.getCommand();
       
   305         log("Start jstatd", cmd);
       
   306         return cmd;
       
   307     }
       
   308 
       
   309     private ProcessThread tryToSetupJstatdProcess() throws Throwable {
       
   310         ProcessThread jstatdThread = new ProcessThread("Jstatd-Thread",
       
   311                 getJstatdCmd());
       
   312         try {
       
   313             jstatdThread.start();
       
   314             // Make sure jstatd is up and running
       
   315             jstatdPid = waitOnTool("jstatd", jstatdThread);
       
   316             if (jstatdPid == null) {
       
   317                 // The port is already in use. Cancel and try with new one.
       
   318                 jstatdThread.stopProcess();
       
   319                 jstatdThread.join();
       
   320                 return null;
       
   321             }
       
   322         } catch (Throwable t) {
       
   323             // Something went wrong in the product - clean up!
       
   324             cleanUpThread(jstatdThread);
       
   325             throw t;
       
   326         }
       
   327 
       
   328         return jstatdThread;
       
   329     }
       
   330 
       
   331     public void doTest() throws Throwable {
       
   332         ProcessThread jstatdThread = null;
       
   333         try {
       
   334             while (jstatdThread == null) {
       
   335                 if (!useDefaultPort || withExternalRegistry) {
       
   336                     port = Integer.toString(Utils.getFreePort());
       
   337                 }
       
   338 
       
   339                 if (withExternalRegistry) {
       
   340                     Registry registry = startRegistry();
       
   341                     if (registry == null) {
       
   342                         // The port is already in use. Cancel and try with new one.
       
   343                         continue;
       
   344                     }
       
   345                 }
       
   346 
       
   347                 jstatdThread = tryToSetupJstatdProcess();
       
   348             }
       
   349 
       
   350             runToolsAndVerify();
       
   351         } finally {
       
   352             cleanUpThread(jstatdThread);
       
   353         }
       
   354     }
       
   355 
       
   356 }