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