1 /*
2 * Copyright (c) 2021, 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 compiler.lib.ir_framework;
25
26 import compiler.lib.ir_framework.driver.FlagVMProcess;
27 import compiler.lib.ir_framework.driver.TestVMException;
28 import compiler.lib.ir_framework.driver.TestVMProcess;
29 import compiler.lib.ir_framework.driver.irmatching.IRMatcher;
30 import compiler.lib.ir_framework.driver.irmatching.IRViolationException;
31 import compiler.lib.ir_framework.driver.irmatching.Matchable;
32 import compiler.lib.ir_framework.driver.irmatching.parser.TestClassParser;
33 import compiler.lib.ir_framework.shared.*;
34 import compiler.lib.ir_framework.test.TestVM;
35 import jdk.test.lib.Platform;
36 import jdk.test.lib.Utils;
37 import jdk.test.lib.helpers.ClassFileInstaller;
38 import jdk.test.whitebox.WhiteBox;
39 import jtreg.SkippedException;
40
41 import java.io.PrintWriter;
42 import java.io.StringWriter;
43 import java.lang.reflect.Method;
44 import java.net.MalformedURLException;
45 import java.net.URL;
46 import java.net.URLClassLoader;
47 import java.nio.file.Path;
48 import java.util.*;
49 import java.util.concurrent.atomic.AtomicInteger;
50 import java.util.stream.Collectors;
51 import java.util.stream.Stream;
52
53 /**
54 * This class represents the main entry point to the test framework whose main purpose is to perform regex-based checks on
55 * the C2 IR shape emitted by the VM flags {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly}. The framework can
56 * also be used for other non-IR matching (and non-compiler) tests by providing easy to use annotations for commonly used
57 * testing patterns and compiler control flags.
58 * <p>
59 * The framework offers various annotations to control how your test code should be invoked and being checked. There are
60 * three kinds of tests depending on how much control is needed over the test invocation:
61 * <b>Base tests</b> (see {@link Test}), <b>checked tests</b> (see {@link Check}), and <b>custom run tests</b>
62 * (see {@link Run}). Each type of test needs to define a unique <i>test method</i> that specifies a {@link Test @Test}
63 * annotation which represents the test code that is eventually executed by the test framework. More information about
64 * the usage and how to write different tests can be found in {@link Test}, {@link Check}, and {@link Run}.
65 * <p>
66 * Each test method can specify an arbitrary number of IR rules. This is done by using {@link IR @IR} annotations which
67 * can define regex strings that are matched on the output of {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly}.
68 * The matching is done after the test method was (optionally) warmed up and compiled. More information about the usage
69 * and how to write different IR rules can be found at {@link IR}.
70 * <p>
71 * This framework should be used with the following JTreg setup in your Test.java file in package <i>some.package</i>:
72 * <pre>
73 * {@literal @}library /test/lib
74 * {@literal @}run driver some.package.Test
75 * </pre>
76 * Note that even though the framework uses the Whitebox API internally, it is not required to build and enabel it in the
77 * JTreg test if the test itself is not utilizing any Whitebox features directly.
78 * <p>
79 * To specify additional flags, use {@link #runWithFlags(String...)}, {@link #addFlags(String...)}, or
80 * {@link #addScenarios(Scenario...)} where the scenarios can also be used to run different flag combinations
81 * (instead of specifying multiple JTreg {@code @run} entries).
82 * <p>
83 * After annotating your test code with the framework specific annotations, the framework needs to be invoked from the
84 * {@code main()} method of your JTreg test. There are two ways to do so. The first way is by calling the various
85 * {@code runXX()} methods of {@link TestFramework}. The second way, which gives more control, is to create a new
86 * {@code TestFramework} builder object on which {@link #start()} needs to be eventually called to start the testing.
87 * <p>
88 * The framework is called from the <i>driver VM</i> in which the JTreg test is initially run by specifying {@code
89 * @run driver} in the JTreg header. This strips all additionally specified JTreg VM and Javaoptions.
90 * The framework creates a new <i>flag VM</i> with all these flags added again in order to figure out which flags are
91 * required to run the tests specified in the test class (e.g. {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly}
92 * for IR matching).
93 * <p>
94 * After the flag VM terminates, it starts a new <i>test VM</i> which performs the execution of the specified
95 * tests in the test class as described in {@link Test}, {@link Check}, and {@link Run}.
96 * <p>
97 * In a last step, once the test VM has terminated without exceptions, IR matching is performed if there are any IR
98 * rules and if no VM flags disable it (e.g. not running with {@code -Xint}, see {@link IR} for more details).
99 * The IR regex matching is done on the output of {@code -XX:+PrintIdeal} and {@code -XX:+PrintOptoAssembly} by parsing
100 * the hotspot_pid file of the test VM. Failing IR rules are reported by throwing a {@link IRViolationException}.
101 *
102 * @see Test
103 * @see Check
104 * @see Run
105 * @see IR
106 */
107 public class TestFramework {
108 /**
109 * JTreg can define additional VM (-Dtest.vm.opts) and Javaoptions (-Dtest.java.opts) flags. IR verification is only
110 * performed when all these additional JTreg flags (does not include additionally added framework and scenario flags
111 * by user code) are whitelisted.
112 *
113 * <p>
114 * A flag is whitelisted if it is a property flag (starting with -D), -ea, -esa, or if the flag name contains any of
115 * the entries of this list as a substring (partial match).
116 */
117 public static final Set<String> JTREG_WHITELIST_FLAGS = new HashSet<>(
118 Arrays.asList(
119 // The following substrings are part of more than one VM flag
120 "RAM",
121 "Heap",
122 "Trace",
123 "Print",
124 "Verify",
125 "UseNewCode",
126 "Xmn",
127 "Xms",
128 "Xmx",
129 "Xss",
130 // The following substrings are only part of one VM flag (=exact match)
131 "CreateCoredumpOnCrash",
132 "IgnoreUnrecognizedVMOptions",
133 "UnlockDiagnosticVMOptions",
134 "UnlockExperimentalVMOptions",
135 "BackgroundCompilation",
136 "Xbatch",
137 "TieredCompilation",
138 "CompileThreshold",
139 "Xmixed",
140 "server",
141 "AlignVector",
142 "UseAVX",
143 "UseSSE",
144 "UseSVE",
145 "Xlog",
146 "LogCompilation",
147 "UseCompactObjectHeaders",
148 "UseFMA",
149 // Riscv
150 "UseRVV",
151 "UseZbb",
152 "UseZfh",
153 "UseZicond",
154 "UseZvbb"
155 )
156 );
157
158 public static final boolean VERBOSE = Boolean.getBoolean("Verbose");
159 public static final boolean PRINT_RULE_MATCHING_TIME = Boolean.getBoolean("PrintRuleMatchingTime");
160 public static final boolean TESTLIST = !System.getProperty("Test", "").isEmpty();
161 public static final boolean EXCLUDELIST = !System.getProperty("Exclude", "").isEmpty();
162 private static final boolean REPORT_STDOUT = Boolean.getBoolean("ReportStdout");
163 // Only used for internal testing and should not be used for normal user testing.
164
165 private static final String RERUN_HINT = """
166 #############################################################
167 - To only run the failed tests use -DTest, -DExclude,
168 and/or -DScenarios.
169 - To also get the standard output of the test VM run with
170 -DReportStdout=true or for even more fine-grained logging
171 use -DVerbose=true.
172 #############################################################
173 """ + System.lineSeparator();
174
175 private boolean irVerificationPossible = Boolean.parseBoolean(System.getProperty("VerifyIR", "true"));
176 private boolean shouldVerifyIR; // Should we perform IR matching?
177 private static boolean toggleBool;
178
179 private final Class<?> testClass;
180 private Set<Class<?>> helperClasses;
181 private List<Scenario> scenarios;
182 private Set<Integer> scenarioIndices;
183 private List<String> flags;
184 private int defaultWarmup = -1;
185 private boolean testClassesOnBootClassPath;
186 private boolean isAllowNotCompilable = false;
187
188 /*
189 * Public interface methods
190 */
191
192 /**
193 * Creates an instance acting as a builder to test the class from which this constructor was invoked from.
194 * Use this constructor if you want to use multiple run options (flags, helper classes, scenarios).
195 * Use the associated add methods ({@link #addFlags(String...)}, {@link #addScenarios(Scenario...)},
196 * {@link #addHelperClasses(Class...)}) to set up everything and then start the testing by invoking {@link #start()}.
197 */
198 public TestFramework() {
199 this(StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE).getCallerClass());
200 }
201
202 /**
203 * Creates an instance acting as a builder to test {@code testClass}.
204 * Use this constructor if you want to use multiple run options (flags, helper classes, scenarios).
205 * Use the associated add methods ({@link #addFlags(String...)}, {@link #addScenarios(Scenario...)},
206 * {@link #addHelperClasses(Class...)}) to set up everything and then start the testing by invoking {@link #start()}.
207 *
208 * @param testClass the class to be tested by the framework.
209 * @see #TestFramework()
210 */
211 public TestFramework(Class<?> testClass) {
212 TestRun.check(testClass != null, "Test class cannot be null");
213 this.testClass = testClass;
214 if (VERBOSE) {
215 System.out.println("Test class: " + testClass);
216 }
217 }
218
219 /**
220 * Tests the class from which this method was invoked from.
221 */
222 public static void run() {
223 StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
224 run(walker.getCallerClass());
225 }
226
227 /**
228 * Tests {@code testClass}.
229 *
230 * @param testClass the class to be tested by the framework.
231 * @see #run()
232 */
233 public static void run(Class<?> testClass) {
234 TestFramework framework = new TestFramework(testClass);
235 framework.start();
236 }
237
238 /**
239 * Tests the class from which this method was invoked from. The test VM is called with the specified {@code flags}.
240 * <ul>
241 * <li><p>The {@code flags} override any set VM or Javaoptions flags by JTreg by default.<p>
242 * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the JTreg VM and Javaoptions flags over
243 * the specified {@code flags} of this method.</li>
244 * <li><p>If you want to run your entire JTreg test with additional flags, use this method.</li>
245 * <li><p>If you want to run your entire JTreg test with additional flags but for another test class then the one
246 * from which this method was called from, use {@link #addFlags(String...)}, use this method.</li>
247 * <li><p>If you want to run your JTreg test with multiple flag combinations, use
248 * {@link #addScenarios(Scenario...)}</li>
249 * </ul>
250 *
251 * @param flags VM flags to be used for the test VM.
252 */
253 public static void runWithFlags(String... flags) {
254 StackWalker walker = StackWalker.getInstance(StackWalker.Option.RETAIN_CLASS_REFERENCE);
255 TestFramework framework = new TestFramework(walker.getCallerClass());
256 framework.addFlags(flags);
257 framework.start();
258 }
259
260 /**
261 * Add VM flags to be used for the test VM. These flags override any VM or Javaoptions set by JTreg by default.<p>
262 * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the VM or Javaoptions over the scenario flags.
263 *
264 * <p>
265 * The testing can be started by invoking {@link #start()}
266 *
267 * @param flags VM options to be applied to the test VM.
268 * @return the same framework instance.
269 */
270 public TestFramework addFlags(String... flags) {
271 TestRun.check(flags != null && Arrays.stream(flags).noneMatch(Objects::isNull), "A flag cannot be null");
272 if (this.flags == null) {
273 this.flags = new ArrayList<>();
274 }
275 this.flags.addAll(Arrays.asList(flags));
276 return this;
277 }
278
279 /**
280 * Add helper classes that can specify additional compile command annotations ({@link ForceCompile @ForceCompile},
281 * {@link DontCompile @DontCompile}, {@link ForceInline @ForceInline}, {@link DontInline @DontInline}) to be applied
282 * while testing {@code testClass} (also see description of {@link TestFramework}).
283 *
284 * <p>
285 * Duplicates in {@code helperClasses} are ignored. If a class is used by the test class that does not specify any
286 * compile command annotations, you do not need to include it with this method. If no helper class specifies any
287 * compile commands, you do not need to call this method at all.
288 *
289 * <p>
290 * The testing can be started by invoking {@link #start()}.
291 *
292 * @param helperClasses helper classes containing compile command annotations ({@link ForceCompile},
293 * {@link DontCompile}, {@link ForceInline}, {@link DontInline}) to be applied
294 * while testing {@code testClass} (also see description of {@link TestFramework}).
295 * @return the same framework instance.
296 */
297 public TestFramework addHelperClasses(Class<?>... helperClasses) {
298 TestRun.check(helperClasses != null && Arrays.stream(helperClasses).noneMatch(Objects::isNull),
299 "A Helper class cannot be null");
300 if (this.helperClasses == null) {
301 this.helperClasses = new HashSet<>();
302 }
303
304 this.helperClasses.addAll(Arrays.asList(helperClasses));
305 return this;
306 }
307
308 /**
309 * Add scenarios to be used for the test VM. A test VM is called for each scenario in {@code scenarios} by using the
310 * specified VM flags in the scenario. The scenario flags override any flags set by {@link #addFlags(String...)}
311 * and thus also override any VM or Javaoptions set by JTreg by default.<p>
312 * Use {@code -DPreferCommandLineFlags=true} if you want to prefer the VM and Javaoptions over the scenario flags.
313 *
314 * <p>
315 * The testing can be started by invoking {@link #start()}
316 *
317 * @param scenarios scenarios which specify specific flags for the test VM.
318 * @return the same framework instance.
319 */
320 public TestFramework addScenarios(Scenario... scenarios) {
321 TestFormat.checkAndReport(scenarios != null && Arrays.stream(scenarios).noneMatch(Objects::isNull),
322 "A scenario cannot be null");
323 if (this.scenarios == null) {
324 this.scenarios = new ArrayList<>();
325 this.scenarioIndices = new HashSet<>();
326 }
327
328 for (Scenario scenario : scenarios) {
329 int scenarioIndex = scenario.getIndex();
330 if (!scenarioIndices.add(scenarioIndex)) {
331 TestFormat.failNoThrow("Cannot define two scenarios with the same index " + scenarioIndex);
332 continue;
333 }
334 this.scenarios.add(scenario);
335 }
336 TestFormat.throwIfAnyFailures();
337 return this;
338 }
339
340 /**
341 * Add the cross-product (cartesian product) of sets of flags as Scenarios. Unlike when constructing
342 * scenarios directly a string can contain multiple flags separated with a space. This allows grouping
343 * flags that have to be specified together. Further, an empty string in a set stands in for "no flag".
344 * <p>
345 * Passing a single set will create a scenario for each of the provided flags in the set (i.e. the same as
346 * passing an additional set with an empty string only).
347 * <p>
348 * Example:
349 * <pre>
350 * addCrossProductScenarios(Set.of("", "-Xint", "-Xbatch -XX:-TieredCompilation"),
351 * Set.of("-XX:+UseNewCode", "-XX:UseNewCode2"))
352 * </pre>
353 * produces the following Scenarios
354 * <pre>
355 * Scenario(0, "-XX:+UseNewCode")
356 * Scenario(1, "-XX:+UseNewCode2")
357 * Scenario(2, "-Xint", "-XX:+UseNewCode")
358 * Scenario(3, "-Xint", "-XX:+UseNewCode2")
359 * Scenario(4, "-Xbatch -XX:-TieredCompilation", "-XX:+UseNewCode")
360 * Scenario(5, "-Xbatch -XX:-TieredCompilation", "-XX:+UseNewCode2")
361 * </pre>
362 *
363 * @param flagSets sets of flags to generate the cross product for.
364 * @return the same framework instance.
365 */
366 @SafeVarargs
367 final public TestFramework addCrossProductScenarios(Set<String>... flagSets) {
368 TestFormat.checkAndReport(flagSets != null &&
369 Arrays.stream(flagSets).noneMatch(Objects::isNull) &&
370 Arrays.stream(flagSets).flatMap(Set::stream).noneMatch(Objects::isNull),
371 "Flags must not be null");
372 if (flagSets.length == 0) {
373 return this;
374 }
375
376 int initIdx = 0;
377 if (this.scenarioIndices != null && !this.scenarioIndices.isEmpty()) {
378 initIdx = this.scenarioIndices.stream().max(Comparator.comparingInt(Integer::intValue)).get() + 1;
379 }
380 AtomicInteger idx = new AtomicInteger(initIdx);
381
382 Stream<List<String>> crossProduct = Arrays.stream(flagSets)
383 .reduce(
384 Stream.of(Collections.emptyList()), // Initialize Stream<List<String>> acc with a Stream containing an empty list of Strings.
385 (Stream<List<String>> acc, Set<String> set) ->
386 acc.flatMap(lAcc -> // For each List<String>> lAcc in acc...
387 set.stream().map(flag -> { // ...and each flag in the current set...
388 List<String> newList = new ArrayList<>(lAcc); // ...create a new list containing lAcc...
389 newList.add(flag); // ...and append the flag.
390 return newList;
391 }) // This results in one List<List<String>> for each lAcc...
392 ), // ...that get flattened into one big List<List<String>>.
393 Stream::concat); // combiner; if any reduction steps are executed in parallel, just concat two streams.
394
395 Scenario[] newScenarios = crossProduct
396 .map(flags -> new Scenario( // For each List<String> flags in crossProduct create a new Scenario.
397 idx.getAndIncrement(),
398 flags.stream() // Process flags
399 .map(s -> Set.of(s.split("[ ]"))) // Split multiple flags in the same string into separate strings.
400 .flatMap(Collection::stream) // Flatten the Stream<List<String>> into Stream<String>>.
401 .filter(s -> !s.isEmpty()) // Remove empty string flags.
402 .toList()
403 .toArray(new String[0])))
404 .toList().toArray(new Scenario[0]);
405 return addScenarios(newScenarios);
406 }
407
408 /**
409 * Add test classes to boot classpath. This adds all classes found on path {@link jdk.test.lib.Utils#TEST_CLASSES}
410 * to the boot classpath with "-Xbootclasspath/a". This is useful when trying to run tests in a privileged mode.
411 */
412 public TestFramework addTestClassesToBootClassPath() {
413 this.testClassesOnBootClassPath = true;
414 return this;
415 }
416
417 /**
418 * Start the testing of the implicitly (by {@link #TestFramework()}) or explicitly (by {@link #TestFramework(Class)})
419 * set test class.
420 */
421 public void start() {
422 if (shouldInstallWhiteBox()) {
423 installWhiteBox();
424 }
425 checkCompatibleFlags();
426 checkIRRuleCompilePhasesFormat();
427 disableIRVerificationIfNotFeasible();
428
429 if (scenarios == null) {
430 try {
431 start(null);
432 } catch (TestVMException e) {
433 System.err.println(System.lineSeparator() + e.getExceptionInfo() + RERUN_HINT);
434 throw e;
435 } catch (IRViolationException e) {
436 System.out.println(e.getCompilations());
437 System.err.println(System.lineSeparator() + e.getExceptionInfo() + System.lineSeparator() + RERUN_HINT);
438 throw e;
439 }
440 } else {
441 startWithScenarios();
442 }
443 }
444
445 private void checkIRRuleCompilePhasesFormat() {
446 for (Method method : testClass.getDeclaredMethods()) {
447 for (IR irAnno : method.getAnnotationsByType(IR.class)) {
448 TestFormat.checkNoThrow(irAnno.phase().length > 0,
449 "@IR rule " + irAnno + " must specify a non-empty list of compile " +
450 "phases \"phase\" at " + method);
451 }
452 }
453 TestFormat.throwIfAnyFailures();
454 }
455
456 /**
457 * Try to load the Whitebox class from the user directory with a custom class loader. If the user has already built the
458 * Whitebox, we can load it. Otherwise, the framework needs to install it.
459 *
460 * @return true if the framework needs to install the Whitebox
461 */
462 private boolean shouldInstallWhiteBox() {
463 try {
464 URL url = Path.of(System.getProperty("user.dir")).toUri().toURL();
465 URLClassLoader userDirClassLoader =
466 URLClassLoader.newInstance(new URL[] {url}, TestFramework.class.getClassLoader().getParent());
467 Class.forName(WhiteBox.class.getName(), false, userDirClassLoader);
468 } catch (MalformedURLException e) {
469 throw new TestFrameworkException("corrupted user.dir property", e);
470 } catch (ClassNotFoundException e) {
471 // We need to manually install the WhiteBox if we cannot load the WhiteBox class from the user directory.
472 // This happens when the user test does not explicitly install the WhiteBox as part of the test.
473 return true;
474 }
475 return false;
476 }
477
478 /**
479 * Set a new default warm-up (overriding the framework default of 2000 at
480 * {@link TestVM#WARMUP_ITERATIONS}) to be applied for all tests that do not specify an explicit
481 * warm-up with {@link Warmup @Warmup}.
482 *
483 * @param defaultWarmup a new non-negative default warm-up.
484 * @return the same framework instance.
485 */
486 public TestFramework setDefaultWarmup(int defaultWarmup) {
487 TestFormat.checkAndReport(defaultWarmup >= 0, "Cannot specify a negative default warm-up");
488 this.defaultWarmup = defaultWarmup;
489 return this;
490 }
491
492 /**
493 * In rare cases, methods may not be compilable because of a compilation bailout. By default, this leads to a
494 * test failure. However, if such cases are expected in multiple methods in a test class, this flag can be set to
495 * true, which allows any test to pass even if there is a compilation bailout. If only selected methods are prone
496 * to bail out, it is preferred to use {@link Test#allowNotCompilable()} instead for more fine-grained control.
497 * By setting this flag, any associated {@link IR} rule of a test is only executed if the test method was compiled,
498 * and else it is ignored silently.
499 */
500 public TestFramework allowNotCompilable() {
501 this.isAllowNotCompilable = true;
502 return this;
503 }
504
505 /**
506 * Get the VM output of the test VM. Use {@code -DVerbose=true} to enable more debug information. If scenarios
507 * were run, use {@link Scenario#getTestVMOutput()}.
508 *
509 * @return the last test VM output.
510 */
511 public static String getLastTestVMOutput() {
512 return TestVMProcess.getLastTestVMOutput();
513 }
514
515 /*
516 * The following methods are only intended to be called from actual @Test methods and not from the main() method of
517 * a JTreg test. Calling these methods from main() results in a linking exception (Whitebox not yet loaded and enabled).
518 */
519
520 /**
521 * Compile {@code m} at compilation level {@code compLevel}. {@code m} is first enqueued and might not be compiled,
522 * yet, upon returning from this method.
523 *
524 * @param m the method to be compiled.
525 * @param compLevel the (valid) compilation level at which the method should be compiled.
526 * @throws TestRunException if compilation level is {@link CompLevel#SKIP} or {@link CompLevel#WAIT_FOR_COMPILATION}.
527 */
528 public static void compile(Method m, CompLevel compLevel) {
529 TestVM.compile(m, compLevel);
530 }
531
532 /**
533 * Deoptimize {@code m}.
534 *
535 * @param m the method to be deoptimized.
536 */
537 public static void deoptimize(Method m) {
538 TestVM.deoptimize(m);
539 }
540
541 /**
542 * Returns a boolean indicating if {@code m} is compiled at any level.
543 *
544 * @param m the method to be checked.
545 * @return {@code true} if {@code m} is compiled at any level;
546 * {@code false} otherwise.
547 */
548 public static boolean isCompiled(Method m) {
549 return TestVM.isCompiled(m);
550 }
551
552 /**
553 * Returns a boolean indicating if {@code m} is compiled with C1.
554 *
555 * @param m the method to be checked.
556 * @return {@code true} if {@code m} is compiled with C1;
557 * {@code false} otherwise.
558 */
559 public static boolean isC1Compiled(Method m) {
560 return TestVM.isC1Compiled(m);
561 }
562
563 /**
564 * Returns a boolean indicating if {@code m} is compiled with C2.
565 *
566 * @param m the method to be checked.
567 * @return {@code true} if {@code m} is compiled with C2;
568 * {@code false} otherwise.
569 */
570 public static boolean isC2Compiled(Method m) {
571 return TestVM.isC2Compiled(m);
572 }
573
574 /**
575 * Returns a boolean indicating if {@code m} is compiled at the specified {@code compLevel}.
576 *
577 * @param m the method to be checked.
578 * @param compLevel the compilation level.
579 * @return {@code true} if {@code m} is compiled at {@code compLevel};
580 * {@code false} otherwise.
581 */
582 public static boolean isCompiledAtLevel(Method m, CompLevel compLevel) {
583 return TestVM.isCompiledAtLevel(m, compLevel);
584 }
585
586 /**
587 * Checks if {@code m} is compiled at any level.
588 *
589 * @param m the method to be checked.
590 * @throws TestRunException if {@code m} is not compiled at any level.
591 */
592 public static void assertCompiled(Method m) {
593 TestVM.assertCompiled(m);
594 }
595
596 /**
597 * Checks if {@code m} is not compiled at any level.
598 *
599 * @param m the method to be checked.
600 * @throws TestRunException if {@code m} is compiled at any level.
601 */
602 public static void assertNotCompiled(Method m) {
603 TestVM.assertNotCompiled(m);
604 }
605
606 /**
607 * Verifies that {@code m} is compiled with C1.
608 *
609 * @param m the method to be verified.
610 * @throws TestRunException if {@code m} is not compiled with C1.
611 */
612 public static void assertCompiledByC1(Method m) {
613 TestVM.assertCompiledByC1(m);
614 }
615
616 /**
617 * Verifies that {@code m} is compiled with C2.
618 *
619 * @param m the method to be checked.
620 * @throws TestRunException if {@code m} is not compiled with C2.
621 */
622 public static void assertCompiledByC2(Method m) {
623 TestVM.assertCompiledByC2(m);
624 }
625
626 /**
627 * Verifies that {@code m} is compiled at the specified {@code compLevel}.
628 *
629 * @param m the method to be checked.
630 * @param compLevel the compilation level.
631 * @throws TestRunException if {@code m} is not compiled at {@code compLevel}.
632 */
633 public static void assertCompiledAtLevel(Method m, CompLevel compLevel) {
634 TestVM.assertCompiledAtLevel(m, compLevel);
635 }
636
637 /**
638 * Verifies that {@code m} was deoptimized after being C1 compiled.
639 *
640 * @param m the method to be checked.
641 * @throws TestRunException if {@code m} is was not deoptimized after being C1 compiled.
642 */
643 public static void assertDeoptimizedByC1(Method m) {
644 TestVM.assertDeoptimizedByC1(m);
645 }
646
647 /**
648 * Verifies that {@code m} was deoptimized after being C2 compiled.
649 *
650 * @param m the method to be checked.
651 * @throws TestRunException if {@code m} is was not deoptimized after being C2 compiled.
652 */
653 public static void assertDeoptimizedByC2(Method m) {
654 TestVM.assertDeoptimizedByC2(m);
655 }
656
657 /**
658 * Returns a different boolean each time this method is invoked (switching between {@code false} and {@code true}).
659 * The very first invocation returns {@code false}. Note that this method could be used by different tests and
660 * thus the first invocation for a test could be {@code true} or {@code false} depending on how many times
661 * other tests have already invoked this method.
662 *
663 * @return an inverted boolean of the result of the last invocation of this method.
664 */
665 public static boolean toggleBoolean() {
666 toggleBool = !toggleBool;
667 return toggleBool;
668 }
669
670 /*
671 * End of public interface methods
672 */
673
674 /**
675 * Used to move Whitebox class to the right folder in the JTreg test
676 */
677 private void installWhiteBox() {
678 try {
679 ClassFileInstaller.main(WhiteBox.class.getName());
680 } catch (Exception e) {
681 throw new Error("failed to install whitebox classes", e);
682 }
683 }
684
685 /**
686 * Disable IR verification completely in certain cases.
687 */
688 private void disableIRVerificationIfNotFeasible() {
689 if (!irVerificationPossible) {
690 return;
691 }
692
693 boolean debugTest = Platform.isDebugBuild();
694 boolean intTest = !Platform.isInt();
695 boolean compTest = !Platform.isComp();
696 boolean irTest = hasIRAnnotations();
697 // No IR verification is done if additional non-whitelisted JTreg VM or Javaoptions flag is specified.
698 List<String> nonWhiteListedFlags = anyNonWhitelistedJTregVMAndJavaOptsFlags();
699 boolean nonWhiteListedTest = nonWhiteListedFlags.isEmpty();
700
701 irVerificationPossible = debugTest && intTest && compTest && irTest && nonWhiteListedTest;
702 if (irVerificationPossible) {
703 return;
704 }
705
706 System.out.println("IR verification disabled due to the following reason(s):");
707 if (!debugTest) {
708 System.out.println("- Not running a debug build (required for PrintIdeal and PrintOptoAssembly)");
709 }
710 if (!intTest) {
711 System.out.println("- Running with -Xint (no compilations)");
712 }
713 if (!compTest) {
714 System.out.println("- Running with -Xcomp (use warm-up of 0 instead)");
715 }
716 if (!irTest) {
717 System.out.println("- Test " + testClass + " not specifying any @IR annotations");
718 }
719 if (!nonWhiteListedTest) {
720 System.out.println("- Using non-whitelisted JTreg VM or Javaoptions flag(s):");
721 nonWhiteListedFlags.forEach((f) -> System.out.println(" - " + f));
722 }
723
724 System.out.println("");
725 }
726
727 /**
728 * For scenarios: Run the tests with the scenario settings and collect all exceptions to be able to run all
729 * scenarios without prematurely throwing an exception. Format violations, however, are wrong for all scenarios
730 * and thus is reported immediately on the first scenario execution.
731 */
732 private void startWithScenarios() {
733 Map<Scenario, Exception> exceptionMap = new TreeMap<>(Comparator.comparingInt(Scenario::getIndex));
734 for (Scenario scenario : scenarios) {
735 try {
736 start(scenario);
737 } catch (TestFormatException e) {
738 // Test format violation is wrong for all the scenarios. Only report once.
739 throw e;
740 } catch (Exception e) {
741 exceptionMap.put(scenario, e);
742 }
743 }
744 if (!exceptionMap.isEmpty()) {
745 reportScenarioFailures(exceptionMap);
746 }
747 }
748
749 private void reportScenarioFailures(Map<Scenario, Exception> exceptionMap) {
750 String failedScenarios = "The following scenarios have failed: #"
751 + exceptionMap.keySet().stream()
752 .map(s -> String.valueOf(s.getIndex()))
753 .collect(Collectors.joining(", #"));
754 StringBuilder builder = new StringBuilder(failedScenarios);
755 builder.append(System.lineSeparator()).append(System.lineSeparator());
756 for (Map.Entry<Scenario, Exception> entry : exceptionMap.entrySet()) {
757 Exception e = entry.getValue();
758 Scenario scenario = entry.getKey();
759 String errorMsg = "";
760 if (scenario != null) {
761 errorMsg = getScenarioTitleAndFlags(scenario);
762 }
763 if (e instanceof IRViolationException irException) {
764 // For IR violations, only show the actual violations and not the (uninteresting) stack trace.
765 if (scenario != null) {
766 System.out.println("Scenario #" + scenario.getIndex());
767 }
768 System.out.println(irException.getCompilations());
769 builder.append(errorMsg).append(System.lineSeparator()).append(irException.getExceptionInfo());
770 } else if (e instanceof TestVMException testVMException) {
771 builder.append(errorMsg).append(System.lineSeparator()).append(testVMException.getExceptionInfo());
772 } else {
773 // Print stack trace otherwise
774 StringWriter errors = new StringWriter();
775 e.printStackTrace(new PrintWriter(errors));
776 builder.append(errors);
777 }
778 builder.append(System.lineSeparator());
779 }
780 System.err.println(builder);
781 if (!VERBOSE && !REPORT_STDOUT && !TESTLIST && !EXCLUDELIST) {
782 // Provide a hint to the user how to get additional output/debugging information.
783 System.err.println(RERUN_HINT);
784 }
785 throw new TestRunException(failedScenarios + ". Please check stderr for more information.");
786 }
787
788 private static String getScenarioTitleAndFlags(Scenario scenario) {
789 StringBuilder builder = new StringBuilder();
790 String title = "Scenario #" + scenario.getIndex();
791 builder.append(title).append(System.lineSeparator()).append("=".repeat(title.length()))
792 .append(System.lineSeparator());
793 builder.append("Scenario flags: [").append(String.join(", ", scenario.getFlags())).append("]")
794 .append(System.lineSeparator());
795 return builder.toString();
796 }
797
798 /**
799 * Execute a separate "flag" VM with White Box access to determine all test VM flags. The flag VM sends an encoding of
800 * all required flags for the test VM to the driver VM over a socket. Once the flag VM exits, this driver VM parses the
801 * test VM flags, which also determine if IR matching should be done, and then starts the test VM to execute all tests.
802 */
803 private void start(Scenario scenario) {
804 if (scenario != null && !scenario.isEnabled()) {
805 System.out.println("Disabled scenario #" + scenario.getIndex() + "! This scenario is not present in set flag " +
806 "-DScenarios and is therefore not executed.");
807 return;
808 }
809 shouldVerifyIR = irVerificationPossible;
810 try {
811 // Use TestFramework flags and scenario flags for new VMs.
812 List<String> additionalFlags = new ArrayList<>();
813 if (flags != null) {
814 additionalFlags.addAll(flags);
815 }
816 if (scenario != null) {
817 List<String> scenarioFlags = scenario.getFlags();
818 String scenarioFlagsString = scenarioFlags.isEmpty() ? "" : " - [" + String.join(", ", scenarioFlags) + "]";
819 System.out.println("Scenario #" + scenario.getIndex() + scenarioFlagsString + ":");
820 additionalFlags.addAll(scenarioFlags);
821 }
822 String frameworkAndScenarioFlags = additionalFlags.isEmpty() ?
823 "" : " - [" + String.join(", ", additionalFlags) + "]";
824
825 if (shouldVerifyIR) {
826 // Only need to use flag VM if an IR verification is possibly done.
827 System.out.println("Run Flag VM:");
828 FlagVMProcess flagVMProcess = new FlagVMProcess(testClass, additionalFlags);
829 shouldVerifyIR = flagVMProcess.shouldVerifyIR();
830 if (shouldVerifyIR) {
831 // Add more flags for the test VM which are required to do IR verification.
832 additionalFlags.addAll(flagVMProcess.getTestVMFlags());
833 } // else: Flag VM found a reason to not do IR verification.
834 } else {
835 System.out.println("Skip Flag VM due to not performing IR verification.");
836 }
837
838 System.out.println("Run Test VM" + frameworkAndScenarioFlags + ":");
839 runTestVM(additionalFlags);
840 } finally {
841 if (scenario != null) {
842 scenario.setTestVMOutput(TestVMProcess.getLastTestVMOutput());
843 }
844 System.out.println();
845 }
846 }
847
848 private boolean hasIRAnnotations() {
849 return Arrays.stream(testClass.getDeclaredMethods()).anyMatch(m -> m.getAnnotationsByType(IR.class).length > 0);
850 }
851
852 private void checkCompatibleFlags() {
853 for (String flag : Utils.getTestJavaOpts()) {
854 if (flag.contains("-agentpath")) {
855 throw new SkippedException("Can't run test with agent.");
856 }
857 }
858 }
859
860 private List<String> anyNonWhitelistedJTregVMAndJavaOptsFlags() {
861 List<String> flags = Arrays.stream(Utils.getTestJavaOpts())
862 .map(s -> s.replaceFirst("-XX:[+|-]?|-(?=[^D|^e])", ""))
863 .collect(Collectors.toList());
864 List<String> nonWhiteListedFlags = new ArrayList();
865 for (String flag : flags) {
866 if (flag.contains("agentpath")) {
867 throw new SkippedException("Can't run test with -javaagent");
868 }
869 // Property flags (prefix -D), -ea and -esa are whitelisted.
870 if (!flag.startsWith("-D") && !flag.startsWith("-e") && JTREG_WHITELIST_FLAGS.stream().noneMatch(flag::contains)) {
871 // Found VM flag that is not whitelisted
872 nonWhiteListedFlags.add(flag);
873 }
874 }
875 return nonWhiteListedFlags;
876 }
877
878 private void runTestVM(List<String> additionalFlags) {
879 TestVMProcess testVMProcess = new TestVMProcess(additionalFlags, testClass, helperClasses, defaultWarmup,
880 isAllowNotCompilable, testClassesOnBootClassPath);
881 if (shouldVerifyIR) {
882 try {
883 TestClassParser testClassParser = new TestClassParser(testClass, isAllowNotCompilable);
884 Matchable testClassMatchable = testClassParser.parse(testVMProcess.getHotspotPidFileName(),
885 testVMProcess.getIrEncoding());
886 IRMatcher matcher = new IRMatcher(testClassMatchable);
887 matcher.match();
888 } catch (IRViolationException e) {
889 e.addCommandLine(testVMProcess.getCommandLine());
890 throw e;
891 }
892 } else {
893 System.out.println("IR verification disabled either due to no @IR annotations, through explicitly setting " +
894 "-DVerify=false, due to not running a debug build, using a non-whitelisted JTreg VM or " +
895 "Javaopts flag like -Xint, or running the test VM with other VM flags added by user code " +
896 "that make the IR verification impossible (e.g. -XX:-UseCompile, " +
897 "-XX:TieredStopAtLevel=[1,2,3], etc.).");
898 }
899 }
900
901 public static void check(boolean test, String failureMessage) {
902 if (!test) {
903 throw new TestFrameworkException(failureMessage);
904 }
905 }
906 }
--- EOF ---