提交 b9eb40b4 编写于 作者: F fzhinkin

8059342: Add test to cover JDK-8030976

Reviewed-by: kvn, iignatyev
上级 57020328
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
package uncommontrap;
import java.io.FileReader;
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.regex.Pattern;
import com.oracle.java.testlibrary.Asserts;
/**
* Utility tool aimed to verify presence or absence of specified uncommon trap
* in compilation log.
*/
public class Verifier {
public static final String PROPERTIES_FILE_SUFFIX = ".verify.properties";
public static final String VERIFICATION_SHOULD_BE_SKIPPED
= "uncommon.trap.verification.skipped";
public static final String UNCOMMON_TRAP_NAME = "uncommon.trap.name";
public static final String UNCOMMON_TRAP_BCI = "uncommon.trap.bci";
public static final String UNCOMMON_TRAP_COMMENT = "uncommon.trap.comment";
public static final String UNCOMMON_TRAP_ACTION = "uncommon.trap.action";
public static final String UNCOMMON_TRAP_SHOULD_EMITTED
= "uncommon.trap.emitted";
public static final String UNCOMMON_TRAP_SHOULD_FIRED
= "uncommon.trap.fired";
private static final String EMITTED_TRAP_PATTERN
= "<uncommon_trap bci='%s' reason='%s' action='%s' "
+ "comment='%s'";
private static final String FIRED_TRAP_PATTERN
= "<uncommon_trap thread='[0-9]*' reason='%s' action='%s'";
private static final String JVMS_PATTERN = "<jvms bci='%s'";
public static void main(String args[]) {
if (args.length == 0) {
throw new Error("At least one argument containing name of "
+ "compilation log file is expected");
}
for (String compLogFile : args) {
try {
verify(Paths.get(compLogFile));
} catch (IOException e) {
throw new Error("Unable to process compilation log.", e);
}
}
}
private static void verify(Path compLogFile) throws IOException {
Path propertiesFile = Paths.get(compLogFile.toString() +
PROPERTIES_FILE_SUFFIX);
Properties properties = new Properties();
properties.load(new FileReader(propertiesFile.toFile()));
if (Boolean.valueOf(properties.getProperty(
VERIFICATION_SHOULD_BE_SKIPPED, "false"))) {
System.out.println("Skipping verification for log file: "
+ compLogFile.toString());
return;
}
System.out.println("Verifying log file: " + compLogFile.toString());
List<String> compLogContent = Files.readAllLines(compLogFile);
verifyUncommonTrapEmitted(properties, compLogContent);
verifyUncommonTrapFired(properties, compLogContent);
}
private static void verifyUncommonTrapEmitted(Properties properties,
List<String> compLogContent) {
String emittedTrapRE = String.format(EMITTED_TRAP_PATTERN,
properties.getProperty(UNCOMMON_TRAP_BCI, ".*"),
properties.getProperty(UNCOMMON_TRAP_NAME, ".*"),
properties.getProperty(UNCOMMON_TRAP_ACTION, ".*"),
properties.getProperty(UNCOMMON_TRAP_COMMENT, ".*"));
Pattern pattern = Pattern.compile(emittedTrapRE);
long trapsCount = compLogContent.stream()
.filter(line -> pattern.matcher(line).find())
.count();
boolean shouldBeEmitted = Boolean.valueOf(
properties.getProperty(UNCOMMON_TRAP_SHOULD_EMITTED));
Asserts.assertEQ(shouldBeEmitted, trapsCount > 0, String.format(
"Uncommon trap that matches following string in compilation log"
+ " should %sbe emitted: %s.",
(shouldBeEmitted ? " " : "not "), emittedTrapRE));
}
private static void verifyUncommonTrapFired(Properties properties,
List<String> compLogContent) {
String firedTrapRE = String.format(FIRED_TRAP_PATTERN,
properties.getProperty(UNCOMMON_TRAP_NAME, ".*"),
properties.getProperty(UNCOMMON_TRAP_ACTION, ".*"));
String jvmsRE = String.format(JVMS_PATTERN,
properties.getProperty(UNCOMMON_TRAP_BCI, ".*"));
boolean trapFired = false;
Pattern firedTrapPattern = Pattern.compile(firedTrapRE);
Pattern jvmsPattern = Pattern.compile(jvmsRE);
Iterator<String> iterator = compLogContent.iterator();
while (iterator.hasNext() && !trapFired) {
trapFired = firedTrapPattern.matcher(iterator.next()).find()
&& jvmsPattern.matcher(iterator.next()).find();
}
boolean shouldBeFired = Boolean.valueOf(
properties.getProperty(UNCOMMON_TRAP_SHOULD_FIRED));
Asserts.assertEQ(shouldBeFired, trapFired, String.format(
"Uncommon trap that matches following string in compilation log"
+ " should %sbe fired: %s.",
(shouldBeFired ? "" : "not "), firedTrapRE));
}
}
/*
* Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.Properties;
import com.oracle.java.testlibrary.ByteCodeLoader;
import com.oracle.java.testlibrary.Platform;
import jdk.internal.org.objectweb.asm.ClassVisitor;
import jdk.internal.org.objectweb.asm.ClassWriter;
import jdk.internal.org.objectweb.asm.Label;
import jdk.internal.org.objectweb.asm.MethodVisitor;
import static jdk.internal.org.objectweb.asm.Opcodes.*;
import sun.hotspot.WhiteBox;
import uncommontrap.Verifier;
/*
* @test
* @bug 8030976 8059226
* @library /testlibrary /compiler/testlibrary /../../test/lib
* @build TestUnstableIfTrap com.oracle.java.testlibrary.* uncommontrap.Verifier
* @run main ClassFileInstaller sun.hotspot.WhiteBox
* sun.hotspot.WhiteBox$WhiteBoxPermission
* @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI -XX:+LogCompilation
* -XX:CompileCommand=compileonly,UnstableIfExecutable.test
* -XX:LogFile=always_taken_not_fired.xml
* TestUnstableIfTrap ALWAYS_TAKEN false
* @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI -XX:+LogCompilation
* -XX:CompileCommand=compileonly,UnstableIfExecutable.test
* -XX:LogFile=always_taken_fired.xml
* TestUnstableIfTrap ALWAYS_TAKEN true
* @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI -XX:+LogCompilation
* -XX:CompileCommand=compileonly,UnstableIfExecutable.test
* -XX:LogFile=never_taken_not_fired.xml
* TestUnstableIfTrap NEVER_TAKEN false
* @run main/othervm -Xbatch -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions
* -XX:+WhiteBoxAPI -XX:+LogCompilation
* -XX:CompileCommand=compileonly,UnstableIfExecutable.test
* -XX:LogFile=never_taken_fired.xml
* TestUnstableIfTrap NEVER_TAKEN true
* @run main uncommontrap.Verifier always_taken_not_fired.xml
* always_taken_fired.xml
* never_taken_not_fired.xml
* never_taken_fired.xml
*/
public class TestUnstableIfTrap {
private static final WhiteBox WB = WhiteBox.getWhiteBox();
private static final String CLASS_NAME = "UnstableIfExecutable";
private static final String METHOD_NAME = "test";
private static final String FIELD_NAME = "field";
private static final int ITERATIONS = 1_000_000;
// There is no dependency on particular class file version, so it could be
// set to any version (if you're updating this test for Java 42).
private static final int CLASS_FILE_VERSION = 49;
private static final int MAX_TIER = 4;
// This test aimed to verify that uncommon trap with reason "unstable_if"
// is emitted when method that contain control-flow divergence such that
// one of two branches is never taken (and other one is taken always).
// C2 will made a decision whether or not the branch was ever taken
// depending on method's profile.
// If profile was collected for a few method's invocations, then C2 will not
// trust in branches' probabilities and the tested trap won't be emitted.
// In fact, a method has to be invoked at least 40 time at the day when this
// comment was written (see Parse::dynamic_branch_prediction for an actual
// value). It would be to implementation dependent to use "40" as
// a threshold value in the test, so in order to improve test's robustness
// the threshold value is 1000: if the tested method was compiled by C2
// before it was invoked 1000 times, then we won't verify that trap was
// emitted and fired.
private static final int MIN_INVOCATIONS_BEFORE_C2_COMPILATION = 1000;
/**
* Description of test case parameters and uncommon trap that will
* be emitted during tested method compilation.
*/
private static enum TestCaseName {
ALWAYS_TAKEN(false, "taken always"),
NEVER_TAKEN(true, "taken never");
TestCaseName(boolean predicate, String comment) {
this.predicate = predicate;
this.comment = comment;
}
public final boolean predicate;
public final String name = "unstable_if";
public final String comment;
}
public static void main(String args[]) {
if (args.length != 2) {
throw new Error("Expected two arguments: test case name and a "
+ "boolean determining if uncommon trap should be fired.");
}
test(TestCaseName.valueOf(args[0]), Boolean.valueOf(args[1]));
}
private static void test(TestCaseName testCase, boolean shouldBeFired) {
Method testMethod;
Label unstableIfLocation = new Label();
boolean shouldBeEmitted;
boolean compiledToEarly = false;
try {
Class testClass = ByteCodeLoader.load(CLASS_NAME,
generateTest(unstableIfLocation));
testMethod = testClass.getDeclaredMethod(METHOD_NAME,
boolean.class);
for (int i = 0; i < ITERATIONS; i++) {
testMethod.invoke(null, testCase.predicate);
if (i < MIN_INVOCATIONS_BEFORE_C2_COMPILATION
&& isMethodCompiledByC2(testMethod)) {
compiledToEarly = true;
// There is no sense in further invocations: we already
// decided to avoid verification.
break;
}
}
// We're checking that trap should be emitted (i.e. it was compiled
// by C2) before the trap is fired, because otherwise the nmethod
// will be deoptimized and isMethodCompiledByC2 will return false.
shouldBeEmitted = isMethodCompiledByC2(testMethod)
&& !compiledToEarly;
if (shouldBeFired) {
testMethod.invoke(null, !testCase.predicate);
}
} catch (ReflectiveOperationException e) {
throw new Error("Test case should be generated, loaded and executed"
+ " without any issues.", e);
}
shouldBeFired &= shouldBeEmitted;
Properties properties = new Properties();
properties.setProperty(Verifier.VERIFICATION_SHOULD_BE_SKIPPED,
Boolean.toString(compiledToEarly));
properties.setProperty(Verifier.UNCOMMON_TRAP_SHOULD_EMITTED,
Boolean.toString(shouldBeEmitted));
properties.setProperty(Verifier.UNCOMMON_TRAP_SHOULD_FIRED,
Boolean.toString(shouldBeFired));
properties.setProperty(Verifier.UNCOMMON_TRAP_NAME, testCase.name);
properties.setProperty(Verifier.UNCOMMON_TRAP_COMMENT,
testCase.comment);
properties.setProperty(Verifier.UNCOMMON_TRAP_BCI,
Integer.toString(unstableIfLocation.getOffset()));
properties.list(System.out);
File f = new File(WB.getStringVMFlag("LogFile") +
Verifier.PROPERTIES_FILE_SUFFIX);
try (FileWriter wr = new FileWriter(f)) {
properties.store(wr, "");
} catch (IOException e) {
throw new Error("Unable to store test properties.", e);
}
}
private static boolean isMethodCompiledByC2(Method m) {
boolean isTiered = WB.getBooleanVMFlag("TieredCompilation");
boolean isMethodCompiled = WB.isMethodCompiled(m);
boolean isMethodCompiledAtMaxTier
= WB.getMethodCompilationLevel(m) == MAX_TIER;
return Platform.isServer() && isMethodCompiled
&& (!isTiered || isMethodCompiledAtMaxTier);
}
/**
* Generates class with name {@code CLASS_NAME}, which will contain a
* static method {@code METHOD_NAME}:
*
* <pre>{@code
* public abstract class UnstableIfExecutable {
* private static int field = 0;
*
* public static void test(boolean alwaysTrue) {
* if (alwaysTrue) {
* field++;
* } else {
* field--;
* }
* }
* }
* }</pre>
*
* @return generated bytecode.
*/
private static byte[] generateTest(Label unstableIfLocation) {
ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
cw.visit(CLASS_FILE_VERSION, ACC_PUBLIC | ACC_ABSTRACT, CLASS_NAME,
null, "java/lang/Object", null);
cw.visitField(ACC_PUBLIC | ACC_STATIC | ACC_VOLATILE, FIELD_NAME,
"I", null, Integer.valueOf(0));
generateTestMethod(cw, unstableIfLocation);
return cw.toByteArray();
}
private static void generateTestMethod(ClassVisitor cv,
Label unstableIfLocation) {
MethodVisitor mv = cv.visitMethod(ACC_PUBLIC | ACC_STATIC, METHOD_NAME,
"(Z)V", null, null);
mv.visitCode();
Label end = new Label();
Label falseBranch = new Label();
// push "field" field's value and 1 to stack
mv.visitFieldInsn(GETSTATIC, CLASS_NAME, FIELD_NAME, "I");
mv.visitInsn(ICONST_1);
// load argument's value
mv.visitVarInsn(ILOAD, 0); // alwaysTrue
// here is our unstable if
mv.visitLabel(unstableIfLocation);
mv.visitJumpInsn(IFEQ, falseBranch);
// increment on "true"
mv.visitInsn(IADD);
mv.visitJumpInsn(GOTO, end);
// decrement on "false"
mv.visitLabel(falseBranch);
mv.visitInsn(ISUB);
mv.visitLabel(end);
// bye bye
mv.visitInsn(RETURN);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册