CDSTestUtils.java revision 2746:fc761196703d
133965Sjdp/* 2218822Sdim * Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved. 3218822Sdim * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 478828Sobrien * 533965Sjdp * This code is free software; you can redistribute it and/or modify it 633965Sjdp * under the terms of the GNU General Public License version 2 only, as 733965Sjdp * published by the Free Software Foundation. 833965Sjdp * 933965Sjdp * This code is distributed in the hope that it will be useful, but WITHOUT 1033965Sjdp * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1133965Sjdp * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1233965Sjdp * version 2 for more details (a copy is included in the LICENSE file that 1333965Sjdp * accompanied this code). 1433965Sjdp * 1533965Sjdp * You should have received a copy of the GNU General Public License version 1633965Sjdp * 2 along with this work; if not, write to the Free Software Foundation, 1733965Sjdp * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1833965Sjdp * 1933965Sjdp * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 2033965Sjdp * or visit www.oracle.com if you need additional information or have any 2133965Sjdp * questions. 2233965Sjdp */ 2333965Sjdppackage jdk.test.lib.cds; 24218822Sdim 25218822Sdimimport java.io.IOException; 2633965Sjdpimport java.io.File; 2733965Sjdpimport java.io.FileOutputStream; 2833965Sjdpimport java.io.PrintStream; 2933965Sjdpimport java.util.ArrayList; 3033965Sjdpimport jdk.test.lib.Utils; 3133965Sjdpimport jdk.test.lib.process.OutputAnalyzer; 3233965Sjdpimport jdk.test.lib.process.ProcessTools; 3360484Sobrien 3460484Sobrien 3533965Sjdp// This class contains common test utilities for testing CDS 3633965Sjdppublic class CDSTestUtils { 3733965Sjdp // Specify this property to copy sdandard output of the child test process to 3833965Sjdp // the parent/main stdout of the test. 3933965Sjdp // By default such output is logged into a file, and is copied into the main stdout. 4033965Sjdp public static final boolean CopyChildStdoutToMainStdout = 4189857Sobrien Boolean.valueOf(System.getProperty("test.cds.copy.child.stdout", "true")); 4233965Sjdp 4333965Sjdp // This property is passed to child test processes 4433965Sjdp public static final String TestTimeoutFactor = System.getProperty("test.timeout.factor", "1.0"); 4533965Sjdp 4633965Sjdp public static final String UnableToMapMsg = 4733965Sjdp "Unable to map shared archive: test did not complete; assumed PASS"; 4833965Sjdp 4933965Sjdp // Create bootstrap CDS archive, 5033965Sjdp // use extra JVM command line args as a prefix. 5133965Sjdp // For CDS tests specifying prefix makes more sense than specifying suffix, since 5233965Sjdp // normally there are no classes or arguments to classes, just "-version" 5333965Sjdp // To specify suffix explicitly use CDSOptions.addSuffix() 5433965Sjdp public static OutputAnalyzer createArchive(String... cliPrefix) 5533965Sjdp throws Exception { 5633965Sjdp return createArchive((new CDSOptions()).addPrefix(cliPrefix)); 5733965Sjdp } 5833965Sjdp 5933965Sjdp // Create bootstrap CDS archive 6033965Sjdp public static OutputAnalyzer createArchive(CDSOptions opts) 6133965Sjdp throws Exception { 6233965Sjdp 6333965Sjdp ArrayList<String> cmd = new ArrayList<String>(); 6433965Sjdp 6533965Sjdp for (String p : opts.prefix) cmd.add(p); 6633965Sjdp 6733965Sjdp cmd.add("-Xshare:dump"); 6833965Sjdp cmd.add("-Xlog:cds,cds+hashtables"); 6933965Sjdp cmd.add("-XX:+UnlockDiagnosticVMOptions"); 7033965Sjdp if (opts.archiveName == null) 7133965Sjdp opts.archiveName = getDefaultArchiveName(); 7233965Sjdp cmd.add("-XX:SharedArchiveFile=./" + opts.archiveName); 7333965Sjdp 7433965Sjdp for (String s : opts.suffix) cmd.add(s); 7533965Sjdp 7633965Sjdp String[] cmdLine = cmd.toArray(new String[cmd.size()]); 7733965Sjdp ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine); 7833965Sjdp return executeAndLog(pb, "dump"); 7933965Sjdp } 8033965Sjdp 8133965Sjdp 8233965Sjdp // check result of 'dump-the-archive' operation, that is "-Xshare:dump" 8333965Sjdp public static OutputAnalyzer checkDump(OutputAnalyzer output, String... extraMatches) 8433965Sjdp throws Exception { 8533965Sjdp 8633965Sjdp output.shouldContain("Loading classes to share"); 8733965Sjdp output.shouldHaveExitValue(0); 8833965Sjdp 8933965Sjdp for (String match : extraMatches) { 9033965Sjdp output.shouldContain(match); 9133965Sjdp } 9233965Sjdp 9333965Sjdp return output; 9433965Sjdp } 9533965Sjdp 9633965Sjdp 9733965Sjdp // A commonly used convenience methods to create an archive and check the results 9833965Sjdp // Creates an archive and checks for errors 9933965Sjdp public static OutputAnalyzer createArchiveAndCheck(CDSOptions opts) 10033965Sjdp throws Exception { 10133965Sjdp return checkDump(createArchive(opts)); 10233965Sjdp } 10333965Sjdp 10433965Sjdp 10533965Sjdp public static OutputAnalyzer createArchiveAndCheck(String... cliPrefix) 10633965Sjdp throws Exception { 10733965Sjdp return checkDump(createArchive(cliPrefix)); 10833965Sjdp } 10933965Sjdp 11033965Sjdp 11133965Sjdp // This method should be used to check the output of child VM for common exceptions. 11233965Sjdp // Most of CDS tests deal with child VM processes for creating and using the archive. 11333965Sjdp // However exceptions that occur in the child process do not automatically propagate 11433965Sjdp // to the parent process. This mechanism aims to improve the propagation 11533965Sjdp // of exceptions and common errors. 11633965Sjdp // Exception e argument - an exception to be re-thrown if none of the common 11733965Sjdp // exceptions match. Pass null if you wish not to re-throw any exception. 11833965Sjdp public static void checkCommonExecExceptions(OutputAnalyzer output, Exception e) 11933965Sjdp throws Exception { 12033965Sjdp if (output.getStdout().contains("http://bugreport.java.com/bugreport/crash.jsp")) { 12133965Sjdp throw new RuntimeException("Hotspot crashed"); 12233965Sjdp } 12333965Sjdp if (output.getStdout().contains("TEST FAILED")) { 12433965Sjdp throw new RuntimeException("Test Failed"); 12533965Sjdp } 12633965Sjdp if (output.getOutput().contains("shared class paths mismatch")) { 12733965Sjdp throw new RuntimeException("shared class paths mismatch"); 12833965Sjdp } 12933965Sjdp if (output.getOutput().contains("Unable to unmap shared space")) { 13033965Sjdp throw new RuntimeException("Unable to unmap shared space"); 13133965Sjdp } 13233965Sjdp 13333965Sjdp // Special case -- sometimes Xshare:on fails because it failed to map 13433965Sjdp // at given address. This behavior is platform-specific, machine config-specific 13533965Sjdp // and can be random (see ASLR). 13633965Sjdp if (isUnableToMap(output)) { 13733965Sjdp System.out.println(UnableToMapMsg); 13833965Sjdp return; 13933965Sjdp } 14033965Sjdp 14133965Sjdp if (e != null) 14233965Sjdp throw e; 14333965Sjdp } 14433965Sjdp 14533965Sjdp 14633965Sjdp // Check the output for indication that mapping of the archive failed. 14733965Sjdp // Performance note: this check seems to be rather costly - searching the entire 14833965Sjdp // output stream of a child process for multiple strings. However, it is necessary 14933965Sjdp // to detect this condition, a failure to map an archive, since this is not a real 15033965Sjdp // failure of the test or VM operation, and results in a test being "skipped". 15133965Sjdp // Suggestions to improve: 15233965Sjdp // 1. VM can designate a special exit code for such condition. 15333965Sjdp // 2. VM can print a single distinct string indicating failure to map an archive, 15433965Sjdp // instead of utilizing multiple messages. 15533965Sjdp // These are suggestions to improve testibility of the VM. However, implementing them 15633965Sjdp // could also improve usability in the field. 15733965Sjdp public static boolean isUnableToMap(OutputAnalyzer output) { 15833965Sjdp String outStr = output.getOutput(); 15933965Sjdp if ((output.getExitValue() == 1) && ( 16033965Sjdp outStr.contains("Unable to reserve shared space at required address") || 16133965Sjdp outStr.contains("Unable to map ReadOnly shared space at required address") || 16233965Sjdp outStr.contains("Unable to map ReadWrite shared space at required address") || 16333965Sjdp outStr.contains("Unable to map MiscData shared space at required address") || 16433965Sjdp outStr.contains("Unable to map MiscCode shared space at required address") || 16533965Sjdp outStr.contains("Unable to map shared string space at required address") || 16633965Sjdp outStr.contains("Could not allocate metaspace at a compatible address") || 16733965Sjdp outStr.contains("Unable to allocate shared string space: range is not within java heap") )) 16833965Sjdp { 16933965Sjdp return true; 17033965Sjdp } 17133965Sjdp 17233965Sjdp return false; 17333965Sjdp } 17433965Sjdp 17533965Sjdp 17633965Sjdp // Execute JVM with CDS archive, specify command line args suffix 17733965Sjdp public static OutputAnalyzer runWithArchive(String... cliPrefix) 17833965Sjdp throws Exception { 17933965Sjdp 18033965Sjdp return runWithArchive( (new CDSOptions()) 18133965Sjdp .setArchiveName(getDefaultArchiveName()) 18233965Sjdp .addPrefix(cliPrefix) ); 18333965Sjdp } 18433965Sjdp 18533965Sjdp 18633965Sjdp // Execute JVM with CDS archive, specify CDSOptions 18733965Sjdp public static OutputAnalyzer runWithArchive(CDSOptions opts) 18833965Sjdp throws Exception { 18933965Sjdp 19033965Sjdp ArrayList<String> cmd = new ArrayList<String>(); 19133965Sjdp 19233965Sjdp for (String p : opts.prefix) cmd.add(p); 19333965Sjdp 19433965Sjdp cmd.add("-Xshare:" + opts.xShareMode); 19533965Sjdp cmd.add("-XX:+UnlockDiagnosticVMOptions"); 19633965Sjdp cmd.add("-Dtest.timeout.factor=" + TestTimeoutFactor); 19733965Sjdp 19833965Sjdp if (opts.archiveName == null) 19933965Sjdp opts.archiveName = getDefaultArchiveName(); 20033965Sjdp cmd.add("-XX:SharedArchiveFile=" + opts.archiveName); 20133965Sjdp 20233965Sjdp if (opts.useVersion) 20333965Sjdp cmd.add("-version"); 20433965Sjdp 20533965Sjdp for (String s : opts.suffix) cmd.add(s); 20633965Sjdp 20733965Sjdp String[] cmdLine = cmd.toArray(new String[cmd.size()]); 20833965Sjdp ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine); 20933965Sjdp return executeAndLog(pb, "exec"); 21033965Sjdp } 21133965Sjdp 21233965Sjdp 21333965Sjdp // A commonly used convenience methods to create an archive and check the results 21433965Sjdp // Creates an archive and checks for errors 21533965Sjdp public static OutputAnalyzer runWithArchiveAndCheck(CDSOptions opts) throws Exception { 21633965Sjdp return checkExec(runWithArchive(opts)); 21733965Sjdp } 21833965Sjdp 21933965Sjdp 22033965Sjdp public static OutputAnalyzer runWithArchiveAndCheck(String... cliPrefix) throws Exception { 22133965Sjdp return checkExec(runWithArchive(cliPrefix)); 22233965Sjdp } 22333965Sjdp 22433965Sjdp 22533965Sjdp public static OutputAnalyzer checkExec(OutputAnalyzer output, 22633965Sjdp String... extraMatches) throws Exception { 22733965Sjdp CDSOptions opts = new CDSOptions(); 22833965Sjdp return checkExec(output, opts, extraMatches); 22933965Sjdp } 23033965Sjdp 23133965Sjdp 23233965Sjdp // check result of 'exec' operation, that is when JVM is run using the archive 23333965Sjdp public static OutputAnalyzer checkExec(OutputAnalyzer output, CDSOptions opts, 23433965Sjdp String... extraMatches) throws Exception { 23533965Sjdp try { 23633965Sjdp if ("on".equals(opts.xShareMode)) { 23733965Sjdp output.shouldContain("sharing"); 23833965Sjdp } 23933965Sjdp output.shouldHaveExitValue(0); 24033965Sjdp } catch (RuntimeException e) { 24133965Sjdp checkCommonExecExceptions(output, e); 24233965Sjdp return output; 24333965Sjdp } 24433965Sjdp 24533965Sjdp checkExtraMatches(output, extraMatches); 24633965Sjdp return output; 24733965Sjdp } 24833965Sjdp 24933965Sjdp 25033965Sjdp public static OutputAnalyzer checkExecExpectError(OutputAnalyzer output, 25133965Sjdp int expectedExitValue, 25233965Sjdp String... extraMatches) throws Exception { 25333965Sjdp if (isUnableToMap(output)) { 25433965Sjdp System.out.println(UnableToMapMsg); 25533965Sjdp return output; 25633965Sjdp } 25733965Sjdp 25833965Sjdp output.shouldHaveExitValue(expectedExitValue); 25933965Sjdp checkExtraMatches(output, extraMatches); 26033965Sjdp return output; 26133965Sjdp } 26233965Sjdp 26333965Sjdp public static OutputAnalyzer checkExtraMatches(OutputAnalyzer output, 26433965Sjdp String... extraMatches) throws Exception { 26533965Sjdp for (String match : extraMatches) { 26633965Sjdp output.shouldContain(match); 26733965Sjdp } 26833965Sjdp return output; 26933965Sjdp } 27033965Sjdp 27133965Sjdp 27233965Sjdp // get the file object for the test artifact 27333965Sjdp public static File getTestArtifact(String name, boolean checkExistence) { 27433965Sjdp File dir = new File(System.getProperty("test.classes", ".")); 27533965Sjdp File file = new File(dir, name); 27633965Sjdp 27733965Sjdp if (checkExistence && !file.exists()) { 27833965Sjdp throw new RuntimeException("Cannot find " + file.getPath()); 27933965Sjdp } 28033965Sjdp 28133965Sjdp return file; 28233965Sjdp } 28333965Sjdp 28433965Sjdp 28533965Sjdp // create file containing the specified class list 28633965Sjdp public static File makeClassList(String classes[]) 28733965Sjdp throws Exception { 28833965Sjdp return makeClassList(getTestName() + "-", classes); 28933965Sjdp } 29033965Sjdp 29133965Sjdp // create file containing the specified class list 29233965Sjdp public static File makeClassList(String testCaseName, String classes[]) 29333965Sjdp throws Exception { 29433965Sjdp 29533965Sjdp File classList = getTestArtifact(testCaseName + "test.classlist", false); 29633965Sjdp FileOutputStream fos = new FileOutputStream(classList); 29733965Sjdp PrintStream ps = new PrintStream(fos); 29833965Sjdp 29933965Sjdp addToClassList(ps, classes); 30033965Sjdp 30133965Sjdp ps.close(); 30233965Sjdp fos.close(); 30333965Sjdp 30433965Sjdp return classList; 30533965Sjdp } 30633965Sjdp 30733965Sjdp 30833965Sjdp public static void addToClassList(PrintStream ps, String classes[]) 30933965Sjdp throws IOException 31033965Sjdp { 31133965Sjdp if (classes != null) { 31233965Sjdp for (String s : classes) { 31333965Sjdp ps.println(s); 31433965Sjdp } 31533965Sjdp } 31633965Sjdp } 31733965Sjdp 31833965Sjdp 31933965Sjdp // Optimization for getting a test name. 32033965Sjdp // Test name does not change during execution of the test, 32133965Sjdp // but getTestName() uses stack walking hence it is expensive. 32233965Sjdp // Therefore cache it and reuse it. 32333965Sjdp private static String testName; 32433965Sjdp public static String getTestName() { 32533965Sjdp if (testName == null) { 32633965Sjdp testName = Utils.getTestName(); 32733965Sjdp } 32833965Sjdp return testName; 32933965Sjdp } 33033965Sjdp 33133965Sjdp 33233965Sjdp public static String getDefaultArchiveName() { 33333965Sjdp return getTestName() + ".jsa"; 33433965Sjdp } 33533965Sjdp 33633965Sjdp 33733965Sjdp // ===================== FILE ACCESS convenience methods 33833965Sjdp public static File getOutputFile(String name) { 33933965Sjdp File dir = new File(System.getProperty("test.classes", ".")); 34033965Sjdp return new File(dir, getTestName() + "-" + name); 34133965Sjdp } 34233965Sjdp 34333965Sjdp 34433965Sjdp public static File getOutputSourceFile(String name) { 34533965Sjdp File dir = new File(System.getProperty("test.classes", ".")); 34633965Sjdp return new File(dir, name); 34733965Sjdp } 34833965Sjdp 34933965Sjdp 35033965Sjdp public static File getSourceFile(String name) { 35133965Sjdp File dir = new File(System.getProperty("test.src", ".")); 35233965Sjdp return new File(dir, name); 35333965Sjdp } 35433965Sjdp 35533965Sjdp 35633965Sjdp // ============================= Logging 35733965Sjdp public static OutputAnalyzer executeAndLog(ProcessBuilder pb, String logName) throws Exception { 35833965Sjdp long started = System.currentTimeMillis(); 35933965Sjdp OutputAnalyzer output = new OutputAnalyzer(pb.start()); 36033965Sjdp 36133965Sjdp writeFile(getOutputFile(logName + ".stdout"), output.getStdout()); 36233965Sjdp writeFile(getOutputFile(logName + ".stderr"), output.getStderr()); 36333965Sjdp System.out.println("[ELAPSED: " + (System.currentTimeMillis() - started) + " ms]"); 36433965Sjdp System.out.println("[STDERR]\n" + output.getStderr()); 36533965Sjdp 36633965Sjdp if (CopyChildStdoutToMainStdout) 36733965Sjdp System.out.println("[STDOUT]\n" + output.getStdout()); 36833965Sjdp 36933965Sjdp return output; 37033965Sjdp } 37133965Sjdp 37233965Sjdp 37333965Sjdp private static void writeFile(File file, String content) throws Exception { 37433965Sjdp FileOutputStream fos = new FileOutputStream(file); 37533965Sjdp PrintStream ps = new PrintStream(fos); 37633965Sjdp ps.print(content); 37733965Sjdp ps.close(); 37833965Sjdp fos.close(); 37933965Sjdp } 38033965Sjdp} 38133965Sjdp