< prev index next >

test/lib/jdk/test/lib/cds/CDSAppTester.java

Print this page
@@ -26,47 +26,83 @@
  import java.io.File;
  import jdk.test.lib.cds.CDSTestUtils;
  import jdk.test.lib.process.ProcessTools;
  import jdk.test.lib.process.OutputAnalyzer;
  import jdk.test.lib.StringArrayUtils;
+ import jtreg.SkippedException;
  
  /*
   * This is a base class used for testing CDS functionalities with complex applications.
   * You can define the application by overridding the vmArgs(), classpath() and appCommandLine()
   * methods. Application-specific validation checks can be implemented with checkExecution().
+  *
+  * Note: to debug the new workflow, run jtreg with -vmoption:-DCDSAppTester.split.new.workflow=true
+  * This will run the new workflow in two separate processes that you can rerun easily inside a debugger.
+  * Also, the log files are easier to read.
  */
  abstract public class CDSAppTester {
      private final String name;
      private final String classListFile;
      private final String classListFileLog;
      private final String staticArchiveFile;
      private final String staticArchiveFileLog;
      private final String dynamicArchiveFile;
      private final String dynamicArchiveFileLog;
-     private final String productionRunLog;
+     private final String codeCacheFile;  // old workflow
+     private final String codeCacheFileLog;
+     private final String cdsFile;        // new workflow: -XX:CacheDataStore=<foo>.cds
+     private final String cdsFileLog;
+     private final String cdsFilePreImage;        // new workflow: -XX:CacheDataStore=<foo>.cds
+     private final String cdsFilePreImageLog;
+     private final String aotFile;        // new workflow = cdsFile + ".code"
+     private int numProductionRuns = 0;
  
      public CDSAppTester(String name) {
+         if (CDSTestUtils.DYNAMIC_DUMP) {
+             throw new jtreg.SkippedException("Tests based on CDSAppTester should be excluded when -Dtest.dynamic.cds.archive is specified");
+         }
+ 
          // Old workflow
          this.name = name;
          classListFile = name() + ".classlist";
          classListFileLog = classListFile + ".log";
          staticArchiveFile = name() + ".static.jsa";
          staticArchiveFileLog = staticArchiveFile + ".log";
          dynamicArchiveFile = name() + ".dynamic.jsa";
          dynamicArchiveFileLog = dynamicArchiveFile + ".log";
-         productionRunLog = name() + ".production.log";
+         codeCacheFile = name() + ".code.jsa";
+         codeCacheFileLog = codeCacheFile + ".log";
+         cdsFile = name() + ".cds";
+         cdsFileLog = cdsFile + ".log";
+         cdsFilePreImage = cdsFile + ".preimage";
+         cdsFilePreImageLog = cdsFilePreImage + ".log";
+         aotFile = cdsFile + ".code";
+     }
+ 
+     private String productionRunLog() {
+         if (numProductionRuns == 0) {
+             return name() + ".production.log";
+         } else {
+             return name() + ".production." + numProductionRuns + ".log";
+         }
      }
  
      private enum Workflow {
          STATIC,        // classic -Xshare:dump workflow
          DYNAMIC,       // classic -XX:ArchiveClassesAtExit
+         LEYDEN_OLD,    // The old "5 step workflow", to be phased out
+         LEYDEN,        // The new "one step training workflow" -- see JDK-8320264
      }
  
      public enum RunMode {
          CLASSLIST,
          DUMP_STATIC,
          DUMP_DYNAMIC,
+         DUMP_CODECACHE,    // LEYDEN_OLD only
+         TRAINING,          // LEYDEN only
+         TRAINING0,         // LEYDEN only
+         TRAINING1,         // LEYDEN only
          PRODUCTION;
  
          public boolean isStaticDump() {
              return this == DUMP_STATIC;
          }

@@ -95,19 +131,32 @@
  
      // optional
      public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {}
  
      private Workflow workflow;
+     private boolean checkExitValue = true;
+ 
+     public final void setCheckExitValue(boolean b) {
+         checkExitValue = b;
+     }
  
      public final boolean isStaticWorkflow() {
          return workflow == Workflow.STATIC;
      }
  
      public final boolean isDynamicWorkflow() {
          return workflow == Workflow.DYNAMIC;
      }
  
+     public final boolean isLeydenOldWorkflow() {
+         return workflow == Workflow.LEYDEN_OLD;
+     }
+ 
+     public final boolean isLeydenWorkflow() {
+         return workflow == Workflow.LEYDEN;
+     }
+ 
      private String logToFile(String logFile, String... logTags) {
          StringBuilder sb = new StringBuilder("-Xlog:");
          String prefix = "";
          for (String tag : logTags) {
              sb.append(prefix);

@@ -132,11 +181,13 @@
          Process process = pb.start();
          OutputAnalyzer output = CDSTestUtils.executeAndLog(process, runMode.toString());
          for (String logFile : logFiles) {
              listOutputFile(logFile);
          }
-         output.shouldHaveExitValue(0);
+         if (checkExitValue) {
+             output.shouldHaveExitValue(0);
+         }
          CDSTestUtils.checkCommonExecExceptions(output);
          checkExecution(output, runMode);
          return output;
      }
  

@@ -164,10 +215,17 @@
                                                     logToFile(staticArchiveFileLog,
                                                               "cds=debug",
                                                               "cds+class=debug",
                                                               "cds+heap=warning",
                                                               "cds+resolve=debug"));
+         if (isLeydenOldWorkflow()) {
+             cmdLine = StringArrayUtils.concat(cmdLine,
+                                               "-XX:+AOTClassLinking",
+                                               "-XX:+ArchiveDynamicProxies",
+                                               "-XX:+ArchiveReflectionData");
+         }
+ 
          return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog);
      }
  
      private OutputAnalyzer dumpDynamicArchive() throws Exception {
          RunMode runMode = RunMode.DUMP_DYNAMIC;

@@ -181,29 +239,161 @@
                                              logToFile(dynamicArchiveFileLog,
                                                        "cds=debug",
                                                        "cds+class=debug",
                                                        "cds+resolve=debug",
                                                        "class+load=debug"));
