1 /* 2 * Copyright (c) 2014, 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.core.test; 26 27 import java.io.File; 28 import java.io.IOException; 29 import java.io.PrintWriter; 30 import java.io.StringWriter; 31 import java.lang.annotation.Annotation; 32 import java.lang.reflect.Method; 33 import java.lang.reflect.Modifier; 34 import java.util.ArrayList; 35 import java.util.Collections; 36 import java.util.Enumeration; 37 import java.util.List; 38 import java.util.concurrent.LinkedBlockingQueue; 39 import java.util.concurrent.ThreadPoolExecutor; 40 import java.util.concurrent.TimeUnit; 41 import java.util.zip.ZipEntry; 42 import java.util.zip.ZipFile; 43 44 import org.graalvm.compiler.api.replacements.MethodSubstitution; 45 import org.graalvm.compiler.api.replacements.Snippet; 46 import org.graalvm.compiler.api.replacements.Snippet.ConstantParameter; 47 import org.graalvm.compiler.api.replacements.Snippet.NonNullParameter; 48 import org.graalvm.compiler.api.replacements.Snippet.VarargsParameter; 49 import org.graalvm.compiler.api.test.Graal; 50 import org.graalvm.compiler.bytecode.BridgeMethodUtils; 51 import org.graalvm.compiler.core.CompilerThreadFactory; 52 import org.graalvm.compiler.core.common.LIRKind; 53 import org.graalvm.compiler.core.common.type.ArithmeticOpTable; 54 import org.graalvm.compiler.debug.DebugCloseable; 55 import org.graalvm.compiler.debug.DebugContext; 56 import org.graalvm.compiler.debug.DebugHandlersFactory; 57 import org.graalvm.compiler.debug.GraalError; 58 import org.graalvm.compiler.graph.Node; 59 import org.graalvm.compiler.graph.NodeClass; 60 import org.graalvm.compiler.java.GraphBuilderPhase; 61 import org.graalvm.compiler.nodeinfo.NodeInfo; 62 import org.graalvm.compiler.nodes.PhiNode; 63 import org.graalvm.compiler.nodes.StructuredGraph; 64 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 65 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 66 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; 67 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; 68 import org.graalvm.compiler.options.OptionValues; 69 import org.graalvm.compiler.phases.OptimisticOptimizations; 70 import org.graalvm.compiler.phases.PhaseSuite; 71 import org.graalvm.compiler.phases.VerifyPhase; 72 import org.graalvm.compiler.phases.VerifyPhase.VerificationError; 73 import org.graalvm.compiler.phases.contract.VerifyNodeCosts; 74 import org.graalvm.compiler.phases.tiers.HighTierContext; 75 import org.graalvm.compiler.phases.tiers.PhaseContext; 76 import org.graalvm.compiler.phases.util.Providers; 77 import org.graalvm.compiler.runtime.RuntimeProvider; 78 import jdk.internal.vm.compiler.word.LocationIdentity; 79 import org.junit.Assert; 80 import org.junit.Assume; 81 import org.junit.Test; 82 83 import jdk.vm.ci.code.BailoutException; 84 import jdk.vm.ci.code.Register; 85 import jdk.vm.ci.code.Register.RegisterCategory; 86 import jdk.vm.ci.meta.JavaField; 87 import jdk.vm.ci.meta.JavaMethod; 88 import jdk.vm.ci.meta.JavaType; 89 import jdk.vm.ci.meta.MetaAccessProvider; 90 import jdk.vm.ci.meta.ResolvedJavaMethod; 91 import jdk.vm.ci.meta.ResolvedJavaType; 92 import jdk.vm.ci.meta.Value; 93 94 /** 95 * Checks that all classes in *graal*.jar and *jvmci*.jar entries on the boot class path comply with 96 * global invariants such as using {@link Object#equals(Object)} to compare certain types instead of 97 * identity comparisons. 98 */ 99 public class CheckGraalInvariants extends GraalCompilerTest { 100 101 private static boolean shouldVerifyEquals(ResolvedJavaMethod m) { 102 if (m.getName().equals("identityEquals")) { 103 ResolvedJavaType c = m.getDeclaringClass(); 104 if (c.getName().equals("Ljdk/vm/ci/meta/AbstractValue;") || c.getName().equals("jdk/vm/ci/meta/Value")) { 105 return false; 106 } 107 } 108 109 return true; 110 } 111 112 public static String relativeFileName(String absolutePath) { 113 int lastFileSeparatorIndex = absolutePath.lastIndexOf(File.separator); 114 return absolutePath.substring(lastFileSeparatorIndex >= 0 ? lastFileSeparatorIndex : 0); 115 } 116 117 public static class InvariantsTool { 118 119 protected boolean shouldProcess(String classpathEntry) { 120 if (classpathEntry.endsWith(".jar")) { 121 String name = new File(classpathEntry).getName(); 122 return name.contains("jvmci") || name.contains("graal") || name.contains("jdk.internal.vm.compiler"); 123 } 124 return false; 125 } 126 127 protected String getClassPath() { 128 String bootclasspath; 129 if (Java8OrEarlier) { 130 bootclasspath = System.getProperty("sun.boot.class.path"); 131 } else { 132 bootclasspath = System.getProperty("jdk.module.path") + File.pathSeparatorChar + System.getProperty("jdk.module.upgrade.path"); 133 } 134 return bootclasspath; 135 } 136 137 protected boolean shouldLoadClass(String className) { 138 if (className.equals("module-info") || className.startsWith("META-INF.versions.")) { 139 return false; 140 } 141 if (!Java8OrEarlier) { 142 // @formatter:off 143 /* 144 * Work around to prevent: 145 * 146 * org.graalvm.compiler.debug.GraalError: java.lang.IllegalAccessError: class org.graalvm.compiler.serviceprovider.GraalServices$Lazy (in module 147 * jdk.internal.vm.compiler) cannot access class java.lang.management.ManagementFactory (in module java.management) because module 148 * jdk.internal.vm.compiler does not read module java.management 149 * at jdk.internal.vm.compiler/org.graalvm.compiler.debug.GraalError.shouldNotReachHere(GraalError.java:55) 150 * at org.graalvm.compiler.core.test.CheckGraalInvariants$InvariantsTool.handleClassLoadingException(CheckGraalInvariants.java:149) 151 * at org.graalvm.compiler.core.test.CheckGraalInvariants.initializeClasses(CheckGraalInvariants.java:321) 152 * at org.graalvm.compiler.core.test.CheckGraalInvariants.runTest(CheckGraalInvariants.java:239) 153 * 154 * which occurs because JDK8 overlays are in modular jars. They are never used normally. 155 */ 156 // @formatter:on 157 if (className.equals("org.graalvm.compiler.serviceprovider.GraalServices$Lazy")) { 158 return false; 159 } 160 } 161 return true; 162 } 163 164 protected void handleClassLoadingException(Throwable t) { 165 GraalError.shouldNotReachHere(t); 166 } 167 168 protected void handleParsingException(Throwable t) { 169 GraalError.shouldNotReachHere(t); 170 } 171 172 public boolean shouldVerifyFoldableMethods() { 173 return true; 174 } 175 } 176 177 @Test 178 @SuppressWarnings("try") 179 public void test() { 180 assumeManagementLibraryIsLoadable(); 181 runTest(new InvariantsTool()); 182 } 183 184 @SuppressWarnings("try") 185 public static void runTest(InvariantsTool tool) { 186 RuntimeProvider rt = Graal.getRequiredCapability(RuntimeProvider.class); 187 Providers providers = rt.getHostBackend().getProviders(); 188 MetaAccessProvider metaAccess = providers.getMetaAccess(); 189 190 PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>(); 191 Plugins plugins = new Plugins(new InvocationPlugins()); 192 GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true).withUnresolvedIsError(true); 193 graphBuilderSuite.appendPhase(new GraphBuilderPhase(config)); 194 HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE); 195 196 Assume.assumeTrue(VerifyPhase.class.desiredAssertionStatus()); 197 198 String bootclasspath = tool.getClassPath(); 199 Assert.assertNotNull("Cannot find boot class path", bootclasspath); 200 201 final List<String> classNames = new ArrayList<>(); 202 for (String path : bootclasspath.split(File.pathSeparator)) { 203 if (tool.shouldProcess(path)) { 204 try { 205 final ZipFile zipFile = new ZipFile(new File(path)); 206 for (final Enumeration<? extends ZipEntry> entry = zipFile.entries(); entry.hasMoreElements();) { 207 final ZipEntry zipEntry = entry.nextElement(); 208 String name = zipEntry.getName(); 209 if (name.endsWith(".class") && !name.startsWith("META-INF/versions/")) { 210 String className = name.substring(0, name.length() - ".class".length()).replace('/', '.'); 211 if (isInNativeImage(className)) { 212 /* 213 * Native Image is an external tool and does not need to follow the 214 * Graal invariants. 215 */ 216 continue; 217 } 218 if (isGSON(className)) { 219 /* 220 * GSON classes are compiled with old JDK 221 */ 222 continue; 223 } 224 classNames.add(className); 225 } 226 } 227 } catch (IOException ex) { 228 Assert.fail(ex.toString()); 229 } 230 } 231 } 232 Assert.assertFalse("Could not find graal jars on boot class path: " + bootclasspath, classNames.isEmpty()); 233 234 // Allows a subset of methods to be checked through use of a system property 235 String property = System.getProperty(CheckGraalInvariants.class.getName() + ".filters"); 236 String[] filters = property == null ? null : property.split(","); 237 238 OptionValues options = getInitialOptions(); 239 CompilerThreadFactory factory = new CompilerThreadFactory("CheckInvariantsThread"); 240 int availableProcessors = Runtime.getRuntime().availableProcessors(); 241 ThreadPoolExecutor executor = new ThreadPoolExecutor(availableProcessors, availableProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory); 242 243 List<String> errors = Collections.synchronizedList(new ArrayList<>()); 244 245 List<VerifyPhase<PhaseContext>> verifiers = new ArrayList<>(); 246 247 // If you add a new type to test here, be sure to add appropriate 248 // methods to the BadUsageWithEquals class below 249 verifiers.add(new VerifyUsageWithEquals(Value.class)); 250 verifiers.add(new VerifyUsageWithEquals(Register.class)); 251 verifiers.add(new VerifyUsageWithEquals(RegisterCategory.class)); 252 verifiers.add(new VerifyUsageWithEquals(JavaType.class)); 253 verifiers.add(new VerifyUsageWithEquals(JavaMethod.class)); 254 verifiers.add(new VerifyUsageWithEquals(JavaField.class)); 255 verifiers.add(new VerifyUsageWithEquals(LocationIdentity.class)); 256 verifiers.add(new VerifyUsageWithEquals(LIRKind.class)); 257 verifiers.add(new VerifyUsageWithEquals(ArithmeticOpTable.class)); 258 verifiers.add(new VerifyUsageWithEquals(ArithmeticOpTable.Op.class)); 259 260 verifiers.add(new VerifyDebugUsage()); 261 verifiers.add(new VerifyCallerSensitiveMethods()); 262 verifiers.add(new VerifyVirtualizableUsage()); 263 verifiers.add(new VerifyUpdateUsages()); 264 verifiers.add(new VerifyBailoutUsage()); 265 verifiers.add(new VerifyInstanceOfUsage()); 266 verifiers.add(new VerifyGraphAddUsage()); 267 verifiers.add(new VerifyGetOptionsUsage()); 268 269 VerifyFoldableMethods foldableMethodsVerifier = new VerifyFoldableMethods(); 270 if (tool.shouldVerifyFoldableMethods()) { 271 verifiers.add(foldableMethodsVerifier); 272 } 273 274 for (Method m : BadUsageWithEquals.class.getDeclaredMethods()) { 275 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 276 try (DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER)) { 277 StructuredGraph graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).method(method).build(); 278 try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("CheckingGraph", graph, method)) { 279 graphBuilderSuite.apply(graph, context); 280 // update phi stamps 281 graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp); 282 checkGraph(verifiers, context, graph); 283 errors.add(String.format("Expected error while checking %s", m)); 284 } catch (VerificationError e) { 285 // expected! 286 } catch (Throwable e) { 287 errors.add(String.format("Error while checking %s:%n%s", m, printStackTraceToString(e))); 288 } 289 } 290 } 291 if (errors.isEmpty()) { 292 // Order outer classes before the inner classes 293 classNames.sort((String a, String b) -> a.compareTo(b)); 294 // Initialize classes in single thread to avoid deadlocking issues during initialization 295 List<Class<?>> classes = initializeClasses(tool, classNames); 296 for (Class<?> c : classes) { 297 String className = c.getName(); 298 executor.execute(() -> { 299 try { 300 checkClass(c, metaAccess); 301 } catch (Throwable e) { 302 errors.add(String.format("Error while checking %s:%n%s", className, printStackTraceToString(e))); 303 } 304 }); 305 306 for (Method m : c.getDeclaredMethods()) { 307 if (Modifier.isNative(m.getModifiers()) || Modifier.isAbstract(m.getModifiers())) { 308 // ignore 309 } else { 310 String methodName = className + "." + m.getName(); 311 if (matches(filters, methodName)) { 312 executor.execute(() -> { 313 try (DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER)) { 314 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 315 boolean isSubstitution = method.getAnnotation(Snippet.class) != null || method.getAnnotation(MethodSubstitution.class) != null; 316 StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).setIsSubstitution(isSubstitution).build(); 317 try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("CheckingGraph", graph, method)) { 318 checkMethod(method); 319 graphBuilderSuite.apply(graph, context); 320 // update phi stamps 321 graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp); 322 checkGraph(verifiers, context, graph); 323 } catch (VerificationError e) { 324 errors.add(e.getMessage()); 325 } catch (LinkageError e) { 326 // suppress linkages errors resulting from eager resolution 327 } catch (BailoutException e) { 328 // Graal bail outs on certain patterns in Java bytecode 329 // (e.g., 330 // unbalanced monitors introduced by jacoco). 331 } catch (Throwable e) { 332 try { 333 tool.handleParsingException(e); 334 } catch (Throwable t) { 335 errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e))); 336 } 337 } 338 } 339 }); 340 } 341 } 342 } 343 } 344 345 executor.shutdown(); 346 try { 347 executor.awaitTermination(1, TimeUnit.HOURS); 348 } catch (InterruptedException e1) { 349 throw new RuntimeException(e1); 350 } 351 352 if (tool.shouldVerifyFoldableMethods()) { 353 try { 354 foldableMethodsVerifier.finish(); 355 } catch (Throwable e) { 356 errors.add(e.getMessage()); 357 } 358 } 359 } 360 if (!errors.isEmpty()) { 361 StringBuilder msg = new StringBuilder(); 362 String nl = String.format("%n"); 363 for (String e : errors) { 364 if (msg.length() != 0) { 365 msg.append(nl); 366 } 367 msg.append(e); 368 } 369 Assert.fail(msg.toString()); 370 } 371 } 372 373 private static boolean isInNativeImage(String className) { 374 return className.startsWith("org.graalvm.nativeimage"); 375 } 376 377 private static boolean isGSON(String className) { 378 return className.contains("com.google.gson"); 379 } 380 381 private static List<Class<?>> initializeClasses(InvariantsTool tool, List<String> classNames) { 382 List<Class<?>> classes = new ArrayList<>(classNames.size()); 383 for (String className : classNames) { 384 if (!tool.shouldLoadClass(className)) { 385 continue; 386 } 387 try { 388 Class<?> c = Class.forName(className, true, CheckGraalInvariants.class.getClassLoader()); 389 classes.add(c); 390 } catch (Throwable t) { 391 tool.handleClassLoadingException(t); 392 } 393 } 394 return classes; 395 } 396 397 /** 398 * @param metaAccess 399 */ 400 private static void checkClass(Class<?> c, MetaAccessProvider metaAccess) { 401 if (Node.class.isAssignableFrom(c)) { 402 if (c.getAnnotation(NodeInfo.class) == null) { 403 throw new AssertionError(String.format("Node subclass %s requires %s annotation", c.getName(), NodeClass.class.getSimpleName())); 404 } 405 VerifyNodeCosts.verifyNodeClass(c); 406 } 407 } 408 409 private static void checkMethod(ResolvedJavaMethod method) { 410 if (method.getAnnotation(Snippet.class) == null) { 411 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 412 for (int i = 0; i < parameterAnnotations.length; i++) { 413 for (Annotation a : parameterAnnotations[i]) { 414 Class<? extends Annotation> annotationType = a.annotationType(); 415 if (annotationType == ConstantParameter.class || annotationType == VarargsParameter.class || annotationType == NonNullParameter.class) { 416 VerificationError verificationError = new VerificationError("Parameter %d of %s is annotated with %s but the method is not annotated with %s", i, method, 417 annotationType.getSimpleName(), 418 Snippet.class.getSimpleName()); 419 throw verificationError; 420 } 421 } 422 } 423 } 424 } 425 426 /** 427 * Checks the invariants for a single graph. 428 */ 429 private static void checkGraph(List<VerifyPhase<PhaseContext>> verifiers, HighTierContext context, StructuredGraph graph) { 430 for (VerifyPhase<PhaseContext> verifier : verifiers) { 431 if (!(verifier instanceof VerifyUsageWithEquals) || shouldVerifyEquals(graph.method())) { 432 verifier.apply(graph, context); 433 } else { 434 verifier.apply(graph, context); 435 } 436 } 437 if (graph.method().isBridge()) { 438 BridgeMethodUtils.getBridgedMethod(graph.method()); 439 } 440 } 441 442 private static boolean matches(String[] filters, String s) { 443 if (filters == null || filters.length == 0) { 444 return true; 445 } 446 for (String filter : filters) { 447 if (s.contains(filter)) { 448 return true; 449 } 450 } 451 return false; 452 } 453 454 private static String printStackTraceToString(Throwable t) { 455 StringWriter sw = new StringWriter(); 456 t.printStackTrace(new PrintWriter(sw)); 457 return sw.toString(); 458 } 459 460 static class BadUsageWithEquals { 461 Value aValue; 462 Register aRegister; 463 RegisterCategory aRegisterCategory; 464 JavaType aJavaType; 465 JavaField aJavaField; 466 JavaMethod aJavaMethod; 467 LocationIdentity aLocationIdentity; 468 LIRKind aLIRKind; 469 ArithmeticOpTable anArithmeticOpTable; 470 ArithmeticOpTable.Op anArithmeticOpTableOp; 471 472 static Value aStaticValue; 473 static Register aStaticRegister; 474 static RegisterCategory aStaticRegisterCategory; 475 static JavaType aStaticJavaType; 476 static JavaField aStaticJavaField; 477 static JavaMethod aStaticJavaMethod; 478 static LocationIdentity aStaticLocationIdentity; 479 static LIRKind aStaticLIRKind; 480 static ArithmeticOpTable aStaticArithmeticOpTable; 481 static ArithmeticOpTable.Op aStaticArithmeticOpTableOp; 482 483 boolean test01(Value f) { 484 return aValue == f; 485 } 486 487 boolean test02(Register f) { 488 return aRegister == f; 489 } 490 491 boolean test03(RegisterCategory f) { 492 return aRegisterCategory == f; 493 } 494 495 boolean test04(JavaType f) { 496 return aJavaType == f; 497 } 498 499 boolean test05(JavaField f) { 500 return aJavaField == f; 501 } 502 503 boolean test06(JavaMethod f) { 504 return aJavaMethod == f; 505 } 506 507 boolean test07(LocationIdentity f) { 508 return aLocationIdentity == f; 509 } 510 511 boolean test08(LIRKind f) { 512 return aLIRKind == f; 513 } 514 515 boolean test09(ArithmeticOpTable f) { 516 return anArithmeticOpTable == f; 517 } 518 519 boolean test10(ArithmeticOpTable.Op f) { 520 return anArithmeticOpTableOp == f; 521 } 522 523 boolean test12(Value f) { 524 return aStaticValue == f; 525 } 526 527 boolean test13(Register f) { 528 return aStaticRegister == f; 529 } 530 531 boolean test14(RegisterCategory f) { 532 return aStaticRegisterCategory == f; 533 } 534 535 boolean test15(JavaType f) { 536 return aStaticJavaType == f; 537 } 538 539 boolean test16(JavaField f) { 540 return aStaticJavaField == f; 541 } 542 543 boolean test17(JavaMethod f) { 544 return aStaticJavaMethod == f; 545 } 546 547 boolean test18(LocationIdentity f) { 548 return aStaticLocationIdentity == f; 549 } 550 551 boolean test19(LIRKind f) { 552 return aStaticLIRKind == f; 553 } 554 555 boolean test20(ArithmeticOpTable f) { 556 return aStaticArithmeticOpTable == f; 557 } 558 559 boolean test21(ArithmeticOpTable.Op f) { 560 return aStaticArithmeticOpTableOp == f; 561 } 562 } 563 }