1 /* 2 * Copyright (c) 2023, 2025, 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 package jdk.test.lib.cds; 25 26 import java.io.File; 27 import jdk.test.lib.cds.CDSTestUtils; 28 import jdk.test.lib.process.ProcessTools; 29 import jdk.test.lib.process.OutputAnalyzer; 30 import jdk.test.lib.StringArrayUtils; 31 import jdk.test.whitebox.WhiteBox; 32 import jtreg.SkippedException; 33 34 /* 35 * This is a base class used for testing CDS functionalities with complex applications. 36 * You can define the application by overridding the vmArgs(), classpath() and appCommandLine() 37 * methods. Application-specific validation checks can be implemented with checkExecution(). 38 * 39 * The AOT workflow runs with one-step training by default. For debugging purposes, run 40 * jtreg with -vmoption:-DCDSAppTester.two.step.training=true. This will run -XX:AOTMode=record 41 * and -XX:AOTMode=record in two separate processes that you can rerun easily inside a debugger. 42 * Also, the log files are easier to read. 43 */ 44 abstract public class CDSAppTester { 45 private final String name; 46 private final String classListFile; 47 private final String classListFileLog; 48 private final String aotConfigurationFile; 49 private final String aotConfigurationFileLog; 50 private final String staticArchiveFile; 51 private final String staticArchiveFileLog; 52 private final String aotCacheFile; 53 private final String aotCacheFileLog; 54 private final String dynamicArchiveFile; 55 private final String dynamicArchiveFileLog; 56 private final String cdsFile; // new workflow: -XX:CacheDataStore=<foo>.cds 57 private final String cdsFileLog; 58 private final String cdsFilePreImage; // new workflow: -XX:CacheDataStore=<foo>.cds 59 private final String cdsFilePreImageLog; 60 private final String tempBaseArchiveFile; 61 private int numProductionRuns = 0; 62 private String whiteBoxJar = null; 63 private boolean inOneStepTraining = false; 64 65 /** 66 * All files created in the CDS/AOT workflow will be name + extension. E.g. 67 * - name.aot 68 * - name.aotconfig 69 * - name.classlist 70 * - name.jsa 71 */ 72 public CDSAppTester(String name) { 73 if (CDSTestUtils.DYNAMIC_DUMP) { 74 throw new SkippedException("Tests based on CDSAppTester should be excluded when -Dtest.dynamic.cds.archive is specified"); 75 } 76 77 this.name = name; 78 classListFile = name() + ".classlist"; 79 classListFileLog = logFileName(classListFile); 80 aotConfigurationFile = name() + ".aotconfig"; 81 aotConfigurationFileLog = logFileName(aotConfigurationFile); 82 staticArchiveFile = name() + ".static.jsa"; 83 staticArchiveFileLog = logFileName(staticArchiveFile); 84 aotCacheFile = name() + ".aot"; 85 aotCacheFileLog = logFileName(aotCacheFile);; 86 dynamicArchiveFile = name() + ".dynamic.jsa"; 87 dynamicArchiveFileLog = logFileName(dynamicArchiveFile); 88 cdsFile = name() + ".cds"; 89 cdsFileLog = logFileName(cdsFile); 90 cdsFilePreImage = cdsFile + ".preimage"; 91 cdsFilePreImageLog = logFileName(cdsFilePreImage); 92 tempBaseArchiveFile = name() + ".temp-base.jsa"; 93 } 94 95 private String productionRunLog() { 96 if (numProductionRuns == 0) { 97 return logFileName(name() + ".production"); 98 } else { 99 return logFileName(name() + ".production." + numProductionRuns); 100 } 101 } 102 103 private static String logFileName(String file) { 104 file = file.replace("\"", "%22"); 105 file = file.replace("'", "%27"); 106 return file + ".log"; 107 } 108 109 private enum Workflow { 110 STATIC, // classic -Xshare:dump workflow 111 DYNAMIC, // classic -XX:ArchiveClassesAtExit 112 AOT, // JEP 483 Ahead-of-Time Class Loading & Linking 113 LEYDEN, // The new "one step training workflow" -- see JDK-8320264 114 } 115 116 public enum RunMode { 117 TRAINING, // -XX:DumpLoadedClassList OR {-XX:AOTMode=record -XX:AOTConfiguration} 118 TRAINING0, // LEYDEN only 119 TRAINING1, // LEYDEN only (assembly phase, app logic not executed) 120 DUMP_STATIC, // -Xshare:dump 121 DUMP_DYNAMIC, // -XX:ArchiveClassesArExit 122 ASSEMBLY, // JEP 483 (assembly phase, app logic not executed) 123 PRODUCTION; // Running with the CDS archive produced from the above steps 124 125 public boolean isStaticDump() { 126 return this == DUMP_STATIC; 127 } 128 public boolean isProductionRun() { 129 return this == PRODUCTION; 130 } 131 132 // When <code>CDSAppTester::checkExecution(out, runMode)</code> is called, has the application been 133 // executed? If so, <code>out</code> should contain logs printed by the application's own logic. 134 public boolean isApplicationExecuted() { 135 return (this != TRAINING1) && (this != ASSEMBLY) && (this != DUMP_STATIC); 136 } 137 } 138 139 public boolean isDumping(RunMode runMode) { 140 if (isStaticWorkflow()) { 141 return runMode == RunMode.DUMP_STATIC; 142 } else if (isDynamicWorkflow()) { 143 return runMode == RunMode.DUMP_DYNAMIC; 144 } else if (isAOTWorkflow()) { 145 return runMode == RunMode.TRAINING || runMode == RunMode.ASSEMBLY; 146 } else { 147 return runMode == RunMode.TRAINING || runMode == RunMode.TRAINING0 || runMode == RunMode.TRAINING1; 148 } 149 } 150 151 public final String name() { 152 return this.name; 153 } 154 155 // optional 156 public String[] vmArgs(RunMode runMode) { 157 return new String[0]; 158 } 159 160 // optional 161 public String classpath(RunMode runMode) { 162 return null; 163 } 164 165 // optional 166 public String modulepath(RunMode runMode) { 167 return null; 168 } 169 170 // must override 171 // main class, followed by arguments to the main class 172 abstract public String[] appCommandLine(RunMode runMode); 173 174 // optional 175 public void checkExecution(OutputAnalyzer out, RunMode runMode) throws Exception {} 176 177 private Workflow workflow; 178 private boolean checkExitValue = true; 179 180 public final void setCheckExitValue(boolean b) { 181 checkExitValue = b; 182 } 183 184 public final void useWhiteBox(String whiteBoxJar) { 185 this.whiteBoxJar = whiteBoxJar; 186 } 187 188 public final boolean isStaticWorkflow() { 189 return workflow == Workflow.STATIC; 190 } 191 192 public final boolean isDynamicWorkflow() { 193 return workflow == Workflow.DYNAMIC; 194 } 195 196 public final boolean isAOTWorkflow() { 197 return workflow == Workflow.AOT; 198 } 199 200 public final boolean isLeydenWorkflow() { 201 return workflow == Workflow.LEYDEN; 202 } 203 204 private String logToFile(String logFile, String... logTags) { 205 StringBuilder sb = new StringBuilder("-Xlog:arguments"); 206 String prefix = ","; 207 for (String tag : logTags) { 208 sb.append(prefix); 209 sb.append(tag); 210 } 211 sb.append(":file=" + logFile + "::filesize=0"); 212 return sb.toString(); 213 } 214 215 private void listOutputFile(String file) { 216 File f = new File(file); 217 if (f.exists()) { 218 System.out.println("[output file: " + file + " " + f.length() + " bytes]"); 219 } else { 220 System.out.println("[output file: " + file + " does not exist]"); 221 } 222 } 223 224 private OutputAnalyzer executeAndCheck(String[] cmdLine, RunMode runMode, String... logFiles) throws Exception { 225 ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmdLine); 226 Process process = pb.start(); 227 OutputAnalyzer output = CDSTestUtils.executeAndLog(process, runMode.toString()); 228 for (String logFile : logFiles) { 229 listOutputFile(logFile); 230 } 231 if (checkExitValue) { 232 output.shouldHaveExitValue(0); 233 } 234 //output.shouldNotContain(CDSTestUtils.MSG_STATIC_FIELD_MAY_HOLD_DIFFERENT_VALUE); // FIXME -- leyden+JEP483 merge 235 CDSTestUtils.checkCommonExecExceptions(output); 236 checkExecution(output, runMode); 237 return output; 238 } 239 240 private String[] addCommonVMArgs(RunMode runMode, String[] cmdLine) { 241 cmdLine = addClassOrModulePath(runMode, cmdLine); 242 cmdLine = addWhiteBox(cmdLine); 243 return cmdLine; 244 } 245 246 private String[] addClassOrModulePath(RunMode runMode, String[] cmdLine) { 247 String cp = classpath(runMode); 248 if (cp == null) { 249 // Override the "-cp ...." added by Jtreg 250 cmdLine = StringArrayUtils.concat(cmdLine, "-Djava.class.path="); 251 } else { 252 cmdLine = StringArrayUtils.concat(cmdLine, "-cp", cp); 253 } 254 String mp = modulepath(runMode); 255 if (mp != null) { 256 cmdLine = StringArrayUtils.concat(cmdLine, "--module-path", mp); 257 } 258 return cmdLine; 259 } 260 261 private String[] addWhiteBox(String[] cmdLine) { 262 if (whiteBoxJar != null) { 263 cmdLine = StringArrayUtils.concat(cmdLine, 264 "-XX:+UnlockDiagnosticVMOptions", 265 "-XX:+WhiteBoxAPI", 266 "-Xbootclasspath/a:" + whiteBoxJar); 267 } 268 return cmdLine; 269 } 270 271 private OutputAnalyzer recordAOTConfiguration() throws Exception { 272 RunMode runMode = RunMode.TRAINING; 273 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 274 "-XX:AOTMode=record", 275 "-XX:AOTConfiguration=" + aotConfigurationFile, 276 logToFile(aotConfigurationFileLog, 277 "class+load=debug", 278 "aot=debug", 279 "aot+class=debug")); 280 cmdLine = addCommonVMArgs(runMode, cmdLine); 281 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 282 return executeAndCheck(cmdLine, runMode, aotConfigurationFile, aotConfigurationFileLog); 283 } 284 285 private OutputAnalyzer createAOTCacheOneStep() throws Exception { 286 RunMode runMode = RunMode.TRAINING; 287 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 288 "-XX:AOTMode=record", 289 "-XX:AOTCacheOutput=" + aotCacheFile, 290 logToFile(aotCacheFileLog, 291 "class+load=debug", 292 "aot=debug", 293 "aot+class=debug")); 294 cmdLine = addCommonVMArgs(runMode, cmdLine); 295 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 296 OutputAnalyzer out = executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog); 297 listOutputFile(aotCacheFileLog + ".0"); // the log file for the training run 298 return out; 299 } 300 301 private OutputAnalyzer createClassList() throws Exception { 302 RunMode runMode = RunMode.TRAINING; 303 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 304 "-Xshare:off", 305 "-XX:DumpLoadedClassList=" + classListFile, 306 logToFile(classListFileLog, 307 "class+load=debug")); 308 cmdLine = addCommonVMArgs(runMode, cmdLine); 309 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 310 return executeAndCheck(cmdLine, runMode, classListFile, classListFileLog); 311 } 312 313 private OutputAnalyzer dumpStaticArchive() throws Exception { 314 RunMode runMode = RunMode.DUMP_STATIC; 315 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 316 "-Xlog:cds", 317 "-Xlog:cds+heap=error", 318 "-Xshare:dump", 319 "-XX:SharedArchiveFile=" + staticArchiveFile, 320 "-XX:SharedClassListFile=" + classListFile, 321 logToFile(staticArchiveFileLog, 322 "cds=debug", 323 "cds+class=debug", 324 "cds+heap=warning", 325 "cds+resolve=debug")); 326 cmdLine = addCommonVMArgs(runMode, cmdLine); 327 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 328 return executeAndCheck(cmdLine, runMode, staticArchiveFile, staticArchiveFileLog); 329 } 330 331 private OutputAnalyzer createAOTCache() throws Exception { 332 RunMode runMode = RunMode.ASSEMBLY; 333 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 334 "-Xlog:aot", 335 "-Xlog:aot+heap=error", 336 "-XX:AOTMode=create", 337 "-XX:AOTConfiguration=" + aotConfigurationFile, 338 "-XX:AOTCache=" + aotCacheFile, 339 logToFile(aotCacheFileLog, 340 "aot=debug", 341 "aot+class=debug", 342 "aot+heap=warning", 343 "aot+resolve=debug")); 344 cmdLine = addCommonVMArgs(runMode, cmdLine); 345 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 346 return executeAndCheck(cmdLine, runMode, aotCacheFile, aotCacheFileLog); 347 } 348 349 // Creating a dynamic CDS archive (with -XX:ArchiveClassesAtExit=<foo>.jsa) requires that the current 350 // JVM process is using a static archive (which is usually the default CDS archive included in the JDK). 351 // However, if the JDK doesn't include a default CDS archive that's compatible with the set of 352 // VM options used by this test, we need to create a temporary static archive to be used with -XX:ArchiveClassesAtExit. 353 private String getBaseArchiveForDynamicArchive() throws Exception { 354 WhiteBox wb = WhiteBox.getWhiteBox(); 355 if (wb.isSharingEnabled()) { 356 // This current JVM is able to use a default CDS archive included by the JDK, so 357 // if we launch a JVM child process (with the same set of options as the current JVM), 358 // that process is also able to use the same default CDS archive for creating 359 // a dynamic archive. 360 return null; 361 } else { 362 // This current JVM is unable to use a default CDS archive, so let's create a temporary 363 // static archive to be used with -XX:ArchiveClassesAtExit. 364 File f = new File(tempBaseArchiveFile); 365 if (!f.exists()) { 366 CDSOptions opts = new CDSOptions(); 367 opts.setArchiveName(tempBaseArchiveFile); 368 opts.addSuffix("-Djava.class.path="); 369 OutputAnalyzer out = CDSTestUtils.createArchive(opts); 370 CDSTestUtils.checkBaseDump(out); 371 } 372 return tempBaseArchiveFile; 373 } 374 } 375 376 private OutputAnalyzer dumpDynamicArchive() throws Exception { 377 RunMode runMode = RunMode.DUMP_DYNAMIC; 378 String[] cmdLine = new String[0]; 379 String baseArchive = getBaseArchiveForDynamicArchive(); 380 if (isDynamicWorkflow()) { 381 // "classic" dynamic archive 382 cmdLine = StringArrayUtils.concat(vmArgs(runMode), 383 "-Xlog:aot", 384 "-Xlog:cds", 385 "-XX:ArchiveClassesAtExit=" + dynamicArchiveFile, 386 logToFile(dynamicArchiveFileLog, 387 "cds=debug", 388 "cds+class=debug", 389 "cds+resolve=debug", 390 "class+load=debug")); 391 cmdLine = addCommonVMArgs(runMode, cmdLine); 392 } 393 if (baseArchive != null) { 394 cmdLine = StringArrayUtils.concat(cmdLine, "-XX:SharedArchiveFile=" + baseArchive); 395 } 396 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 397 return executeAndCheck(cmdLine, runMode, dynamicArchiveFile, dynamicArchiveFileLog); 398 } 399 400 private String trainingLog(String file) { 401 return logToFile(file, 402 "cds=debug", 403 "cds+class=debug", 404 "cds+heap=warning", 405 "cds+resolve=debug"); 406 } 407 408 // normal training workflow (main JVM process spawns child process) 409 private OutputAnalyzer trainingRun() throws Exception { 410 RunMode runMode = RunMode.TRAINING; 411 File f = new File(cdsFile); 412 f.delete(); 413 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 414 "-XX:+AOTClassLinking", 415 "-XX:+ArchiveDynamicProxies", 416 //"-XX:+ArchiveReflectionData", 417 "-XX:CacheDataStore=" + cdsFile, 418 "-cp", classpath(runMode), 419 // Use PID to distinguish the logs of the training process 420 // and the forked final image dump process. 421 "-Xlog:cds::uptime,level,tags,pid", 422 trainingLog(cdsFileLog)); 423 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 424 OutputAnalyzer out = executeAndCheck(cmdLine, runMode, cdsFile, cdsFileLog); 425 listOutputFile(cdsFileLog + ".0"); // the preimage dump 426 return out; 427 } 428 429 // "split" training workflow (launch the two processes manually, for easier debugging); 430 private OutputAnalyzer trainingRun0() throws Exception { 431 RunMode runMode = RunMode.TRAINING0; 432 File f = new File(cdsFile); 433 f.delete(); 434 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 435 "-XX:+UnlockDiagnosticVMOptions", 436 "-XX:+CDSManualFinalImage", 437 "-XX:+AOTClassLinking", 438 "-XX:+ArchiveDynamicProxies", 439 //"-XX:+ArchiveReflectionData", 440 "-XX:CacheDataStore=" + cdsFile, 441 "-cp", classpath(runMode), 442 trainingLog(cdsFilePreImageLog)); 443 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 444 return executeAndCheck(cmdLine, runMode, cdsFilePreImage, cdsFilePreImageLog); 445 } 446 private OutputAnalyzer trainingRun1() throws Exception { 447 RunMode runMode = RunMode.TRAINING1; 448 File f = new File(cdsFile); 449 f.delete(); 450 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 451 "-XX:+UnlockDiagnosticVMOptions", 452 "-XX:+AOTClassLinking", 453 "-XX:+ArchiveDynamicProxies", 454 //"-XX:+ArchiveReflectionData", 455 "-XX:CacheDataStore=" + cdsFile, 456 "-XX:CDSPreimage=" + cdsFilePreImage, 457 "-cp", classpath(runMode), 458 trainingLog(cdsFileLog)); 459 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 460 return executeAndCheck(cmdLine, runMode, cdsFile, cdsFileLog); 461 } 462 463 public OutputAnalyzer productionRun() throws Exception { 464 return productionRun(null, null); 465 } 466 467 public OutputAnalyzer productionRun(String[] extraVmArgs) throws Exception { 468 return productionRun(extraVmArgs, null); 469 } 470 471 // After calling run(String[]), you can call this method to run the app again, with the AOTCache 472 // using different args to the VM and application. 473 public OutputAnalyzer productionRun(String[] extraVmArgs, String[] extraAppArgs) throws Exception { 474 RunMode runMode = RunMode.PRODUCTION; 475 String[] cmdLine = StringArrayUtils.concat(vmArgs(runMode), 476 "-XX:+UnlockDiagnosticVMOptions", 477 "-XX:VerifyArchivedFields=2", // make sure archived heap objects are good. 478 logToFile(productionRunLog(), "aot", "cds")); 479 cmdLine = addCommonVMArgs(runMode, cmdLine); 480 481 if (isStaticWorkflow()) { 482 cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + staticArchiveFile); 483 } else if (isDynamicWorkflow()) { 484 cmdLine = StringArrayUtils.concat(cmdLine, "-Xshare:on", "-XX:SharedArchiveFile=" + dynamicArchiveFile); 485 } else if (isAOTWorkflow()) { 486 cmdLine = StringArrayUtils.concat(cmdLine, "-XX:AOTMode=on", "-XX:AOTCache=" + aotCacheFile); 487 } else { 488 cmdLine = StringArrayUtils.concat(cmdLine, "-XX:CacheDataStore=" + cdsFile); 489 } 490 491 if (extraVmArgs != null) { 492 cmdLine = StringArrayUtils.concat(cmdLine, extraVmArgs); 493 } 494 495 cmdLine = StringArrayUtils.concat(cmdLine, appCommandLine(runMode)); 496 497 if (extraAppArgs != null) { 498 cmdLine = StringArrayUtils.concat(cmdLine, extraAppArgs); 499 } 500 501 OutputAnalyzer out = executeAndCheck(cmdLine, runMode, productionRunLog()); 502 numProductionRuns ++; 503 return out; 504 } 505 506 public void run(String... args) throws Exception { 507 String err = "Must have at least one command line argument of the following: "; 508 String prefix = ""; 509 for (Workflow wf : Workflow.values()) { 510 err += prefix; 511 err += wf; 512 prefix = ", "; 513 } 514 if (args.length < 1) { 515 throw new RuntimeException(err); 516 } else { 517 if (args[0].equals("STATIC")) { 518 runStaticWorkflow(); 519 } else if (args[0].equals("DYNAMIC")) { 520 runDynamicWorkflow(); 521 } else if (args[0].equals("AOT")) { 522 runAOTWorkflow(args); 523 } else if (args[0].equals("LEYDEN")) { 524 runLeydenWorkflow(false); 525 } else if (args[0].equals("LEYDEN_TRAINONLY")) { 526 runLeydenWorkflow(true); 527 } else { 528 throw new RuntimeException(err); 529 } 530 } 531 } 532 533 public void runStaticWorkflow() throws Exception { 534 this.workflow = Workflow.STATIC; 535 createClassList(); 536 dumpStaticArchive(); 537 productionRun(); 538 } 539 540 public void runDynamicWorkflow() throws Exception { 541 this.workflow = Workflow.DYNAMIC; 542 dumpDynamicArchive(); 543 productionRun(); 544 } 545 546 // See JEP 483; stop at the assembly run; do not execute production run 547 public void runAOTAssemblyWorkflow() throws Exception { 548 this.workflow = Workflow.AOT; 549 recordAOTConfiguration(); 550 createAOTCache(); 551 } 552 553 // See JEP 483 554 public void runAOTWorkflow(String... args) throws Exception { 555 this.workflow = Workflow.AOT; 556 boolean oneStepTraining = true; // by default use onestep trainning 557 558 if (System.getProperty("CDSAppTester.two.step.training") != null) { 559 oneStepTraining = false; 560 } 561 562 if (args.length > 1) { 563 // Tests such as test/hotspot/jtreg/runtime/cds/appcds/aotCache/SpecialCacheNames.java 564 // use --one-step-training or --two-step-training to force a certain training workflow. 565 if (args[1].equals("--one-step-training")) { 566 oneStepTraining = true; 567 } else if (args[1].equals("--two-step-training")) { 568 oneStepTraining = false; 569 } else { 570 throw new RuntimeException("Unknown option: " + args[1]); 571 } 572 } 573 574 if (oneStepTraining) { 575 try { 576 inOneStepTraining = true; 577 createAOTCacheOneStep(); 578 } finally { 579 inOneStepTraining = false; 580 } 581 } else { 582 recordAOTConfiguration(); 583 createAOTCache(); 584 } 585 productionRun(); 586 } 587 588 private void runLeydenWorkflow(boolean trainOnly) throws Exception { 589 this.workflow = Workflow.LEYDEN; 590 if (System.getProperty("CDSAppTester.split.new.workflow") != null) { 591 trainingRun0(); 592 trainingRun1(); 593 } else { 594 trainingRun(); 595 } 596 if (!trainOnly) { 597 productionRun(); 598 } 599 } 600 }