+         } else {
+           // Leyden "OLD" workflow step 3
+           cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                             "-Xlog:cds",
+                                             "-XX:ArchiveClassesAtExit=" + dynamicArchiveFile,
+                                             "-XX:SharedArchiveFile=" + staticArchiveFile,
+                                             "-XX:+RecordTraining",
+                                             "-cp", classpath(runMode),
+                                             logToFile(dynamicArchiveFileLog,
+                                                       "cds=debug",
+                                                       "cds+class=debug",
+                                                       "cds+resolve=debug",
+                                                       "class+load=debug"));
          }
          cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
          return executeAndCheck(cmdLine, runMode, dynamicArchiveFile, dynamicArchiveFileLog);
      }
  
+ 
+     private OutputAnalyzer dumpCodeCache() throws Exception {
+         RunMode runMode = RunMode.DUMP_CODECACHE;
+         String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                    "-XX:SharedArchiveFile=" + dynamicArchiveFile,
+                                                    "-XX:+ReplayTraining",
+                                                    "-XX:+StoreCachedCode",
+                                                    "-XX:CachedCodeFile=" + codeCacheFile,
+                                                    "-XX:CachedCodeMaxSize=512M",
+                                                    "-cp", classpath(runMode),
+                                                    logToFile(codeCacheFileLog, "scc"));
+ 
+         cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+         return executeAndCheck(cmdLine, runMode, codeCacheFile, codeCacheFileLog);
+     }
+ 
+     private OutputAnalyzer oldProductionRun() throws Exception {
+         RunMode runMode = RunMode.PRODUCTION;
+         String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                    "-XX:+UnlockDiagnosticVMOptions",
+                                                    "-XX:VerifyArchivedFields=2", // make sure archived heap objects are good.
+                                                    "-XX:SharedArchiveFile=" + dynamicArchiveFile,
+                                                    "-XX:+ReplayTraining",
+                                                    "-XX:+LoadCachedCode",
+                                                    "-XX:CachedCodeFile=" + codeCacheFile,
+                                                    "-cp", classpath(runMode),
+                                                    logToFile(productionRunLog(), "cds", "scc*=warning"));
+         cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+         return executeAndCheck(cmdLine, runMode, productionRunLog());
+     }
+ 
+     private String trainingLog(String file) {
+         return logToFile(file,
+                          "cds=debug",
+                          "cds+class=debug",
+                          "cds+heap=warning",
+                          "cds+resolve=debug");
+     }
+ 
+     // normal training workflow (main JVM process spawns child process)
+     private OutputAnalyzer trainingRun() throws Exception {
+         RunMode runMode = RunMode.TRAINING;
+         File f = new File(cdsFile);
+         f.delete();
+         String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                    "-XX:+AOTClassLinking",
+                                                    "-XX:+ArchiveDynamicProxies",
+                                                  //"-XX:+ArchiveReflectionData",
+                                                    "-XX:CacheDataStore=" + cdsFile,
+                                                    "-cp", classpath(runMode),
+                                                    // Use PID to distinguish the logs of the training process
+                                                    // and the forked final image dump process.
+                                                    "-Xlog:cds::uptime,level,tags,pid",
+                                                    trainingLog(cdsFileLog));
+         cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+         OutputAnalyzer out =  executeAndCheck(cmdLine, runMode, cdsFile, cdsFileLog);
+         listOutputFile(cdsFile + ".log.0"); // the preimage dump
+         return out;
+     }
+ 
+     // "split" training workflow (launch the two processes manually, for easier debugging);
+     private OutputAnalyzer trainingRun0() throws Exception {
+         RunMode runMode = RunMode.TRAINING0;
+         File f = new File(cdsFile);
+         f.delete();
+         String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                    "-XX:+UnlockDiagnosticVMOptions",
+                                                    "-XX:+CDSManualFinalImage",
+                                                    "-XX:+AOTClassLinking",
+                                                    "-XX:+ArchiveDynamicProxies",
+                                                  //"-XX:+ArchiveReflectionData",
+                                                    "-XX:CacheDataStore=" + cdsFile,
+                                                    "-cp", classpath(runMode),
+                                                    trainingLog(cdsFilePreImageLog));
+         cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+         return executeAndCheck(cmdLine, runMode, cdsFilePreImage, cdsFilePreImageLog);
+     }
+     private OutputAnalyzer trainingRun1() throws Exception {
+         RunMode runMode = RunMode.TRAINING1;
+         File f = new File(cdsFile);
+         f.delete();
+         String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                    "-XX:+UnlockDiagnosticVMOptions",
+                                                    "-XX:+AOTClassLinking",
+                                                    "-XX:+ArchiveDynamicProxies",
+                                                  //"-XX:+ArchiveReflectionData",
+                                                    "-XX:CacheDataStore=" + cdsFile,
+                                                    "-XX:CDSPreimage=" + cdsFilePreImage,
+                                                    "-cp", classpath(runMode),
+                                                    trainingLog(cdsFileLog));
+         cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
+         return executeAndCheck(cmdLine, runMode, cdsFile, aotFile, cdsFileLog);
+     }
+ 
      private OutputAnalyzer productionRun() throws Exception {
+         return productionRun(null, null);
+     }
+ 
+     public OutputAnalyzer productionRun(String[] extraVmArgs) throws Exception {
+         return productionRun(extraVmArgs, null);
+     }
+ 
+     // After calling run(String[]), you can call this method to run the app again, with the AOTCache
+     // using different args to the VM and application.
+     public OutputAnalyzer productionRun(String[] extraVmArgs, String[] extraAppArgs) throws Exception {
          RunMode runMode = RunMode.PRODUCTION;
          String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode),
