test/hotspot/jtreg/runtime/appcds/TestCommon.java
changeset 54927 1512d88b24c6
parent 54340 2221f042556d
child 55524 b279ae9843b8
child 55694 7b7df2be6219
equal deleted inserted replaced
54926:d4e7ccaf1445 54927:1512d88b24c6
    30 import jdk.test.lib.cds.CDSTestUtils;
    30 import jdk.test.lib.cds.CDSTestUtils;
    31 import jdk.test.lib.cds.CDSTestUtils.Result;
    31 import jdk.test.lib.cds.CDSTestUtils.Result;
    32 import jdk.test.lib.process.ProcessTools;
    32 import jdk.test.lib.process.ProcessTools;
    33 import jdk.test.lib.process.OutputAnalyzer;
    33 import jdk.test.lib.process.OutputAnalyzer;
    34 import java.io.File;
    34 import java.io.File;
       
    35 import java.io.FileInputStream;
       
    36 import java.io.FileOutputStream;
       
    37 import java.io.InputStream;
       
    38 import java.net.URI;
       
    39 import java.nio.file.DirectoryStream;
       
    40 import java.nio.file.Files;
       
    41 import java.nio.file.FileSystem;
       
    42 import java.nio.file.FileSystems;
       
    43 import java.nio.file.Path;
    35 import java.text.SimpleDateFormat;
    44 import java.text.SimpleDateFormat;
    36 import java.util.Arrays;
    45 import java.util.Arrays;
    37 import java.util.ArrayList;
    46 import java.util.ArrayList;
    38 import java.util.Date;
    47 import java.util.Date;
       
    48 import java.util.Enumeration;
       
    49 import java.util.regex.Matcher;
       
    50 import java.util.regex.Pattern;
       
    51 import java.util.zip.ZipEntry;
       
    52 import java.util.zip.ZipFile;
       
    53 import java.util.zip.ZipOutputStream;
       
    54 import jtreg.SkippedException;
       
    55 import cdsutils.DynamicDumpHelper;
       
    56 
    39 
    57 
    40 /**
    58 /**
    41  * This is a test utility class for common AppCDS test functionality.
    59  * This is a test utility class for common AppCDS test functionality.
    42  *
    60  *
    43  * Various methods use (String ...) for passing VM options. Note that the order
    61  * Various methods use (String ...) for passing VM options. Note that the order
    49  *
    67  *
    50  *    prefix + opts + suffix
    68  *    prefix + opts + suffix
    51  */
    69  */
    52 public class TestCommon extends CDSTestUtils {
    70 public class TestCommon extends CDSTestUtils {
    53     private static final String JSA_FILE_PREFIX = System.getProperty("user.dir") +
    71     private static final String JSA_FILE_PREFIX = System.getProperty("user.dir") +
    54         File.separator + "appcds-";
    72         File.separator;
    55 
    73 
    56     private static final SimpleDateFormat timeStampFormat =
    74     private static final SimpleDateFormat timeStampFormat =
    57         new SimpleDateFormat("HH'h'mm'm'ss's'SSS");
    75         new SimpleDateFormat("HH'h'mm'm'ss's'SSS");
    58 
    76 
    59     private static final String timeoutFactor =
    77     private static final String timeoutFactor =
    62     private static String currentArchiveName;
    80     private static String currentArchiveName;
    63 
    81 
    64     // Call this method to start new archive with new unique name
    82     // Call this method to start new archive with new unique name
    65     public static void startNewArchiveName() {
    83     public static void startNewArchiveName() {
    66         deletePriorArchives();
    84         deletePriorArchives();
    67         currentArchiveName = JSA_FILE_PREFIX +
    85         currentArchiveName = getNewArchiveName();
    68             timeStampFormat.format(new Date()) + ".jsa";
       
    69     }
    86     }
    70 
    87 
    71     // Call this method to get current archive name
    88     // Call this method to get current archive name
    72     public static String getCurrentArchiveName() {
    89     public static String getCurrentArchiveName() {
    73         return currentArchiveName;
    90         return currentArchiveName;
       
    91     }
       
    92 
       
    93     public static String getNewArchiveName() {
       
    94         return getNewArchiveName(null);
       
    95     }
       
    96 
       
    97     public static String getNewArchiveName(String stem) {
       
    98         if (stem == null) {
       
    99             stem = "appcds";
       
   100         }
       
   101         return JSA_FILE_PREFIX + stem + "-" +
       
   102             timeStampFormat.format(new Date()) + ".jsa";
    74     }
   103     }
    75 
   104 
    76     // Attempt to clean old archives to preserve space
   105     // Attempt to clean old archives to preserve space
    77     // Archives are large artifacts (20Mb or more), and much larger than
   106     // Archives are large artifacts (20Mb or more), and much larger than
    78     // most other artifacts created in jtreg testing.
   107     // most other artifacts created in jtreg testing.
    90                     System.out.println("deletePriorArchives(): delete failed for file " + name);
   119                     System.out.println("deletePriorArchives(): delete failed for file " + name);
    91             }
   120             }
    92         }
   121         }
    93     }
   122     }
    94 
   123 
    95 
       
    96     // Create AppCDS archive using most common args - convenience method
   124     // Create AppCDS archive using most common args - convenience method
    97     // Legacy name preserved for compatibility
   125     // Legacy name preserved for compatibility
    98     public static OutputAnalyzer dump(String appJar, String classList[],
   126     public static OutputAnalyzer dump(String appJar, String classList[],
    99                                                String... suffix) throws Exception {
   127                                                String... suffix) throws Exception {
   100         return createArchive(appJar, classList, suffix);
   128         return createArchive(appJar, classList, suffix);
   108         opts.setClassList(classList);
   136         opts.setClassList(classList);
   109         opts.addSuffix(suffix);
   137         opts.addSuffix(suffix);
   110         return createArchive(opts);
   138         return createArchive(opts);
   111     }
   139     }
   112 
   140 
       
   141     // Simulate -Xshare:dump with -XX:ArchiveClassesAtExit. See comments around patchJarForDynamicDump()
       
   142     private static final Class tmp = DynamicDumpHelper.class;
       
   143 
   113     // Create AppCDS archive using appcds options
   144     // Create AppCDS archive using appcds options
   114     public static OutputAnalyzer createArchive(AppCDSOptions opts)
   145     public static OutputAnalyzer createArchive(AppCDSOptions opts)
   115         throws Exception {
   146         throws Exception {
   116 
       
   117         ArrayList<String> cmd = new ArrayList<String>();
   147         ArrayList<String> cmd = new ArrayList<String>();
   118         startNewArchiveName();
   148         startNewArchiveName();
   119 
   149 
   120         for (String p : opts.prefix) cmd.add(p);
   150         for (String p : opts.prefix) cmd.add(p);
   121 
   151 
   122         if (opts.appJar != null) {
   152         if (opts.appJar != null) {
   123             cmd.add("-cp");
   153             cmd.add("-cp");
   124             cmd.add(opts.appJar);
   154             cmd.add(opts.appJar);
       
   155             File jf = new File(opts.appJar);
       
   156             if (DYNAMIC_DUMP && !jf.isDirectory()) {
       
   157                 patchJarForDynamicDump(opts.appJar);
       
   158             }
   125         } else {
   159         } else {
   126             cmd.add("-Djava.class.path=");
   160             cmd.add("-Djava.class.path=");
   127         }
   161         }
   128 
   162 
   129         cmd.add("-Xshare:dump");
   163         if (opts.archiveName == null) {
   130 
       
   131         if (opts.archiveName == null)
       
   132             opts.archiveName = getCurrentArchiveName();
   164             opts.archiveName = getCurrentArchiveName();
   133 
   165         }
   134         cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
   166 
   135 
   167         if (DYNAMIC_DUMP) {
   136         if (opts.classList != null) {
   168             cmd.add("-Xshare:on");
   137             File classListFile = makeClassList(opts.classList);
   169             cmd.add("-XX:ArchiveClassesAtExit=" + opts.archiveName);
   138             cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath());
   170 
   139         }
   171             cmd.add("-Xlog:cds");
   140 
   172             cmd.add("-Xlog:cds+dynamic");
   141         for (String s : opts.suffix) cmd.add(s);
   173             boolean mainModuleSpecified = false;
       
   174             boolean patchModuleSpecified = false;
       
   175             for (String s : opts.suffix) {
       
   176                 if (s.length() == 0) {
       
   177                     continue;
       
   178                 }
       
   179                 if (s.equals("-m")) {
       
   180                     mainModuleSpecified = true;
       
   181                 }
       
   182                 if (s.startsWith("--patch-module=")) {
       
   183                     patchModuleSpecified = true;
       
   184                 }
       
   185                 cmd.add(s);
       
   186             }
       
   187 
       
   188             if (opts.appJar != null) {
       
   189                 // classlist is supported only when we have a Jar file to patch (to insert
       
   190                 // cdsutils.DynamicDumpHelper)
       
   191                 if (opts.classList == null) {
       
   192                     throw new RuntimeException("test.dynamic.dump requires classList file");
       
   193                 }
       
   194 
       
   195                 if (!mainModuleSpecified && !patchModuleSpecified) {
       
   196                     cmd.add("cdsutils.DynamicDumpHelper");
       
   197                     File classListFile = makeClassList(opts.classList);
       
   198                     cmd.add(classListFile.getPath());
       
   199                 }
       
   200             } else {
       
   201                 if (!mainModuleSpecified && !patchModuleSpecified) {
       
   202                     // If you have an empty classpath, you cannot specify a classlist!
       
   203                     if (opts.classList != null && opts.classList.length > 0) {
       
   204                         throw new RuntimeException("test.dynamic.dump not supported empty classpath with non-empty classlist");
       
   205                     }
       
   206                     cmd.add("-version");
       
   207                 }
       
   208             }
       
   209         } else {
       
   210             // static dump
       
   211             cmd.add("-Xshare:dump");
       
   212             cmd.add("-XX:SharedArchiveFile=" + opts.archiveName);
       
   213 
       
   214             if (opts.classList != null) {
       
   215                 File classListFile = makeClassList(opts.classList);
       
   216                 cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath());
       
   217             }
       
   218             for (String s : opts.suffix) {
       
   219                 cmd.add(s);
       
   220             }
       
   221         }
   142 
   222 
   143         String[] cmdLine = cmd.toArray(new String[cmd.size()]);
   223         String[] cmdLine = cmd.toArray(new String[cmd.size()]);
   144         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
   224         ProcessBuilder pb = ProcessTools.createJavaProcessBuilder(true, cmdLine);
   145         return executeAndLog(pb, "dump");
   225         return executeAndLog(pb, "dump");
   146     }
   226     }
   152     //    -Dtest.cds.run.with.jfr=true
   232     //    -Dtest.cds.run.with.jfr=true
   153     //
   233     //
   154     // Some AppCDS tests are not compatible with this mode. See the group
   234     // Some AppCDS tests are not compatible with this mode. See the group
   155     // hotspot_appcds_with_jfr in ../../TEST.ROOT for details.
   235     // hotspot_appcds_with_jfr in ../../TEST.ROOT for details.
   156     private static final boolean RUN_WITH_JFR = Boolean.getBoolean("test.cds.run.with.jfr");
   236     private static final boolean RUN_WITH_JFR = Boolean.getBoolean("test.cds.run.with.jfr");
       
   237     // This method simulates -Xshare:dump with -XX:ArchiveClassesAtExit. This way, we
       
   238     // can re-use many tests (outside of the ./dynamicArchive directory) for testing
       
   239     // general features of JDK-8215311 (JEP 350: Dynamic CDS Archives).
       
   240     //
       
   241     // We insert the cdsutils/DynamicDumpHelper.class into the first Jar file in
       
   242     // the classpath. We use this class to load all the classes specified in the classlist.
       
   243     //
       
   244     // There's no need to change the run-time command-line: in this special mode, two
       
   245     // archives are involved. The command-line specifies only the top archive. However,
       
   246     // the location of the base archive is recorded in the top archive, so it can be
       
   247     // determined by the JVM at runtime start-up.
       
   248     //
       
   249     // To run in this special mode, specify the following in your jtreg command-line
       
   250     //    -Dtest.dynamic.cds.archive=true
       
   251     //
       
   252     // Note that some tests are not compatible with this special mode, including
       
   253     //    + Tests in ./dynamicArchive: these tests are specifically written for
       
   254     //      dynamic archive, and do not use TestCommon.createArchive(), which works
       
   255     //      together with patchJarForDynamicDump().
       
   256     //    + Tests related to cached objects and shared strings: dynamic dumping
       
   257     //      does not support these.
       
   258     //    + Custom loader tests: DynamicDumpHelper doesn't support the required
       
   259     //      classlist syntax. (FIXME).
       
   260     //    + Extra symbols and extra strings.
       
   261     // See the hotspot_appcds_dynamic in ../../TEST.ROOT for details.
       
   262     //
       
   263     // To run all tests that are compatible with this mode:
       
   264     //    cd test/hotspot/jtreg
       
   265     //    jtreg -Dtest.dynamic.cds.archive=true :hotspot_appcds_dynamic
       
   266     //
       
   267     private static void patchJarForDynamicDump(String cp) throws Exception {
       
   268         System.out.println("patchJarForDynamicDump: classpath = " + cp);
       
   269         String firstJar = cp;
       
   270         int n = firstJar.indexOf(File.pathSeparator);
       
   271         if (n > 0) {
       
   272             firstJar = firstJar.substring(0, n);
       
   273         }
       
   274         String classDir = System.getProperty("test.classes");
       
   275         String expected1 = classDir + File.separator;
       
   276         String expected2 = System.getProperty("user.dir") + File.separator;
       
   277 
       
   278         if (!firstJar.startsWith(expected1) && !firstJar.startsWith(expected2)) {
       
   279             throw new RuntimeException("FIXME: jar file not at a supported location ('"
       
   280                                        + expected1 + "', or '" + expected2 + "'): " + firstJar);
       
   281         }
       
   282 
       
   283         String replaceJar = firstJar + ".tmp";
       
   284         String patchClass = "cdsutils/DynamicDumpHelper.class";
       
   285         ZipFile zipFile = new ZipFile(firstJar);
       
   286         byte[] buf = new byte[1024];
       
   287         int len;
       
   288         if (zipFile.getEntry(patchClass) == null) {
       
   289             FileOutputStream fout = new FileOutputStream(replaceJar);
       
   290             final ZipOutputStream zos = new ZipOutputStream(fout);
       
   291 
       
   292             zos.putNextEntry(new ZipEntry(patchClass));
       
   293             InputStream is = new FileInputStream(classDir + File.separator + patchClass);
       
   294             while ((len = (is.read(buf))) > 0) {
       
   295                 zos.write(buf, 0, len);
       
   296             }
       
   297             zos.closeEntry();
       
   298             is.close();
       
   299 
       
   300             for (Enumeration e = zipFile.entries(); e.hasMoreElements(); ) {
       
   301                 ZipEntry entryIn = (ZipEntry) e.nextElement();
       
   302                 zos.putNextEntry(entryIn);
       
   303                 is = zipFile.getInputStream(entryIn);
       
   304                 while ((len = is.read(buf)) > 0) {
       
   305                     zos.write(buf, 0, len);
       
   306                 }
       
   307                 zos.closeEntry();
       
   308                 is.close();
       
   309             }
       
   310 
       
   311             zos.close();
       
   312             fout.close();
       
   313             zipFile.close();
       
   314 
       
   315             File oldFile = new File(firstJar);
       
   316             File newFile = new File(replaceJar);
       
   317             oldFile.delete();
       
   318             newFile.renameTo(oldFile);
       
   319             System.out.println("firstJar = " + firstJar + " Modified");
       
   320         } else {
       
   321             System.out.println("firstJar = " + firstJar);
       
   322         }
       
   323     }
   157 
   324 
   158     // Execute JVM using AppCDS archive with specified AppCDSOptions
   325     // Execute JVM using AppCDS archive with specified AppCDSOptions
   159     public static OutputAnalyzer runWithArchive(AppCDSOptions opts)
   326     public static OutputAnalyzer runWithArchive(AppCDSOptions opts)
   160         throws Exception {
   327         throws Exception {
   161 
   328 
   258         AppCDSOptions opts = makeModuleOptions(prefix, upgrademodulepath, modulepath,
   425         AppCDSOptions opts = makeModuleOptions(prefix, upgrademodulepath, modulepath,
   259                                                mid, testClassArgs);
   426                                                mid, testClassArgs);
   260         return runWithArchive(opts);
   427         return runWithArchive(opts);
   261     }
   428     }
   262 
   429 
   263 
       
   264     // A common operation: dump, then check results
   430     // A common operation: dump, then check results
   265     public static OutputAnalyzer testDump(String appJar, String classList[],
   431     public static OutputAnalyzer testDump(String appJar, String classList[],
   266                                           String... suffix) throws Exception {
   432                                           String... suffix) throws Exception {
   267         OutputAnalyzer output = dump(appJar, classList, suffix);
   433         OutputAnalyzer output = dump(appJar, classList, suffix);
   268         output.shouldContain("Loading classes to share");
   434         if (DYNAMIC_DUMP) {
       
   435             if (isUnableToMap(output)) {
       
   436                 throw new SkippedException(UnableToMapMsg);
       
   437             }
       
   438             output.shouldContain("Written dynamic archive");
       
   439         } else {
       
   440             output.shouldContain("Loading classes to share");
       
   441         }
   269         output.shouldHaveExitValue(0);
   442         output.shouldHaveExitValue(0);
   270         return output;
   443         return output;
   271     }
   444     }
   272 
   445 
   273 
   446 
   299         }
   472         }
   300 
   473 
   301         return output;
   474         return output;
   302     }
   475     }
   303 
   476 
   304 
       
   305     // Convenience concatenation utils
   477     // Convenience concatenation utils
   306     public static String[] list(String ...args) {
   478     public static String[] list(String ...args) {
   307         return args;
   479         return args;
   308     }
   480     }
   309 
   481 
   334         }
   506         }
   335 
   507 
   336         return list.toArray(new String[list.size()]);
   508         return list.toArray(new String[list.size()]);
   337     }
   509     }
   338 
   510 
       
   511     public static String[] concat(String prefix, String[] extra) {
       
   512         ArrayList<String> list = new ArrayList<String>();
       
   513         list.add(prefix);
       
   514         for (String s : extra) {
       
   515             list.add(s);
       
   516         }
       
   517 
       
   518         return list.toArray(new String[list.size()]);
       
   519     }
   339 
   520 
   340     // ===================== Concatenate paths
   521     // ===================== Concatenate paths
   341     public static String concatPaths(String... paths) {
   522     public static String concatPaths(String... paths) {
   342         String prefix = "";
   523         String prefix = "";
   343         String s = "";
   524         String s = "";
   382             }
   563             }
   383             i ++;
   564             i ++;
   384         }
   565         }
   385         return true;
   566         return true;
   386     }
   567     }
       
   568 
       
   569     static Pattern pattern;
       
   570 
       
   571     static void findAllClasses(ArrayList<String> list) throws Throwable {
       
   572         // Find all the classes in the jrt file system
       
   573         pattern = Pattern.compile("/modules/[a-z.]*[a-z]+/([^-]*)[.]class");
       
   574         FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/"));
       
   575         Path base = fs.getPath("/modules/");
       
   576         findAllClassesAtPath(base, list);
       
   577     }
       
   578 
       
   579     private static void findAllClassesAtPath(Path p, ArrayList<String> list) throws Throwable {
       
   580         try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) {
       
   581             for (Path entry: stream) {
       
   582                 Matcher matcher = pattern.matcher(entry.toString());
       
   583                 if (matcher.find()) {
       
   584                     String className = matcher.group(1);
       
   585                     list.add(className);
       
   586                 }
       
   587                 try {
       
   588                     findAllClassesAtPath(entry, list);
       
   589                 } catch (Throwable t) {}
       
   590             }
       
   591         }
       
   592     }
   387 }
   593 }