CheckGraalInvariants.java revision 13083:b9a173f12fe6
1/* 2 * Copyright (c) 2014, 2016, 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 */ 23package org.graalvm.compiler.core.test; 24 25import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.INTERCEPT; 26 27import java.io.File; 28import java.io.IOException; 29import java.io.PrintWriter; 30import java.io.StringWriter; 31import java.lang.annotation.Annotation; 32import java.lang.reflect.Method; 33import java.lang.reflect.Modifier; 34import java.util.ArrayList; 35import java.util.Collections; 36import java.util.Enumeration; 37import java.util.List; 38import java.util.concurrent.LinkedBlockingQueue; 39import java.util.concurrent.ThreadPoolExecutor; 40import java.util.concurrent.TimeUnit; 41import java.util.zip.ZipEntry; 42import java.util.zip.ZipFile; 43 44import org.graalvm.api.word.LocationIdentity; 45import org.graalvm.compiler.api.replacements.Snippet; 46import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter; 47import org.graalvm.compiler.api.replacements.Snippet.NonNullParameter; 48import org.graalvm.compiler.api.replacements.Snippet.VarargsParameter; 49import org.graalvm.compiler.api.test.Graal; 50import org.graalvm.compiler.bytecode.BridgeMethodUtils; 51import org.graalvm.compiler.core.CompilerThreadFactory; 52import org.graalvm.compiler.core.CompilerThreadFactory.DebugConfigAccess; 53import org.graalvm.compiler.core.common.LIRKind; 54import org.graalvm.compiler.core.common.type.ArithmeticOpTable; 55import org.graalvm.compiler.debug.Debug; 56import org.graalvm.compiler.debug.DebugConfigScope; 57import org.graalvm.compiler.debug.DebugEnvironment; 58import org.graalvm.compiler.debug.DelegatingDebugConfig; 59import org.graalvm.compiler.debug.GraalDebugConfig; 60import org.graalvm.compiler.debug.GraalError; 61import org.graalvm.compiler.graph.Node; 62import org.graalvm.compiler.graph.NodeClass; 63import org.graalvm.compiler.java.GraphBuilderPhase; 64import org.graalvm.compiler.nodeinfo.NodeInfo; 65import org.graalvm.compiler.nodes.PhiNode; 66import org.graalvm.compiler.nodes.StructuredGraph; 67import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 68import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 69import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; 70import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; 71import org.graalvm.compiler.options.OptionValues; 72import org.graalvm.compiler.phases.OptimisticOptimizations; 73import org.graalvm.compiler.phases.PhaseSuite; 74import org.graalvm.compiler.phases.VerifyPhase; 75import org.graalvm.compiler.phases.VerifyPhase.VerificationError; 76import org.graalvm.compiler.phases.contract.VerifyNodeCosts; 77import org.graalvm.compiler.phases.tiers.HighTierContext; 78import org.graalvm.compiler.phases.util.Providers; 79import org.graalvm.compiler.phases.verify.VerifyBailoutUsage; 80import org.graalvm.compiler.phases.verify.VerifyCallerSensitiveMethods; 81import org.graalvm.compiler.phases.verify.VerifyDebugUsage; 82import org.graalvm.compiler.phases.verify.VerifyInstanceOfUsage; 83import org.graalvm.compiler.phases.verify.VerifyUpdateUsages; 84import org.graalvm.compiler.phases.verify.VerifyUsageWithEquals; 85import org.graalvm.compiler.phases.verify.VerifyVirtualizableUsage; 86import org.graalvm.compiler.runtime.RuntimeProvider; 87import org.junit.Assert; 88import org.junit.Assume; 89import org.junit.Test; 90 91import jdk.vm.ci.code.BailoutException; 92import jdk.vm.ci.code.Register; 93import jdk.vm.ci.code.Register.RegisterCategory; 94import jdk.vm.ci.meta.JavaField; 95import jdk.vm.ci.meta.JavaMethod; 96import jdk.vm.ci.meta.JavaType; 97import jdk.vm.ci.meta.MetaAccessProvider; 98import jdk.vm.ci.meta.ResolvedJavaMethod; 99import jdk.vm.ci.meta.ResolvedJavaType; 100import jdk.vm.ci.meta.Value; 101 102/** 103 * Checks that all classes in *graal*.jar and *jvmci*.jar entries on the boot class path comply with 104 * global invariants such as using {@link Object#equals(Object)} to compare certain types instead of 105 * identity comparisons. 106 */ 107public class CheckGraalInvariants extends GraalCompilerTest { 108 109 private static boolean shouldVerifyEquals(ResolvedJavaMethod m) { 110 if (m.getName().equals("identityEquals")) { 111 ResolvedJavaType c = m.getDeclaringClass(); 112 if (c.getName().equals("Ljdk/vm/ci/meta/AbstractValue;") || c.getName().equals("jdk/vm/ci/meta/Value")) { 113 return false; 114 } 115 } 116 117 return true; 118 } 119 120 public static String relativeFileName(String absolutePath) { 121 int lastFileSeparatorIndex = absolutePath.lastIndexOf(File.separator); 122 return absolutePath.substring(lastFileSeparatorIndex >= 0 ? lastFileSeparatorIndex : 0); 123 } 124 125 public static class InvariantsTool { 126 127 protected boolean shouldProcess(String classpathEntry) { 128 if (classpathEntry.endsWith(".jar")) { 129 String name = new File(classpathEntry).getName(); 130 return name.contains("jvmci") || name.contains("graal") || name.contains("jdk.internal.vm.compiler"); 131 } 132 return false; 133 } 134 135 protected String getClassPath() { 136 String bootclasspath; 137 if (Java8OrEarlier) { 138 bootclasspath = System.getProperty("sun.boot.class.path"); 139 } else { 140 bootclasspath = System.getProperty("jdk.module.path") + File.pathSeparatorChar + System.getProperty("jdk.module.upgrade.path"); 141 } 142 return bootclasspath; 143 } 144 145 protected boolean shouldLoadClass(String className) { 146 return !className.equals("module-info"); 147 } 148 149 protected void handleClassLoadingException(Throwable t) { 150 GraalError.shouldNotReachHere(t); 151 } 152 153 protected void handleParsingException(Throwable t) { 154 GraalError.shouldNotReachHere(t); 155 } 156 } 157 158 @Test 159 @SuppressWarnings("try") 160 public void test() { 161 runTest(new InvariantsTool()); 162 } 163 164 @SuppressWarnings("try") 165 public static void runTest(InvariantsTool tool) { 166 RuntimeProvider rt = Graal.getRequiredCapability(RuntimeProvider.class); 167 Providers providers = rt.getHostBackend().getProviders(); 168 MetaAccessProvider metaAccess = providers.getMetaAccess(); 169 170 PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>(); 171 Plugins plugins = new Plugins(new InvocationPlugins()); 172 GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true); 173 graphBuilderSuite.appendPhase(new GraphBuilderPhase(config)); 174 HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE); 175 176 Assume.assumeTrue(VerifyPhase.class.desiredAssertionStatus()); 177 178 String bootclasspath = tool.getClassPath(); 179 Assert.assertNotNull("Cannot find boot class path", bootclasspath); 180 181 final List<String> classNames = new ArrayList<>(); 182 for (String path : bootclasspath.split(File.pathSeparator)) { 183 if (tool.shouldProcess(path)) { 184 try { 185 final ZipFile zipFile = new ZipFile(new File(path)); 186 for (final Enumeration<? extends ZipEntry> entry = zipFile.entries(); entry.hasMoreElements();) { 187 final ZipEntry zipEntry = entry.nextElement(); 188 String name = zipEntry.getName(); 189 if (name.endsWith(".class")) { 190 String className = name.substring(0, name.length() - ".class".length()).replace('/', '.'); 191 classNames.add(className); 192 } 193 } 194 } catch (IOException ex) { 195 Assert.fail(ex.toString()); 196 } 197 } 198 } 199 Assert.assertFalse("Could not find graal jars on boot class path: " + bootclasspath, classNames.isEmpty()); 200 201 // Allows a subset of methods to be checked through use of a system property 202 String property = System.getProperty(CheckGraalInvariants.class.getName() + ".filters"); 203 String[] filters = property == null ? null : property.split(","); 204 205 OptionValues options = getInitialOptions(); 206 CompilerThreadFactory factory = new CompilerThreadFactory("CheckInvariantsThread", new DebugConfigAccess() { 207 @Override 208 public GraalDebugConfig getDebugConfig() { 209 return DebugEnvironment.ensureInitialized(options); 210 } 211 }); 212 int availableProcessors = Runtime.getRuntime().availableProcessors(); 213 ThreadPoolExecutor executor = new ThreadPoolExecutor(availableProcessors, availableProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory); 214 215 List<String> errors = Collections.synchronizedList(new ArrayList<>()); 216 217 for (Method m : BadUsageWithEquals.class.getDeclaredMethods()) { 218 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 219 StructuredGraph graph = new StructuredGraph.Builder(options, AllowAssumptions.YES).method(method).build(); 220 try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT)); Debug.Scope ds = Debug.scope("CheckingGraph", graph, method)) { 221 graphBuilderSuite.apply(graph, context); 222 // update phi stamps 223 graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp); 224 checkGraph(context, graph); 225 errors.add(String.format("Expected error while checking %s", m)); 226 } catch (VerificationError e) { 227 // expected! 228 } catch (Throwable e) { 229 errors.add(String.format("Error while checking %s:%n%s", m, printStackTraceToString(e))); 230 } 231 } 232 if (errors.isEmpty()) { 233 // Order outer classes before the inner classes 234 classNames.sort((String a, String b) -> a.compareTo(b)); 235 // Initialize classes in single thread to avoid deadlocking issues during initialization 236 List<Class<?>> classes = initializeClasses(tool, classNames); 237 for (Class<?> c : classes) { 238 String className = c.getName(); 239 executor.execute(() -> { 240 try { 241 checkClass(c, metaAccess); 242 } catch (Throwable e) { 243 errors.add(String.format("Error while checking %s:%n%s", className, printStackTraceToString(e))); 244 } 245 }); 246 247 for (Method m : c.getDeclaredMethods()) { 248 if (Modifier.isNative(m.getModifiers()) || Modifier.isAbstract(m.getModifiers())) { 249 // ignore 250 } else { 251 String methodName = className + "." + m.getName(); 252 if (matches(filters, methodName)) { 253 executor.execute(() -> { 254 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 255 StructuredGraph graph = new StructuredGraph.Builder(options).method(method).build(); 256 try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT)); Debug.Scope ds = Debug.scope("CheckingGraph", graph, method)) { 257 checkMethod(method); 258 graphBuilderSuite.apply(graph, context); 259 // update phi stamps 260 graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp); 261 checkGraph(context, graph); 262 } catch (VerificationError e) { 263 errors.add(e.getMessage()); 264 } catch (LinkageError e) { 265 // suppress linkages errors resulting from eager resolution 266 } catch (BailoutException e) { 267 // Graal bail outs on certain patterns in Java bytecode (e.g., 268 // unbalanced monitors introduced by jacoco). 269 } catch (Throwable e) { 270 try { 271 tool.handleParsingException(e); 272 } catch (Throwable t) { 273 errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e))); 274 } 275 } 276 }); 277 } 278 } 279 } 280 } 281 executor.shutdown(); 282 try { 283 executor.awaitTermination(1, TimeUnit.HOURS); 284 } catch (InterruptedException e1) { 285 throw new RuntimeException(e1); 286 } 287 } 288 if (!errors.isEmpty()) { 289 StringBuilder msg = new StringBuilder(); 290 String nl = String.format("%n"); 291 for (String e : errors) { 292 if (msg.length() != 0) { 293 msg.append(nl); 294 } 295 msg.append(e); 296 } 297 Assert.fail(msg.toString()); 298 } 299 } 300 301 private static List<Class<?>> initializeClasses(InvariantsTool tool, List<String> classNames) { 302 List<Class<?>> classes = new ArrayList<>(classNames.size()); 303 for (String className : classNames) { 304 if (!tool.shouldLoadClass(className)) { 305 continue; 306 } 307 try { 308 Class<?> c = Class.forName(className, true, CheckGraalInvariants.class.getClassLoader()); 309 classes.add(c); 310 } catch (Throwable t) { 311 tool.handleClassLoadingException(t); 312 } 313 } 314 return classes; 315 } 316 317 /** 318 * @param metaAccess 319 */ 320 private static void checkClass(Class<?> c, MetaAccessProvider metaAccess) { 321 if (Node.class.isAssignableFrom(c)) { 322 if (c.getAnnotation(NodeInfo.class) == null) { 323 throw new AssertionError(String.format("Node subclass %s requires %s annotation", c.getName(), NodeClass.class.getSimpleName())); 324 } 325 VerifyNodeCosts.verifyNodeClass(c); 326 } 327 } 328 329 private static void checkMethod(ResolvedJavaMethod method) { 330 if (method.getAnnotation(Snippet.class) == null) { 331 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 332 for (int i = 0; i < parameterAnnotations.length; i++) { 333 for (Annotation a : parameterAnnotations[i]) { 334 Class<? extends Annotation> annotationType = a.annotationType(); 335 if (annotationType == ConstantParameter.class || annotationType == VarargsParameter.class || annotationType == NonNullParameter.class) { 336 VerificationError verificationError = new VerificationError("Parameter %d of %s is annotated with %s but the method is not annotated with %s", i, method, 337 annotationType.getSimpleName(), 338 Snippet.class.getSimpleName()); 339 throw verificationError; 340 } 341 } 342 } 343 } 344 } 345 346 /** 347 * Checks the invariants for a single graph. 348 */ 349 private static void checkGraph(HighTierContext context, StructuredGraph graph) { 350 if (shouldVerifyEquals(graph.method())) { 351 // If you add a new type to test here, be sure to add appropriate 352 // methods to the BadUsageWithEquals class below 353 new VerifyUsageWithEquals(Value.class).apply(graph, context); 354 new VerifyUsageWithEquals(Register.class).apply(graph, context); 355 new VerifyUsageWithEquals(RegisterCategory.class).apply(graph, context); 356 new VerifyUsageWithEquals(JavaType.class).apply(graph, context); 357 new VerifyUsageWithEquals(JavaMethod.class).apply(graph, context); 358 new VerifyUsageWithEquals(JavaField.class).apply(graph, context); 359 new VerifyUsageWithEquals(LocationIdentity.class).apply(graph, context); 360 new VerifyUsageWithEquals(LIRKind.class).apply(graph, context); 361 new VerifyUsageWithEquals(ArithmeticOpTable.class).apply(graph, context); 362 new VerifyUsageWithEquals(ArithmeticOpTable.Op.class).apply(graph, context); 363 } 364 new VerifyDebugUsage().apply(graph, context); 365 new VerifyCallerSensitiveMethods().apply(graph, context); 366 new VerifyVirtualizableUsage().apply(graph, context); 367 new VerifyUpdateUsages().apply(graph, context); 368 new VerifyBailoutUsage().apply(graph, context); 369 new VerifyInstanceOfUsage().apply(graph, context); 370 if (graph.method().isBridge()) { 371 BridgeMethodUtils.getBridgedMethod(graph.method()); 372 } 373 } 374 375 private static boolean matches(String[] filters, String s) { 376 if (filters == null || filters.length == 0) { 377 return true; 378 } 379 for (String filter : filters) { 380 if (s.contains(filter)) { 381 return true; 382 } 383 } 384 return false; 385 } 386 387 private static String printStackTraceToString(Throwable t) { 388 StringWriter sw = new StringWriter(); 389 t.printStackTrace(new PrintWriter(sw)); 390 return sw.toString(); 391 } 392 393 static class BadUsageWithEquals { 394 Value aValue; 395 Register aRegister; 396 RegisterCategory aRegisterCategory; 397 JavaType aJavaType; 398 JavaField aJavaField; 399 JavaMethod aJavaMethod; 400 LocationIdentity aLocationIdentity; 401 LIRKind aLIRKind; 402 ArithmeticOpTable anArithmeticOpTable; 403 ArithmeticOpTable.Op anArithmeticOpTableOp; 404 405 static Value aStaticValue; 406 static Register aStaticRegister; 407 static RegisterCategory aStaticRegisterCategory; 408 static JavaType aStaticJavaType; 409 static JavaField aStaticJavaField; 410 static JavaMethod aStaticJavaMethod; 411 static LocationIdentity aStaticLocationIdentity; 412 static LIRKind aStaticLIRKind; 413 static ArithmeticOpTable aStaticArithmeticOpTable; 414 static ArithmeticOpTable.Op aStaticArithmeticOpTableOp; 415 416 boolean test01(Value f) { 417 return aValue == f; 418 } 419 420 boolean test02(Register f) { 421 return aRegister == f; 422 } 423 424 boolean test03(RegisterCategory f) { 425 return aRegisterCategory == f; 426 } 427 428 boolean test04(JavaType f) { 429 return aJavaType == f; 430 } 431 432 boolean test05(JavaField f) { 433 return aJavaField == f; 434 } 435 436 boolean test06(JavaMethod f) { 437 return aJavaMethod == f; 438 } 439 440 boolean test07(LocationIdentity f) { 441 return aLocationIdentity == f; 442 } 443 444 boolean test08(LIRKind f) { 445 return aLIRKind == f; 446 } 447 448 boolean test09(ArithmeticOpTable f) { 449 return anArithmeticOpTable == f; 450 } 451 452 boolean test10(ArithmeticOpTable.Op f) { 453 return anArithmeticOpTableOp == f; 454 } 455 456 boolean test12(Value f) { 457 return aStaticValue == f; 458 } 459 460 boolean test13(Register f) { 461 return aStaticRegister == f; 462 } 463 464 boolean test14(RegisterCategory f) { 465 return aStaticRegisterCategory == f; 466 } 467 468 boolean test15(JavaType f) { 469 return aStaticJavaType == f; 470 } 471 472 boolean test16(JavaField f) { 473 return aStaticJavaField == f; 474 } 475 476 boolean test17(JavaMethod f) { 477 return aStaticJavaMethod == f; 478 } 479 480 boolean test18(LocationIdentity f) { 481 return aStaticLocationIdentity == f; 482 } 483 484 boolean test19(LIRKind f) { 485 return aStaticLIRKind == f; 486 } 487 488 boolean test20(ArithmeticOpTable f) { 489 return aStaticArithmeticOpTable == f; 490 } 491 492 boolean test21(ArithmeticOpTable.Op f) { 493 return aStaticArithmeticOpTableOp == f; 494 } 495 } 496} 497