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