1 /* 2 * Copyright (c) 2014, 2024, 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 import jdk.test.lib.Utils; 26 import jdk.test.lib.BuildHelper; 27 import jdk.test.lib.JDKToolFinder; 28 import jdk.test.lib.Platform; 29 import jdk.test.lib.cds.CDSOptions; 30 import jdk.test.lib.cds.CDSTestUtils; 31 import jdk.test.lib.cds.CDSTestUtils.Result; 32 import jdk.test.lib.process.ProcessTools; 33 import jdk.test.lib.process.OutputAnalyzer; 34 import java.io.File; 35 import java.io.FileInputStream; 36 import java.io.FileOutputStream; 37 import java.io.InputStream; 38 import java.io.IOException; 39 import java.net.URI; 40 import java.nio.file.DirectoryStream; 41 import java.nio.file.Files; 42 import java.nio.file.FileSystem; 43 import java.nio.file.FileSystems; 44 import java.nio.file.Path; 45 import java.nio.file.Paths; 46 import java.text.SimpleDateFormat; 47 import java.util.Arrays; 48 import java.util.ArrayList; 49 import java.util.Date; 50 import java.util.Enumeration; 51 import java.util.regex.Matcher; 52 import java.util.regex.Pattern; 53 import java.util.zip.ZipEntry; 54 import java.util.zip.ZipFile; 55 import java.util.zip.ZipOutputStream; 56 import jtreg.SkippedException; 57 import cdsutils.DynamicDumpHelper; 58 59 60 /** 61 * This is a test utility class for common AppCDS test functionality. 62 * 63 * Various methods use (String ...) for passing VM options. Note that the order 64 * of the VM options are important in certain cases. Many methods take arguments like 65 * 66 * (String prefix[], String suffix[], String... opts) 67 * 68 * Note that the order of the VM options is: 69 * 70 * prefix + opts + suffix 71 */ 72 public class TestCommon extends CDSTestUtils { 73 private static final String JSA_FILE_PREFIX = CDSTestUtils.getOutputDir() + 74 File.separator; 75 76 private static final SimpleDateFormat timeStampFormat = 77 new SimpleDateFormat("HH'h'mm'm'ss's'SSS"); 78 79 private static final String timeoutFactor = 80 System.getProperty("test.timeout.factor", "1.0"); 81 82 private static String currentArchiveName; 83 84 // Call this method to start new archive with new unique name 85 public static void startNewArchiveName() { 86 deletePriorArchives(); 87 currentArchiveName = getNewArchiveName(); 88 } 89 90 // Call this method to get current archive name 91 public static String getCurrentArchiveName() { 92 return currentArchiveName; 93 } 94 95 public static void setCurrentArchiveName(String archiveName) { 96 currentArchiveName = archiveName; 97 } 98 99 public static String getNewArchiveName() { 100 return getNewArchiveName(null); 101 } 102 103 public static String getNewArchiveName(String stem) { 104 if (stem == null) { 105 stem = "appcds"; 106 } 107 return JSA_FILE_PREFIX + stem + "-" + 108 timeStampFormat.format(new Date()) + ".jsa"; 109 } 110 111 // Attempt to clean old archives to preserve space 112 // Archives are large artifacts (20Mb or more), and much larger than 113 // most other artifacts created in jtreg testing. 114 // Therefore it is a good idea to clean the old archives when they are not needed. 115 // In most cases the deletion attempt will succeed; on rare occasion the 116 // delete operation will fail since the system or VM process still holds a handle 117 // to the file; in such cases the File.delete() operation will silently fail, w/o 118 // throwing an exception, thus allowing testing to continue. 119 public static void deletePriorArchives() { 120 File dir = CDSTestUtils.getOutputDirAsFile(); 121 String files[] = dir.list(); 122 for (String name : files) { 123 if (name.startsWith("appcds-") && name.endsWith(".jsa")) { 124 if (!(new File(dir, name)).delete()) 125 System.out.println("deletePriorArchives(): delete failed for file " + name); 126 } 127 } 128 } 129 130 // Create AppCDS archive using most common args - convenience method 131 // Legacy name preserved for compatibility 132 public static OutputAnalyzer dump(String appJar, String classList[], 133 String... suffix) throws Exception { 134 return createArchive(appJar, classList, suffix); 135 } 136 137 public static OutputAnalyzer dump(String appJarDir, String appJar, String classList[], 138 String... suffix) throws Exception { 139 return createArchive(appJarDir, appJar, classList, suffix); 140 } 141 142 /** 143 * Dump the base archive. The JDK's default class list is used (unless otherwise specified 144 * in cmdLineSuffix). 145 */ 146 public static OutputAnalyzer dumpBaseArchive(String baseArchiveName, String ... cmdLineSuffix) 147 throws Exception 148 { 149 CDSOptions opts = new CDSOptions(); 150 opts.setArchiveName(baseArchiveName); 151 opts.addSuffix(cmdLineSuffix); 152 opts.addSuffix("-Djava.class.path="); 153 OutputAnalyzer out = CDSTestUtils.createArchive(opts); 154 CDSTestUtils.checkBaseDump(out); 155 return out; 156 } 157 158 public static OutputAnalyzer dumpBaseArchive(String baseArchiveName, String classList[], String ... cmdLineSuffix) 159 throws Exception 160 { 161 CDSOptions opts = new CDSOptions(); 162 opts.setArchiveName(baseArchiveName); 163 opts.setClassList(classList); 164 opts.addSuffix(cmdLineSuffix); 165 opts.addSuffix("-Djava.class.path="); 166 OutputAnalyzer out = CDSTestUtils.createArchive(opts); 167 CDSTestUtils.checkBaseDump(out); 168 return out; 169 } 170 171 // Create AppCDS archive using most common args - convenience method 172 public static OutputAnalyzer createArchive(String appJar, String classList[], 173 String... suffix) throws Exception { 174 CDSOptions opts = (new CDSOptions()).setAppJar(appJar); 175 opts.setClassList(classList); 176 opts.addSuffix(suffix); 177 return createArchive(opts); 178 } 179 180 public static OutputAnalyzer createArchive(String appJarDir, String appJar, String classList[], 181 String... suffix) throws Exception { 182 CDSOptions opts = (new CDSOptions()).setAppJar(appJar); 183 opts.setAppJarDir(appJarDir); 184 opts.setClassList(classList); 185 opts.addSuffix(suffix); 186 return createArchive(opts); 187 } 188 189 // Simulate -Xshare:dump with -XX:ArchiveClassesAtExit. See comments around patchJarForDynamicDump() 190 private static final Class tmp = DynamicDumpHelper.class; 191 192 // name of the base archive to be used for dynamic dump 193 private static String tempBaseArchive = null; 194 195 private static void captureVerifyOpts(ArrayList<String> opts, ArrayList<String> verifyOpts) { 196 boolean addedDiagnosticOpt = false; 197 for (String s : opts) { 198 if (s.startsWith("-XX:-BytecodeVerification")) { 199 if (!addedDiagnosticOpt) { 200 verifyOpts.add("-XX:+UnlockDiagnosticVMOptions"); 201 addedDiagnosticOpt = true; 202 } 203 verifyOpts.add(s); 204 } 205 if (s.startsWith("-Xverify")) { 206 verifyOpts.add(s); 207 } 208 } 209 } 210 211 // Create AppCDS archive using appcds options 212 public static OutputAnalyzer createArchive(CDSOptions opts) 213 throws Exception { 214 ArrayList<String> cmd = new ArrayList<String>(); 215 ArrayList<String> verifyOpts = new ArrayList<String>(); 216 startNewArchiveName(); 217 218 for (String p : opts.prefix) cmd.add(p); 219 220 if (opts.appJar != null) { 221 cmd.add("-cp"); 222 cmd.add(opts.appJar); 223 File jf = new File(opts.appJar); 224 if (DYNAMIC_DUMP && !jf.isDirectory()) { 225 patchJarForDynamicDump(opts.appJar); 226 } 227 } else { 228 cmd.add("-Djava.class.path="); 229 } 230 231 if (opts.archiveName == null) { 232 opts.archiveName = getCurrentArchiveName(); 233 } 234 235 if (DYNAMIC_DUMP) { 236 File baseArchive = null; 237 captureVerifyOpts(opts.suffix, verifyOpts); 238 int size = verifyOpts.size(); 239 if (tempBaseArchive == null || !(new File(tempBaseArchive)).isFile() || size > 0) { 240 tempBaseArchive = getNewArchiveName("tempBaseArchive"); 241 if (size == 0) { 242 dumpBaseArchive(tempBaseArchive); 243 } else { 244 dumpBaseArchive(tempBaseArchive, verifyOpts.toArray(new String[size])); 245 } 246 } 247 cmd.add("-Xshare:on"); 248 cmd.add("-XX:SharedArchiveFile=" + tempBaseArchive); 249 cmd.add("-XX:ArchiveClassesAtExit=" + opts.archiveName); 250 cmd.add("-Xlog:cds"); 251 cmd.add("-Xlog:cds+dynamic"); 252 boolean mainModuleSpecified = false; 253 boolean patchModuleSpecified = false; 254 for (String s : opts.suffix) { 255 if (s.length() == 0) { 256 continue; 257 } 258 if (s.equals("-m")) { 259 mainModuleSpecified = true; 260 } 261 if (s.startsWith("--patch-module=")) { 262 patchModuleSpecified = true; 263 } 264 cmd.add(s); 265 } 266 267 if (opts.appJar != null) { 268 // classlist is supported only when we have a Jar file to patch (to insert 269 // cdsutils.DynamicDumpHelper) 270 if (opts.classList == null) { 271 throw new RuntimeException("test.dynamic.dump requires classList file"); 272 } 273 274 if (!mainModuleSpecified && !patchModuleSpecified) { 275 cmd.add("cdsutils.DynamicDumpHelper"); 276 File classListFile = makeClassList(opts.classList); 277 cmd.add(classListFile.getPath()); 278 } 279 } else { 280 if (!mainModuleSpecified && !patchModuleSpecified) { 281 // If you have an empty classpath, you cannot specify a classlist! 282 if (opts.classList != null && opts.classList.length > 0) { 283 throw new RuntimeException("test.dynamic.dump is not supported with an empty classpath while the classlist is not empty"); 284 } 285 cmd.add("-version"); 286 } 287 } 288 } else { 289 // static dump 290 cmd.add("-Xshare:dump"); 291 cmd.add("-Xlog:cds"); 292 cmd.add("-XX:SharedArchiveFile=" + opts.archiveName); 293 294 if (opts.classList != null) { 295 File classListFile = makeClassList(opts.classList); 296 cmd.add("-XX:ExtraSharedClassListFile=" + classListFile.getPath()); 297 } 298 for (String s : opts.suffix) { 299 cmd.add(s); 300 } 301 } 302 303 ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmd); 304 if (opts.appJarDir != null) { 305 pb.directory(new File(opts.appJarDir)); 306 } 307 308 OutputAnalyzer output = executeAndLog(pb, "dump"); 309 if (DYNAMIC_DUMP && isUnableToMap(output)) { 310 throw new SkippedException(UnableToMapMsg); 311 } 312 return output; 313 } 314 315 // This allows you to run the AppCDS tests with JFR enabled at runtime (though not at 316 // dump time, as that's uncommon for typical AppCDS users). 317 // 318 // To run in this special mode, add the following to your jtreg command-line 319 // -Dtest.cds.run.with.jfr=true 320 // 321 // Some AppCDS tests are not compatible with this mode. See the group 322 // hotspot_appcds_with_jfr in ../../TEST.ROOT for details. 323 private static final boolean RUN_WITH_JFR = Boolean.getBoolean("test.cds.run.with.jfr"); 324 // This method simulates -Xshare:dump with -XX:ArchiveClassesAtExit. This way, we 325 // can re-use many tests (outside of the ./dynamicArchive directory) for testing 326 // general features of JDK-8215311 (JEP 350: Dynamic CDS Archives). 327 // 328 // We insert the cdsutils/DynamicDumpHelper.class into the first Jar file in 329 // the classpath. We use this class to load all the classes specified in the classlist. 330 // 331 // There's no need to change the run-time command-line: in this special mode, two 332 // archives are involved. The command-line specifies only the top archive. However, 333 // the location of the base archive is recorded in the top archive, so it can be 334 // determined by the JVM at runtime start-up. 335 // 336 // To run in this special mode, specify the following in your jtreg command-line 337 // -Dtest.dynamic.cds.archive=true 338 // 339 // Note that some tests are not compatible with this special mode, including 340 // + Tests in ./dynamicArchive: these tests are specifically written for 341 // dynamic archive, and do not use TestCommon.createArchive(), which works 342 // together with patchJarForDynamicDump(). 343 // + Tests related to cached objects and shared strings: dynamic dumping 344 // does not support these. 345 // + Custom loader tests: DynamicDumpHelper doesn't support the required 346 // classlist syntax. (FIXME). 347 // + Extra symbols and extra strings. 348 // See the hotspot_appcds_dynamic in ../../TEST.ROOT for details. 349 // 350 // To run all tests that are compatible with this mode: 351 // cd test/hotspot/jtreg 352 // jtreg -Dtest.dynamic.cds.archive=true :hotspot_appcds_dynamic 353 // 354 private static void patchJarForDynamicDump(String cp) throws Exception { 355 System.out.println("patchJarForDynamicDump: classpath = " + cp); 356 String firstJar = cp; 357 int n = firstJar.indexOf(File.pathSeparator); 358 if (n > 0) { 359 firstJar = firstJar.substring(0, n); 360 } 361 // get the real path in case the firstJar is specified as a relative path 362 firstJar = Paths.get(firstJar).toRealPath().toString(); 363 String classDir = System.getProperty("test.classes"); 364 String expected = getOutputDir() + File.separator; 365 366 if (!firstJar.startsWith(expected)) { 367 throw new RuntimeException("FIXME: jar file not at a supported location ('" 368 + expected + "'): " + firstJar); 369 } 370 371 String replaceJar = firstJar + ".tmp"; 372 String patchClass = "cdsutils/DynamicDumpHelper.class"; 373 ZipFile zipFile = new ZipFile(firstJar); 374 byte[] buf = new byte[1024]; 375 int len; 376 if (zipFile.getEntry(patchClass) == null) { 377 FileOutputStream fout = new FileOutputStream(replaceJar); 378 final ZipOutputStream zos = new ZipOutputStream(fout); 379 380 zos.putNextEntry(new ZipEntry(patchClass)); 381 InputStream is = new FileInputStream(classDir + File.separator + patchClass); 382 while ((len = (is.read(buf))) > 0) { 383 zos.write(buf, 0, len); 384 } 385 zos.closeEntry(); 386 is.close(); 387 388 for (Enumeration e = zipFile.entries(); e.hasMoreElements(); ) { 389 ZipEntry entryIn = (ZipEntry) e.nextElement(); 390 zos.putNextEntry(entryIn); 391 is = zipFile.getInputStream(entryIn); 392 while ((len = is.read(buf)) > 0) { 393 zos.write(buf, 0, len); 394 } 395 zos.closeEntry(); 396 is.close(); 397 } 398 399 zos.close(); 400 fout.close(); 401 zipFile.close(); 402 403 File oldFile = new File(firstJar); 404 File newFile = new File(replaceJar); 405 oldFile.delete(); 406 newFile.renameTo(oldFile); 407 System.out.println("firstJar = " + firstJar + " Modified"); 408 } else { 409 zipFile.close(); 410 System.out.println("firstJar = " + firstJar); 411 } 412 } 413 414 // Execute JVM using AppCDS archive with specified CDSOptions 415 public static OutputAnalyzer runWithArchive(CDSOptions opts) 416 throws Exception { 417 418 ArrayList<String> cmd = new ArrayList<String>(); 419 cmd.addAll(opts.prefix); 420 cmd.add("-Xshare:" + opts.xShareMode); 421 cmd.add("-showversion"); 422 cmd.add("-XX:SharedArchiveFile=" + getCurrentArchiveName()); 423 cmd.add("-Dtest.timeout.factor=" + timeoutFactor); 424 425 if (opts.appJar != null) { 426 cmd.add("-cp"); 427 cmd.add(opts.appJar); 428 } 429 430 CDSTestUtils.addVerifyArchivedFields(cmd); 431 432 for (String s : opts.suffix) cmd.add(s); 433 434 if (RUN_WITH_JFR) { 435 boolean usesJFR = false; 436 for (String s : cmd) { 437 if (s.startsWith("-XX:StartFlightRecording") || s.startsWith("-XX:FlightRecorderOptions")) { 438 System.out.println("JFR option might have been specified. Don't interfere: " + s); 439 usesJFR = true; 440 break; 441 } 442 } 443 if (!usesJFR) { 444 System.out.println("JFR option not specified. Enabling JFR ..."); 445 cmd.add(0, "-XX:StartFlightRecording:dumponexit=true"); 446 System.out.println(cmd); 447 } 448 } 449 450 ProcessBuilder pb = ProcessTools.createTestJavaProcessBuilder(cmd); 451 if (opts.appJarDir != null) { 452 pb.directory(new File(opts.appJarDir)); 453 } 454 return executeAndLog(pb, "exec"); 455 } 456 457 458 public static OutputAnalyzer execCommon(String... suffix) throws Exception { 459 CDSOptions opts = (new CDSOptions()); 460 opts.addSuffix(suffix); 461 return runWithArchive(opts); 462 } 463 464 // This is the new API for running a Java process with CDS enabled. 465 // See comments in the CDSTestUtils.Result class for how to use this method. 466 public static Result run(String... suffix) throws Exception { 467 CDSOptions opts = (new CDSOptions()); 468 opts.addSuffix(suffix); 469 return new Result(opts, runWithArchive(opts)); 470 } 471 472 public static Result runWithoutCDS(String... suffix) throws Exception { 473 CDSOptions opts = (new CDSOptions()); 474 opts.addSuffix(suffix).setXShareMode("off"); 475 return new Result(opts, runWithArchive(opts)); 476 } 477 478 public static Result runWithRelativePath(String jarDir, String... suffix) throws Exception { 479 CDSOptions opts = (new CDSOptions()); 480 opts.setAppJarDir(jarDir); 481 opts.addSuffix(suffix); 482 return new Result(opts, runWithArchive(opts)); 483 } 484 485 public static OutputAnalyzer exec(String appJar, String... suffix) throws Exception { 486 CDSOptions opts = (new CDSOptions()).setAppJar(appJar); 487 opts.addSuffix(suffix); 488 return runWithArchive(opts); 489 } 490 491 public static Result runWithModules(String prefix[], String upgrademodulepath, String modulepath, 492 String mid, String... testClassArgs) throws Exception { 493 CDSOptions opts = makeModuleOptions(prefix, upgrademodulepath, modulepath, 494 mid, testClassArgs); 495 return new Result(opts, runWithArchive(opts)); 496 } 497 498 public static OutputAnalyzer execAuto(String... suffix) throws Exception { 499 CDSOptions opts = (new CDSOptions()); 500 opts.addSuffix(suffix).setXShareMode("auto"); 501 return runWithArchive(opts); 502 } 503 504 public static OutputAnalyzer execOff(String... suffix) throws Exception { 505 CDSOptions opts = (new CDSOptions()); 506 opts.addSuffix(suffix).setXShareMode("off"); 507 return runWithArchive(opts); 508 } 509 510 511 private static CDSOptions makeModuleOptions(String prefix[], String upgrademodulepath, String modulepath, 512 String mid, String testClassArgs[]) { 513 CDSOptions opts = (new CDSOptions()); 514 515 opts.addPrefix(prefix); 516 if (upgrademodulepath == null) { 517 opts.addSuffix("-p", modulepath, "-m", mid); 518 } else { 519 opts.addSuffix("--upgrade-module-path", upgrademodulepath, 520 "-p", modulepath, "-m", mid); 521 } 522 opts.addSuffix(testClassArgs); 523 return opts; 524 } 525 526 public static OutputAnalyzer execModule(String prefix[], String upgrademodulepath, String modulepath, 527 String mid, String... testClassArgs) 528 throws Exception { 529 CDSOptions opts = makeModuleOptions(prefix, upgrademodulepath, modulepath, 530 mid, testClassArgs); 531 return runWithArchive(opts); 532 } 533 534 // A common operation: dump, then check results 535 public static OutputAnalyzer testDump(String appJar, String classList[], 536 String... suffix) throws Exception { 537 OutputAnalyzer output = dump(appJar, classList, suffix); 538 if (DYNAMIC_DUMP) { 539 output.shouldContain("Written dynamic archive"); 540 } else { 541 output.shouldContain("Loading classes to share"); 542 } 543 output.shouldHaveExitValue(0); 544 return output; 545 } 546 547 public static OutputAnalyzer testDump(String appJarDir, String appJar, String classList[], 548 String... suffix) throws Exception { 549 OutputAnalyzer output = dump(appJarDir, appJar, classList, suffix); 550 if (DYNAMIC_DUMP) { 551 output.shouldContain("Written dynamic archive"); 552 } else { 553 output.shouldContain("Loading classes to share"); 554 } 555 output.shouldHaveExitValue(0); 556 return output; 557 } 558 559 /** 560 * Simple test -- dump and execute appJar with the given classList in classlist. 561 */ 562 public static OutputAnalyzer test(String appJar, String classList[], String... args) 563 throws Exception { 564 testDump(appJar, classList); 565 566 OutputAnalyzer output = exec(appJar, args); 567 return checkExec(output); 568 } 569 570 571 public static OutputAnalyzer checkExecReturn(OutputAnalyzer output, int ret, 572 boolean checkContain, String... matches) throws Exception { 573 try { 574 for (String s : matches) { 575 if (checkContain) { 576 output.shouldContain(s); 577 } else { 578 output.shouldNotContain(s); 579 } 580 } 581 output.shouldHaveExitValue(ret); 582 } catch (Exception e) { 583 checkCommonExecExceptions(output, e); 584 } 585 586 return output; 587 } 588 589 // Convenience concatenation utils 590 public static String[] list(String ...args) { 591 return args; 592 } 593 594 595 public static String[] list(String arg, int count) { 596 ArrayList<String> stringList = new ArrayList<String>(); 597 for (int i = 0; i < count; i++) { 598 stringList.add(arg); 599 } 600 601 String outputArray[] = stringList.toArray(new String[stringList.size()]); 602 return outputArray; 603 } 604 605 606 public static String[] concat(String... args) { 607 return list(args); 608 } 609 610 611 public static String[] concat(String prefix[], String... extra) { 612 ArrayList<String> list = new ArrayList<String>(); 613 for (String s : prefix) { 614 list.add(s); 615 } 616 for (String s : extra) { 617 list.add(s); 618 } 619 620 return list.toArray(new String[list.size()]); 621 } 622 623 public static String[] concat(String prefix, String[] extra) { 624 ArrayList<String> list = new ArrayList<String>(); 625 list.add(prefix); 626 for (String s : extra) { 627 list.add(s); 628 } 629 630 return list.toArray(new String[list.size()]); 631 } 632 633 // ===================== Concatenate paths 634 public static String concatPaths(String... paths) { 635 String prefix = ""; 636 String s = ""; 637 for (String p : paths) { 638 s += prefix; 639 s += p; 640 prefix = File.pathSeparator; 641 } 642 return s; 643 } 644 645 646 public static String getTestJar(String jar) { 647 File jarFile = CDSTestUtils.getTestArtifact(jar, true); 648 if (!jarFile.isFile()) { 649 throw new RuntimeException("Not a regular file: " + jarFile.getPath()); 650 } 651 return jarFile.getPath(); 652 } 653 654 655 public static String getTestDir(String d) { 656 File dirFile = CDSTestUtils.getTestArtifact(d, true); 657 if (!dirFile.isDirectory()) { 658 throw new RuntimeException("Not a directory: " + dirFile.getPath()); 659 } 660 return dirFile.getPath(); 661 } 662 663 public static boolean checkOutputStrings(String outputString1, 664 String outputString2, 665 String split_regex) { 666 String[] sa1 = outputString1.split(split_regex); 667 String[] sa2 = outputString2.split(split_regex); 668 Arrays.sort(sa1); 669 Arrays.sort(sa2); 670 671 int i = 0; 672 for (String s : sa1) { 673 if (!s.equals(sa2[i])) { 674 throw new RuntimeException(s + " is different from " + sa2[i]); 675 } 676 i ++; 677 } 678 return true; 679 } 680 681 static void findAllClasses(ArrayList<String> list) throws Exception { 682 // Find all the classes in the jrt file system 683 Pattern pattern = Pattern.compile("/modules/[a-z.]*[a-z]+/([^-]*)[.]class"); 684 FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); 685 Path base = fs.getPath("/modules/"); 686 findAllClassesAtPath(base, pattern, list); 687 } 688 689 private static void findAllClassesAtPath(Path p, Pattern pattern, ArrayList<String> list) throws Exception { 690 try (DirectoryStream<Path> stream = Files.newDirectoryStream(p)) { 691 for (Path entry: stream) { 692 if (Files.isDirectory(entry)) { 693 findAllClassesAtPath(entry, pattern, list); 694 } else { 695 Matcher matcher = pattern.matcher(entry.toString()); 696 if (matcher.find()) { 697 String className = matcher.group(1); 698 list.add(className); 699 } 700 } 701 } 702 } 703 } 704 705 public static String composeRelPath(String appJar) { 706 int idx = appJar.lastIndexOf(File.separator); 707 String jarName = appJar.substring(idx + 1); 708 String jarDir = appJar.substring(0, idx); 709 String lastDir = jarDir.substring(jarDir.lastIndexOf(File.separator)); 710 String relPath = jarDir + File.separator + ".." + File.separator + lastDir; 711 String newJar = relPath + File.separator + jarName; 712 return newJar; 713 } 714 715 716 public static File createSymLink(String appJar) throws Exception { 717 int idx = appJar.lastIndexOf(File.separator); 718 String jarName = appJar.substring(idx + 1); 719 String jarDir = appJar.substring(0, idx); 720 File origJar = new File(jarDir, jarName); 721 String linkedJarName = "linked_" + jarName; 722 File linkedJar = null; 723 if (!Platform.isWindows()) { 724 linkedJar = new File(jarDir, linkedJarName); 725 if (linkedJar.exists()) { 726 linkedJar.delete(); 727 } 728 Files.createSymbolicLink(linkedJar.toPath(), origJar.toPath()); 729 } 730 return linkedJar; 731 } 732 733 // Remove all UL log messages from a JVM's STDOUT (such as those printed by -Xlog:cds) 734 static Pattern logPattern = Pattern.compile("^\\[[0-9. ]*s\\].*"); 735 public static String filterOutLogs(String stdout) { 736 StringBuilder sb = new StringBuilder(); 737 String prefix = ""; 738 for (String line : stdout.split("\n")) { 739 if (logPattern.matcher(line).matches()) { 740 continue; 741 } 742 sb.append(prefix); 743 sb.append(line); 744 prefix = "\n"; 745 } 746 if (stdout.endsWith("\n")) { 747 // String.split("A\n") returns {"A"}, not {"A", ""}. 748 sb.append("\n"); 749 } 750 return sb.toString(); 751 } 752 753 public static void filesMustMatch(Path a, Path b) throws IOException { 754 linesMustMatch(Files.readString(a).split("\n"), 755 Files.readString(b).split("\n")); 756 } 757 758 public static void linesMustMatch(String a[], String b[]) { 759 int limit = Math.min(a.length, b.length); 760 761 // Check the lines that are in both a[] and b[] 762 for (int i = 0; i < limit; i++) { 763 if (!a[i].equals(b[i])) { 764 System.out.println("a:" + i + " " + a[i]); 765 System.out.println("b:" + i + " " + b[i]); 766 throw new RuntimeException("Output mismatch on line " + i 767 + ": a=" + a[i] 768 + ", b=" + b[i]); 769 } 770 } 771 772 // Report the first line that is in one array but not in the other 773 if (a.length > b.length) { 774 throw new RuntimeException("Output mismatch on line " + limit 775 + ": a=" + a[limit] 776 + ", b=<none>"); 777 778 } 779 if (a.length < b.length) { 780 throw new RuntimeException("Output mismatch on line " + limit 781 + ": a=<none>" 782 + ", b=" + b[limit]); 783 } 784 } 785 }