1 /* 2 * Copyright (c) 2024, 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 25 /* 26 * @test 27 * @summary "AOT" aliases for traditional CDS command-line options 28 * @requires vm.cds 29 * @requires vm.flagless 30 * @library /test/lib /test/hotspot/jtreg/runtime/cds/appcds/test-classes 31 * @build Hello 32 * @run driver jdk.test.lib.helpers.ClassFileInstaller -jar hello.jar Hello 33 * @run driver AOTFlags 34 */ 35 36 import java.io.File; 37 import java.util.Map; 38 import jdk.test.lib.cds.CDSTestUtils; 39 import jdk.test.lib.helpers.ClassFileInstaller; 40 import jdk.test.lib.process.OutputAnalyzer; 41 import jdk.test.lib.process.ProcessTools; 42 43 public class AOTFlags { 44 static String appJar = ClassFileInstaller.getJarPath("hello.jar"); 45 static String aotConfigFile = "hello.aotconfig"; 46 static String aotCacheFile = "hello.aot"; 47 static String helloClass = "Hello"; 48 49 public static void main(String[] args) throws Exception { 50 positiveTests(); 51 negativeTests(); 52 } 53 54 static void positiveTests() throws Exception { 55 String hasTrainingDataPattern = "MethodTrainingData *= *[1-9]"; 56 String noTrainingDataPattern = "MethodTrainingData *= *0"; 57 String hasCachedCodePattern = "Shared file region .cc. .: *[1-9]"; 58 String noCachedCodePattern = "Shared file region .cc. .: *0"; 59 String hasMappedCachedCodePattern = "Mapped [0-9]+ bytes at address 0x[0-9a-f]+ from AOT Code Cache"; 60 61 //---------------------------------------------------------------------- 62 printTestCase("Training Run"); 63 ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( 64 "-XX:AOTMode=record", 65 "-XX:AOTConfiguration=" + aotConfigFile, 66 "-Xlog:cds=debug", 67 "-cp", appJar, helloClass); 68 69 OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "train"); 70 out.shouldContain("Hello World"); 71 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile); 72 out.shouldMatch(hasTrainingDataPattern); 73 out.shouldMatch(noCachedCodePattern); 74 out.shouldHaveExitValue(0); 75 76 //---------------------------------------------------------------------- 77 printTestCase("Assembly Phase (AOTClassLinking unspecified -> should be enabled by default)"); 78 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 79 "-XX:AOTMode=create", 80 "-XX:AOTConfiguration=" + aotConfigFile, 81 "-XX:AOTCache=" + aotCacheFile, 82 "-Xlog:cds", 83 "-cp", appJar); 84 out = CDSTestUtils.executeAndLog(pb, "asm"); 85 out.shouldContain("Dumping shared data to file:"); 86 out.shouldMatch("cds.*hello[.]aot"); 87 out.shouldMatch(hasTrainingDataPattern); 88 out.shouldMatch(hasCachedCodePattern); 89 out.shouldHaveExitValue(0); 90 91 //---------------------------------------------------------------------- 92 printTestCase("Production Run with AOTCache"); 93 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 94 "-XX:AOTCache=" + aotCacheFile, 95 "-Xlog:cds", 96 "-Xlog:aot+codecache*", 97 "-cp", appJar, helloClass); 98 out = CDSTestUtils.executeAndLog(pb, "prod"); 99 out.shouldContain("Using AOT-linked classes: true (static archive: has aot-linked classes)"); 100 out.shouldContain("Opened AOT cache hello.aot."); 101 out.shouldContain("Hello World"); 102 out.shouldMatch(hasMappedCachedCodePattern); 103 out.shouldHaveExitValue(0); 104 105 //---------------------------------------------------------------------- 106 printTestCase("AOTMode=off"); 107 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 108 "-XX:AOTCache=" + aotCacheFile, 109 "--show-version", 110 "-Xlog:cds", 111 "-XX:AOTMode=off", 112 "-cp", appJar, helloClass); 113 out = CDSTestUtils.executeAndLog(pb, "prod"); 114 out.shouldNotContain(", sharing"); 115 out.shouldNotContain("Opened AOT cache hello.aot."); 116 out.shouldContain("Hello World"); 117 out.shouldHaveExitValue(0); 118 119 //---------------------------------------------------------------------- 120 printTestCase("AOTMode=auto"); 121 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 122 "-XX:AOTCache=" + aotCacheFile, 123 "--show-version", 124 "-Xlog:cds", 125 "-XX:AOTMode=auto", 126 "-cp", appJar, helloClass); 127 out = CDSTestUtils.executeAndLog(pb, "prod"); 128 out.shouldContain(", sharing"); 129 out.shouldContain("Opened AOT cache hello.aot."); 130 out.shouldContain("Hello World"); 131 out.shouldHaveExitValue(0); 132 133 //---------------------------------------------------------------------- 134 printTestCase("AOTMode=on"); 135 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 136 "-XX:AOTCache=" + aotCacheFile, 137 "--show-version", 138 "-Xlog:cds", 139 "-XX:AOTMode=on", 140 "-cp", appJar, helloClass); 141 out = CDSTestUtils.executeAndLog(pb, "prod"); 142 out.shouldContain(", sharing"); 143 out.shouldContain("Opened AOT cache hello.aot."); 144 out.shouldContain("Hello World"); 145 out.shouldHaveExitValue(0); 146 147 //---------------------------------------------------------------------- 148 printTestCase("Assembly Phase with -XX:-AOTClassLinking"); 149 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 150 "-XX:AOTMode=create", 151 "-XX:-AOTClassLinking", 152 "-XX:AOTConfiguration=" + aotConfigFile, 153 "-XX:AOTCache=" + aotCacheFile, 154 "-Xlog:cds=debug", 155 "-cp", appJar); 156 out = CDSTestUtils.executeAndLog(pb, "asm"); 157 out.shouldContain("Dumping shared data to file:"); 158 out.shouldMatch("cds.*hello[.]aot"); 159 out.shouldMatch(noTrainingDataPattern); 160 out.shouldMatch(noCachedCodePattern); 161 out.shouldHaveExitValue(0); 162 163 //---------------------------------------------------------------------- 164 printTestCase("Production Run with AOTCache, which was created with -XX:-AOTClassLinking"); 165 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 166 "-XX:AOTCache=" + aotCacheFile, 167 "-Xlog:cds", 168 "-Xlog:aot+codecache*", 169 "-cp", appJar, helloClass); 170 out = CDSTestUtils.executeAndLog(pb, "prod"); 171 out.shouldContain("Using AOT-linked classes: false (static archive: no aot-linked classes)"); 172 out.shouldContain("Opened AOT cache hello.aot."); 173 out.shouldContain("Hello World"); 174 out.shouldNotMatch(hasMappedCachedCodePattern); 175 out.shouldHaveExitValue(0); 176 177 //---------------------------------------------------------------------- 178 printTestCase("Training run with -XX:-AOTClassLinking, but assembly run with -XX:+AOTClassLinking"); 179 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 180 "-XX:AOTMode=record", 181 "-XX:-AOTClassLinking", 182 "-XX:AOTConfiguration=" + aotConfigFile, 183 "-Xlog:cds=debug", 184 "-cp", appJar, helloClass); 185 out = CDSTestUtils.executeAndLog(pb, "train"); 186 out.shouldContain("Hello World"); 187 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile); 188 out.shouldHaveExitValue(0); 189 190 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 191 "-XX:AOTMode=create", 192 "-XX:+AOTClassLinking", 193 "-XX:AOTConfiguration=" + aotConfigFile, 194 "-XX:AOTCache=" + aotCacheFile, 195 "-Xlog:cds=debug", 196 "-cp", appJar); 197 out = CDSTestUtils.executeAndLog(pb, "asm"); 198 out.shouldContain("Dumping shared data to file:"); 199 out.shouldMatch("cds.*hello[.]aot"); 200 out.shouldHaveExitValue(0); 201 202 //---------------------------------------------------------------------- 203 printTestCase("One step training run (JEP-JDK-8354330"); 204 205 // Set all AOTMode/AOTCacheOutput/AOTConfiguration 206 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 207 "-XX:AOTMode=record", 208 "-XX:AOTCacheOutput=" + aotCacheFile, 209 "-XX:AOTConfiguration=" + aotConfigFile, 210 "-Xlog:cds=debug", 211 "-cp", appJar, helloClass); 212 out = CDSTestUtils.executeAndLog(pb, "ontstep-train"); 213 out.shouldContain("Hello World"); 214 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile); 215 out.shouldContain("AOTCache creation is complete: hello.aot"); 216 out.shouldHaveExitValue(0); 217 218 // Set AOTCacheOutput/AOTConfiguration only; Ergo for: AOTMode=record 219 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 220 "-XX:AOTCacheOutput=" + aotCacheFile, 221 "-XX:AOTConfiguration=" + aotConfigFile, 222 "-Xlog:cds=debug", 223 "-cp", appJar, helloClass); 224 out = CDSTestUtils.executeAndLog(pb, "ontstep-train"); 225 out.shouldContain("Hello World"); 226 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile); 227 out.shouldContain("AOTCache creation is complete: hello.aot"); 228 out.shouldHaveExitValue(0); 229 230 // Set AOTCacheOutput only; Ergo for: AOTMode=record, AOTConfiguration=<temp> 231 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 232 "-XX:AOTCacheOutput=" + aotCacheFile, 233 "-Xlog:cds=debug", 234 "-cp", appJar, helloClass); 235 out = CDSTestUtils.executeAndLog(pb, "ontstep-train"); 236 out.shouldContain("Hello World"); 237 out.shouldContain("Temporary AOTConfiguration recorded: " + aotCacheFile + ".config"); 238 out.shouldContain("AOTCache creation is complete: hello.aot"); 239 out.shouldHaveExitValue(0); 240 241 //---------------------------------------------------------------------- 242 printTestCase("AOT_TOOL_OPTIONS (JEP-JDK-8354330"); 243 244 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 245 "-XX:AOTMode=record", 246 "-XX:AOTCacheOutput=" + aotCacheFile, 247 "-XX:AOTConfiguration=" + aotConfigFile, 248 "-Xlog:cds=debug", 249 "-cp", appJar, helloClass); 250 Map<String, String> env = pb.environment(); 251 // The "-Xshare:off" below should be treated as part of a property value and not 252 // a VM option by itself 253 env.put("AOT_TOOL_OPTIONS", "-Dsome.option='foo -Xshare:off ' -Xmx512m -XX:-AOTClassLinking"); 254 out = CDSTestUtils.executeAndLog(pb, "ontstep-train"); 255 out.shouldContain("Hello World"); 256 out.shouldContain("AOTConfiguration recorded: " + aotConfigFile); 257 out.shouldContain("AOTCache creation is complete: hello.aot"); 258 out.shouldContain("Picked up AOT_TOOL_OPTIONS: -Dsome.option='foo -Xshare:off '"); 259 260 // -XX:-AOTClassLinking should take effect in the assembly process. 261 out.shouldMatch("aot-linked =[ ]+0,"); 262 out.shouldNotMatch("aot-linked =[ ]+[1-9]"); 263 out.shouldHaveExitValue(0); 264 } 265 266 static void negativeTests() throws Exception { 267 //---------------------------------------------------------------------- 268 printTestCase("Mixing old and new options"); 269 String mixOldNewErrSuffix = " cannot be used at the same time with -Xshare:on, -Xshare:auto, " 270 + "-Xshare:off, -Xshare:dump, DumpLoadedClassList, SharedClassListFile, " 271 + "or SharedArchiveFile"; 272 273 ProcessBuilder pb = ProcessTools.createLimitedTestJavaProcessBuilder( 274 "-Xshare:off", 275 "-XX:AOTConfiguration=" + aotConfigFile, 276 "-cp", appJar, helloClass); 277 278 OutputAnalyzer out = CDSTestUtils.executeAndLog(pb, "neg"); 279 out.shouldContain("Option AOTConfiguration" + mixOldNewErrSuffix); 280 out.shouldNotHaveExitValue(0); 281 282 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 283 "-XX:SharedArchiveFile=" + aotCacheFile, 284 "-XX:AOTCache=" + aotCacheFile, 285 "-cp", appJar, helloClass); 286 out = CDSTestUtils.executeAndLog(pb, "neg"); 287 out.shouldContain("Option AOTCache" + mixOldNewErrSuffix); 288 out.shouldNotHaveExitValue(0); 289 290 //---------------------------------------------------------------------- 291 printTestCase("Use AOTConfiguration without AOTMode"); 292 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 293 "-XX:AOTConfiguration=" + aotConfigFile, 294 "-cp", appJar, helloClass); 295 296 out = CDSTestUtils.executeAndLog(pb, "neg"); 297 out.shouldContain("AOTConfiguration can only be used with -XX:AOTMode=record or -XX:AOTMode=create"); 298 out.shouldNotHaveExitValue(0); 299 300 //---------------------------------------------------------------------- 301 printTestCase("Use AOTMode without AOTCacheOutput or AOTConfiguration"); 302 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 303 "-XX:AOTMode=record", 304 "-cp", appJar, helloClass); 305 306 out = CDSTestUtils.executeAndLog(pb, "neg"); 307 out.shouldContain("-XX:AOTMode=record cannot be used without setting AOTCacheOutput or AOTConfiguration"); 308 309 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 310 "-XX:AOTMode=create", 311 "-cp", appJar, helloClass); 312 313 out = CDSTestUtils.executeAndLog(pb, "neg"); 314 out.shouldContain("-XX:AOTMode=create cannot be used without setting AOTConfiguration"); 315 out.shouldNotHaveExitValue(0); 316 317 //---------------------------------------------------------------------- 318 printTestCase("Bad AOTMode"); 319 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 320 "-XX:AOTMode=foo", 321 "-cp", appJar, helloClass); 322 323 out = CDSTestUtils.executeAndLog(pb, "neg"); 324 out.shouldContain("Unrecognized value foo for AOTMode. Must be one of the following: off, record, create, auto, on"); 325 out.shouldNotHaveExitValue(0); 326 327 //---------------------------------------------------------------------- 328 printTestCase("AOTCache specified with -XX:AOTMode=record"); 329 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 330 "-XX:AOTMode=record", 331 "-XX:AOTConfiguration=" + aotConfigFile, 332 "-XX:AOTCache=" + aotCacheFile, 333 "-cp", appJar, helloClass); 334 335 out = CDSTestUtils.executeAndLog(pb, "neg"); 336 out.shouldContain("AOTCache must not be specified when using -XX:AOTMode=record"); 337 out.shouldNotHaveExitValue(0); 338 339 //---------------------------------------------------------------------- 340 printTestCase("AOTCache/AOTCacheOutput not specified with -XX:AOTMode=create"); 341 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 342 "-XX:AOTMode=create", 343 "-XX:AOTConfiguration=" + aotConfigFile, 344 "-cp", appJar, helloClass); 345 346 out = CDSTestUtils.executeAndLog(pb, "neg"); 347 out.shouldContain("AOTCache or AOTCacheOutput must be specified when using -XX:AOTMode=create"); 348 out.shouldNotHaveExitValue(0); 349 350 //---------------------------------------------------------------------- 351 printTestCase("AOTCache and AOTCacheOutput have different values"); 352 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 353 "-XX:AOTMode=create", 354 "-XX:AOTConfiguration=" + aotConfigFile, 355 "-XX:AOTCache=aaa", 356 "-XX:AOTCacheOutput=bbb", 357 "-cp", appJar, helloClass); 358 359 out = CDSTestUtils.executeAndLog(pb, "neg"); 360 out.shouldContain("AOTCache and AOTCacheOutput have different values"); 361 out.shouldNotHaveExitValue(0); 362 363 //---------------------------------------------------------------------- 364 printTestCase("No such config file"); 365 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 366 "-XX:AOTMode=create", 367 "-XX:AOTConfiguration=no-such-file", 368 "-XX:AOTCache=" + aotCacheFile, 369 "-cp", appJar, helloClass); 370 371 out = CDSTestUtils.executeAndLog(pb, "neg"); 372 out.shouldContain("Must be a valid AOT configuration generated by the current JVM: no-such-file"); 373 out.shouldNotHaveExitValue(0); 374 375 //---------------------------------------------------------------------- 376 printTestCase("AOTConfiguration file cannot be used as a CDS archive"); 377 378 // first make sure we have a valid aotConfigFile 379 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 380 "-XX:AOTMode=record", 381 "-XX:AOTConfiguration=" + aotConfigFile, 382 "-cp", appJar, helloClass); 383 384 out = CDSTestUtils.executeAndLog(pb, "train"); 385 out.shouldHaveExitValue(0); 386 387 // Cannot use this config file as a AOT cache 388 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 389 "-XX:AOTMode=on", 390 "-XX:AOTCache=" + aotConfigFile, 391 "-cp", appJar, helloClass); 392 393 out = CDSTestUtils.executeAndLog(pb, "neg"); 394 out.shouldContain("Not a valid AOT cache (hello.aotconfig)"); 395 out.shouldNotHaveExitValue(0); 396 397 // Cannot use this config file as a CDS archive 398 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 399 "-Xshare:on", 400 "-XX:SharedArchiveFile=" + aotConfigFile, 401 "-cp", appJar, helloClass); 402 403 out = CDSTestUtils.executeAndLog(pb, "neg"); 404 out.shouldContain("Not a valid archive (hello.aotconfig)"); 405 out.shouldNotHaveExitValue(0); 406 407 //---------------------------------------------------------------------- 408 printTestCase("Classpath mismatch when creating archive"); 409 410 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 411 "-XX:AOTMode=create", 412 "-XX:AOTConfiguration=" + aotConfigFile, 413 "-XX:AOTCache=" + aotCacheFile, 414 "-cp", "noSuchJar.jar"); 415 416 out = CDSTestUtils.executeAndLog(pb, "neg"); 417 out.shouldContain("class path and/or module path are not compatible with the ones " + 418 "specified when the AOTConfiguration file was recorded"); 419 out.shouldContain("Unable to use create AOT cache"); 420 out.shouldHaveExitValue(1); 421 422 //---------------------------------------------------------------------- 423 printTestCase("Cannot use multiple paths in AOTConfiguration"); 424 425 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 426 "-XX:AOTMode=record", 427 "-XX:AOTConfiguration=" + aotConfigFile + File.pathSeparator + "dummy", 428 "-cp", "noSuchJar.jar"); 429 430 out = CDSTestUtils.executeAndLog(pb, "neg"); 431 out.shouldContain("Option AOTConfiguration must specify a single file name"); 432 out.shouldHaveExitValue(1); 433 434 //---------------------------------------------------------------------- 435 printTestCase("Cannot use multiple paths in AOTCache"); 436 437 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 438 "-XX:AOTMode=create", 439 "-XX:AOTConfiguration=" + aotConfigFile, 440 "-XX:AOTCache=" + aotCacheFile + File.pathSeparator + "dummy", 441 "-cp", "noSuchJar.jar"); 442 443 out = CDSTestUtils.executeAndLog(pb, "neg"); 444 out.shouldContain("Option AOTCache must specify a single file name"); 445 out.shouldHaveExitValue(1); 446 447 //---------------------------------------------------------------------- 448 printTestCase("Cannot use a dynamic CDS archive for -XX:AOTCache"); 449 String staticArchive = "static.jsa"; 450 String dynamicArchive = "dynamic.jsa"; 451 452 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 453 "-Xshare:dump", 454 "-XX:SharedArchiveFile=" + staticArchive); 455 out = CDSTestUtils.executeAndLog(pb, "static"); 456 out.shouldHaveExitValue(0); 457 458 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 459 "-XX:SharedArchiveFile=" + staticArchive, 460 "-XX:ArchiveClassesAtExit=" + dynamicArchive, 461 "--version"); 462 out = CDSTestUtils.executeAndLog(pb, "dynamic"); 463 out.shouldHaveExitValue(0); 464 465 pb = ProcessTools.createLimitedTestJavaProcessBuilder( 466 "-Xlog:cds", 467 "-XX:AOTMode=on", 468 "-XX:AOTCache=" + dynamicArchive, 469 "--version"); 470 471 out = CDSTestUtils.executeAndLog(pb, "neg"); 472 out.shouldContain("Unable to use AOT cache."); 473 out.shouldContain("Not a valid AOT cache (dynamic.jsa)"); 474 out.shouldHaveExitValue(1); 475 } 476 477 static int testNum = 0; 478 static void printTestCase(String s) { 479 System.out.println("vvvvvvv TEST CASE " + testNum + ": " + s + ": starts here vvvvvvv"); 480 testNum++; 481 } 482 }