+                                                    "-XX:+UnlockDiagnosticVMOptions",
+                                                    "-XX:VerifyArchivedFields=2", // make sure archived heap objects are good.
                                                     "-cp", classpath(runMode),
-                                                    logToFile(productionRunLog, "cds"));
+                                                    logToFile(productionRunLog(), "cds"));
  
          if (isStaticWorkflow()) {
              cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + staticArchiveFile);
          } else if (isDynamicWorkflow()) {
              cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + dynamicArchiveFile);
+         } else {
+             cmdLine = StringArrayUtils.concat(cmdLine, "-XX:CacheDataStore=" + cdsFile);
+         }
+ 
+         if (extraVmArgs != null) {
+             cmdLine = StringArrayUtils.concat(cmdLine, extraVmArgs);
          }
  
          cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode));
-         return executeAndCheck(cmdLine, runMode, productionRunLog);
+ 
+         if (extraAppArgs != null) {
+             cmdLine = StringArrayUtils.concat(cmdLine, extraAppArgs);
+         }
+ 
+         OutputAnalyzer out = executeAndCheck(cmdLine, runMode, productionRunLog());
+         numProductionRuns ++;
+         return out;
      }
  
      public void run(String args[]) throws Exception {
          String err = "Must have exactly one command line argument of the following: ";
          String prefix = "";

@@ -217,10 +407,16 @@
          } else {
              if (args[0].equals("STATIC")) {
                  runStaticWorkflow();
              } else if (args[0].equals("DYNAMIC")) {
                  runDynamicWorkflow();
+             } else if (args[0].equals("LEYDEN_OLD")) {
+                 runLeydenOldWorkflow();
+             } else if (args[0].equals("LEYDEN")) {
+                 runLeydenWorkflow(false);
+             } else if (args[0].equals("LEYDEN_TRAINONLY")) {
+                 runLeydenWorkflow(true);
              } else {
                  throw new RuntimeException(err);
              }
          }
      }

@@ -235,6 +431,28 @@
      private void runDynamicWorkflow() throws Exception {
          this.workflow = Workflow.DYNAMIC;
          dumpDynamicArchive();
          productionRun();
      }
+ 
+     private void runLeydenOldWorkflow() throws Exception {
+         this.workflow = Workflow.LEYDEN_OLD;
+         createClassList();
+         dumpStaticArchive();
+         dumpDynamicArchive();
+         dumpCodeCache();
+         oldProductionRun();
+     }
+ 
+     private void runLeydenWorkflow(boolean trainOnly) throws Exception {
+         this.workflow = Workflow.LEYDEN;
+         if (System.getProperty("CDSAppTester.split.new.workflow") != null) {
+             trainingRun0();
+             trainingRun1();
+         } else {
+             trainingRun();
+         }
+         if (!trainOnly) {
+             productionRun();
+         }
+     }
  }
< prev index next >