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 } else { 161 if (className.equals("jdk.vm.ci.services.JVMCIClassLoaderFactory")) { 162 return false; 163 } 164 } 165 return true; 166 } 167 168 protected void handleClassLoadingException(Throwable t) { 169 GraalError.shouldNotReachHere(t); 170 } 171 172 protected void handleParsingException(Throwable t) { 173 GraalError.shouldNotReachHere(t); 174 } 175 176 public boolean shouldVerifyFoldableMethods() { 177 return true; 178 } 179 } 180 181 @Test 182 @SuppressWarnings("try") 183 public void test() { 184 assumeManagementLibraryIsLoadable(); 185 runTest(new InvariantsTool()); 186 } 187 188 @SuppressWarnings("try") 189 public static void runTest(InvariantsTool tool) { 190 RuntimeProvider rt = Graal.getRequiredCapability(RuntimeProvider.class); 191 Providers providers = rt.getHostBackend().getProviders(); 192 MetaAccessProvider metaAccess = providers.getMetaAccess(); 193 194 PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>(); 195 Plugins plugins = new Plugins(new InvocationPlugins()); 196 GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true).withUnresolvedIsError(true); 197 graphBuilderSuite.appendPhase(new GraphBuilderPhase(config)); 198 HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE); 199 200 Assume.assumeTrue(VerifyPhase.class.desiredAssertionStatus()); 201 202 String bootclasspath = tool.getClassPath(); 203 Assert.assertNotNull("Cannot find boot class path", bootclasspath); 204 205 final List<String> classNames = new ArrayList<>(); 206 for (String path : bootclasspath.split(File.pathSeparator)) { 207 if (tool.shouldProcess(path)) { 208 try { 209 final ZipFile zipFile = new ZipFile(new File(path)); 210 for (final Enumeration<? extends ZipEntry> entry = zipFile.entries(); entry.hasMoreElements();) { 211 final ZipEntry zipEntry = entry.nextElement(); 212 String name = zipEntry.getName(); 213 if (name.endsWith(".class") && !name.startsWith("META-INF/versions/")) { 214 String className = name.substring(0, name.length() - ".class".length()).replace('/', '.'); 215 if (isInNativeImage(className)) { 216 /* 217 * Native Image is an external tool and does not need to follow the 218 * Graal invariants. 219 */ 220 continue; 221 } 222 if (isGSON(className)) { 223 /* 224 * GSON classes are compiled with old JDK 225 */ 226 continue; 227 } 228 classNames.add(className); 229 } 230 } 231 } catch (IOException ex) { 232 Assert.fail(ex.toString()); 233 } 234 } 235 } 236 Assert.assertFalse("Could not find graal jars on boot class path: " + bootclasspath, classNames.isEmpty()); 237 238 // Allows a subset of methods to be checked through use of a system property 239 String property = System.getProperty(CheckGraalInvariants.class.getName() + ".filters"); 240 String[] filters = property == null ? null : property.split(","); 241 242 OptionValues options = getInitialOptions(); 243 CompilerThreadFactory factory = new CompilerThreadFactory("CheckInvariantsThread"); 244 int availableProcessors = Runtime.getRuntime().availableProcessors(); 245 ThreadPoolExecutor executor = new ThreadPoolExecutor(availableProcessors, availableProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory); 246 247 List<String> errors = Collections.synchronizedList(new ArrayList<>()); 248 249 List<VerifyPhase<PhaseContext>> verifiers = new ArrayList<>(); 250 251 // If you add a new type to test here, be sure to add appropriate 252 // methods to the BadUsageWithEquals class below 253 verifiers.add(new VerifyUsageWithEquals(Value.class)); 254 verifiers.add(new VerifyUsageWithEquals(Register.class)); 255 verifiers.add(new VerifyUsageWithEquals(RegisterCategory.class)); 256 verifiers.add(new VerifyUsageWithEquals(JavaType.class)); 257 verifiers.add(new VerifyUsageWithEquals(JavaMethod.class)); 258 verifiers.add(new VerifyUsageWithEquals(JavaField.class)); 259 verifiers.add(new VerifyUsageWithEquals(LocationIdentity.class)); 260 verifiers.add(new VerifyUsageWithEquals(LIRKind.class)); 261 verifiers.add(new VerifyUsageWithEquals(ArithmeticOpTable.class)); 262 verifiers.add(new VerifyUsageWithEquals(ArithmeticOpTable.Op.class)); 263 264 verifiers.add(new VerifyDebugUsage()); 265 verifiers.add(new VerifyCallerSensitiveMethods()); 266 verifiers.add(new VerifyVirtualizableUsage()); 267 verifiers.add(new VerifyUpdateUsages()); 268 verifiers.add(new VerifyBailoutUsage()); 269 verifiers.add(new VerifyInstanceOfUsage()); 270 verifiers.add(new VerifyGraphAddUsage()); 271 verifiers.add(new VerifyGetOptionsUsage()); 272 273 VerifyFoldableMethods foldableMethodsVerifier = new VerifyFoldableMethods(); 274 if (tool.shouldVerifyFoldableMethods()) { 275 verifiers.add(foldableMethodsVerifier); 276 } 277 278 for (Method m : BadUsageWithEquals.class.getDeclaredMethods()) { 279 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 280 try (DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER)) { 281 StructuredGraph graph = new StructuredGraph.Builder(options, debug, AllowAssumptions.YES).method(method).build(); 282 try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("CheckingGraph", graph, method)) { 283 graphBuilderSuite.apply(graph, context); 284 // update phi stamps 285 graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp); 286 checkGraph(verifiers, context, graph); 287 errors.add(String.format("Expected error while checking %s", m)); 288 } catch (VerificationError e) { 289 // expected! 290 } catch (Throwable e) { 291 errors.add(String.format("Error while checking %s:%n%s", m, printStackTraceToString(e))); 292 } 293 } 294 } 295 if (errors.isEmpty()) { 296 // Order outer classes before the inner classes 297 classNames.sort((String a, String b) -> a.compareTo(b)); 298 // Initialize classes in single thread to avoid deadlocking issues during initialization 299 List<Class<?>> classes = initializeClasses(tool, classNames); 300 for (Class<?> c : classes) { 301 String className = c.getName(); 302 executor.execute(() -> { 303 try { 304 checkClass(c, metaAccess); 305 } catch (Throwable e) { 306 errors.add(String.format("Error while checking %s:%n%s", className, printStackTraceToString(e))); 307 } 308 }); 309 310 for (Method m : c.getDeclaredMethods()) { 311 if (Modifier.isNative(m.getModifiers()) || Modifier.isAbstract(m.getModifiers())) { 312 // ignore 313 } else { 314 String methodName = className + "." + m.getName(); 315 if (matches(filters, methodName)) { 316 executor.execute(() -> { 317 try (DebugContext debug = DebugContext.create(options, DebugHandlersFactory.LOADER)) { 318 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 319 boolean isSubstitution = method.getAnnotation(Snippet.class) != null || method.getAnnotation(MethodSubstitution.class) != null; 320 StructuredGraph graph = new StructuredGraph.Builder(options, debug).method(method).setIsSubstitution(isSubstitution).build(); 321 try (DebugCloseable s = debug.disableIntercept(); DebugContext.Scope ds = debug.scope("CheckingGraph", graph, method)) { 322 checkMethod(method); 323 graphBuilderSuite.apply(graph, context); 324 // update phi stamps 325 graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp); 326 checkGraph(verifiers, context, graph); 327 } catch (VerificationError e) { 328 errors.add(e.getMessage()); 329 } catch (LinkageError e) { 330 // suppress linkages errors resulting from eager resolution 331 } catch (BailoutException e) { 332 // Graal bail outs on certain patterns in Java bytecode 333 // (e.g., 334 // unbalanced monitors introduced by jacoco). 335 } catch (Throwable e) { 336 try { 337 tool.handleParsingException(e); 338 } catch (Throwable t) { 339 errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e))); 340 } 341 } 342 } 343 }); 344 } 345 } 346 } 347 } 348 349 executor.shutdown(); 350 try { 351 executor.awaitTermination(1, TimeUnit.HOURS); 352 } catch (InterruptedException e1) { 353 throw new RuntimeException(e1); 354 } 355 356 if (tool.shouldVerifyFoldableMethods()) { 357 try { 358 foldableMethodsVerifier.finish(); 359 } catch (Throwable e) { 360 errors.add(e.getMessage()); 361 } 362 } 363 } 364 if (!errors.isEmpty()) { 365 StringBuilder msg = new StringBuilder(); 366 String nl = String.format("%n"); 367 for (String e : errors) { 368 if (msg.length() != 0) { 369 msg.append(nl); 370 } 371 msg.append(e); 372 } 373 Assert.fail(msg.toString()); 374 } 375 } 376 377 private static boolean isInNativeImage(String className) { 378 return className.startsWith("org.graalvm.nativeimage"); 379 } 380 381 private static boolean isGSON(String className) { 382 return className.contains("com.google.gson"); 383 } 384 385 private static List<Class<?>> initializeClasses(InvariantsTool tool, List<String> classNames) { 386 List<Class<?>> classes = new ArrayList<>(classNames.size()); 387 for (String className : classNames) { 388 if (!tool.shouldLoadClass(className)) { 389 continue; 390 } 391 try { 392 Class<?> c = Class.forName(className, true, CheckGraalInvariants.class.getClassLoader()); 393 classes.add(c); 394 } catch (Throwable t) { 395 tool.handleClassLoadingException(t); 396 } 397 } 398 return classes; 399 } 400 401 /** 402 * @param metaAccess 403 */ 404 private static void checkClass(Class<?> c, MetaAccessProvider metaAccess) { 405 if (Node.class.isAssignableFrom(c)) { 406 if (c.getAnnotation(NodeInfo.class) == null) { 407 throw new AssertionError(String.format("Node subclass %s requires %s annotation", c.getName(), NodeClass.class.getSimpleName())); 408 } 409 VerifyNodeCosts.verifyNodeClass(c); 410 } 411 } 412 413 private static void checkMethod(ResolvedJavaMethod method) { 414 if (method.getAnnotation(Snippet.class) == null) { 415 Annotation[][] parameterAnnotations = method.getParameterAnnotations(); 416 for (int i = 0; i < parameterAnnotations.length; i++) { 417 for (Annotation a : parameterAnnotations[i]) { 418 Class<? extends Annotation> annotationType = a.annotationType(); 419 if (annotationType == ConstantParameter.class || annotationType == VarargsParameter.class || annotationType == NonNullParameter.class) { 420 VerificationError verificationError = new VerificationError("Parameter %d of %s is annotated with %s but the method is not annotated with %s", i, method, 421 annotationType.getSimpleName(), 422 Snippet.class.getSimpleName()); 423 throw verificationError; 424 } 425 } 426 } 427 } 428 } 429 430 /** 431 * Checks the invariants for a single graph. 432 */ 433 private static void checkGraph(List<VerifyPhase<PhaseContext>> verifiers, HighTierContext context, StructuredGraph graph) { 434 for (VerifyPhase<PhaseContext> verifier : verifiers) { 435 if (!(verifier instanceof VerifyUsageWithEquals) || shouldVerifyEquals(graph.method())) { 436 verifier.apply(graph, context); 437 } else { 438 verifier.apply(graph, context); 439 } 440 } 441 if (graph.method().isBridge()) { 442 BridgeMethodUtils.getBridgedMethod(graph.method()); 443 } 444 } 445 446 private static boolean matches(String[] filters, String s) { 447 if (filters == null || filters.length == 0) { 448 return true; 449 } 450 for (String filter : filters) { 451 if (s.contains(filter)) { 452 return true; 453 } 454 } 455 return false; 456 } 457 458 private static String printStackTraceToString(Throwable t) { 459 StringWriter sw = new StringWriter(); 460 t.printStackTrace(new PrintWriter(sw)); 461 return sw.toString(); 462 } 463 464 static class BadUsageWithEquals { 465 Value aValue; 466 Register aRegister; 467 RegisterCategory aRegisterCategory; 468 JavaType aJavaType; 469 JavaField aJavaField; 470 JavaMethod aJavaMethod; 471 LocationIdentity aLocationIdentity; 472 LIRKind aLIRKind; 473 ArithmeticOpTable anArithmeticOpTable; 474 ArithmeticOpTable.Op anArithmeticOpTableOp; 475 476 static Value aStaticValue; 477 static Register aStaticRegister; 478 static RegisterCategory aStaticRegisterCategory; 479 static JavaType aStaticJavaType; 480 static JavaField aStaticJavaField; 481 static JavaMethod aStaticJavaMethod; 482 static LocationIdentity aStaticLocationIdentity; 483 static LIRKind aStaticLIRKind; 484 static ArithmeticOpTable aStaticArithmeticOpTable; 485 static ArithmeticOpTable.Op aStaticArithmeticOpTableOp; 486 487 boolean test01(Value f) { 488 return aValue == f; 489 } 490 491 boolean test02(Register f) { 492 return aRegister == f; 493 } 494 495 boolean test03(RegisterCategory f) { 496 return aRegisterCategory == f; 497 } 498 499 boolean test04(JavaType f) { 500 return aJavaType == f; 501 } 502 503 boolean test05(JavaField f) { 504 return aJavaField == f; 505 } 506 507 boolean test06(JavaMethod f) { 508 return aJavaMethod == f; 509 } 510 511 boolean test07(LocationIdentity f) { 512 return aLocationIdentity == f; 513 } 514 515 boolean test08(LIRKind f) { 516 return aLIRKind == f; 517 } 518 519 boolean test09(ArithmeticOpTable f) { 520 return anArithmeticOpTable == f; 521 } 522 523 boolean test10(ArithmeticOpTable.Op f) { 524 return anArithmeticOpTableOp == f; 525 } 526 527 boolean test12(Value f) { 528 return aStaticValue == f; 529 } 530 531 boolean test13(Register f) { 532 return aStaticRegister == f; 533 } 534 535 boolean test14(RegisterCategory f) { 536 return aStaticRegisterCategory == f; 537 } 538 539 boolean test15(JavaType f) { 540 return aStaticJavaType == f; 541 } 542 543 boolean test16(JavaField f) { 544 return aStaticJavaField == f; 545 } 546 547 boolean test17(JavaMethod f) { 548 return aStaticJavaMethod == f; 549 } 550 551 boolean test18(LocationIdentity f) { 552 return aStaticLocationIdentity == f; 553 } 554 555 boolean test19(LIRKind f) { 556 return aStaticLIRKind == f; 557 } 558 559 boolean test20(ArithmeticOpTable f) { 560 return aStaticArithmeticOpTable == f; 561 } 562 563 boolean test21(ArithmeticOpTable.Op f) { 564 return aStaticArithmeticOpTableOp == f; 565 } 566 } 567 }