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 */ 23 package org.graalvm.compiler.core.test; 24 25 import static org.graalvm.compiler.core.common.CompilationIdentifier.INVALID_COMPILATION_ID; 26 import static org.graalvm.compiler.debug.DelegatingDebugConfig.Feature.INTERCEPT; 27 28 import java.io.File; 29 import java.io.IOException; 30 import java.io.PrintWriter; 31 import java.io.StringWriter; 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.junit.Assert; 45 import org.junit.Assume; 46 import org.junit.Test; 47 48 import org.graalvm.compiler.api.test.Graal; 49 import org.graalvm.compiler.bytecode.BridgeMethodUtils; 50 import org.graalvm.compiler.core.CompilerThreadFactory; 51 import org.graalvm.compiler.core.CompilerThreadFactory.DebugConfigAccess; 52 import org.graalvm.compiler.core.common.LIRKind; 53 import org.graalvm.compiler.core.common.LocationIdentity; 54 import org.graalvm.compiler.core.common.type.ArithmeticOpTable; 55 import org.graalvm.compiler.debug.Debug; 56 import org.graalvm.compiler.debug.DebugConfigScope; 57 import org.graalvm.compiler.debug.DebugEnvironment; 58 import org.graalvm.compiler.debug.DelegatingDebugConfig; 59 import org.graalvm.compiler.debug.GraalDebugConfig; 60 import org.graalvm.compiler.graph.Node; 61 import org.graalvm.compiler.graph.NodeClass; 62 import org.graalvm.compiler.java.GraphBuilderPhase; 63 import org.graalvm.compiler.nodeinfo.NodeInfo; 64 import org.graalvm.compiler.nodes.PhiNode; 65 import org.graalvm.compiler.nodes.StructuredGraph; 66 import org.graalvm.compiler.nodes.StructuredGraph.AllowAssumptions; 67 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration; 68 import org.graalvm.compiler.nodes.graphbuilderconf.GraphBuilderConfiguration.Plugins; 69 import org.graalvm.compiler.nodes.graphbuilderconf.InvocationPlugins; 70 import org.graalvm.compiler.phases.OptimisticOptimizations; 71 import org.graalvm.compiler.phases.PhaseSuite; 72 import org.graalvm.compiler.phases.VerifyPhase; 73 import org.graalvm.compiler.phases.VerifyPhase.VerificationError; 74 import org.graalvm.compiler.phases.tiers.HighTierContext; 75 import org.graalvm.compiler.phases.util.Providers; 76 import org.graalvm.compiler.phases.verify.VerifyBailoutUsage; 77 import org.graalvm.compiler.phases.verify.VerifyCallerSensitiveMethods; 78 import org.graalvm.compiler.phases.verify.VerifyDebugUsage; 79 import org.graalvm.compiler.phases.verify.VerifyUpdateUsages; 80 import org.graalvm.compiler.phases.verify.VerifyUsageWithEquals; 81 import org.graalvm.compiler.phases.verify.VerifyVirtualizableUsage; 82 import org.graalvm.compiler.runtime.RuntimeProvider; 83 import org.graalvm.compiler.test.GraalTest; 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 GraalTest { 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 private static boolean shouldProcess(String classpathEntry) { 115 if (classpathEntry.endsWith(".jar")) { 116 String name = new File(classpathEntry).getName(); 117 return name.contains("jvmci") || name.contains("graal"); 118 } 119 return false; 120 } 121 122 @Test 123 @SuppressWarnings("try") 124 public void test() { 125 RuntimeProvider rt = Graal.getRequiredCapability(RuntimeProvider.class); 126 Providers providers = rt.getHostBackend().getProviders(); 127 MetaAccessProvider metaAccess = providers.getMetaAccess(); 128 129 PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>(); 130 Plugins plugins = new Plugins(new InvocationPlugins(metaAccess)); 131 GraphBuilderConfiguration config = GraphBuilderConfiguration.getDefault(plugins).withEagerResolving(true); 132 graphBuilderSuite.appendPhase(new GraphBuilderPhase(config)); 133 HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE); 134 135 Assume.assumeTrue(VerifyPhase.class.desiredAssertionStatus()); 136 137 String propertyName = Java8OrEarlier ? "sun.boot.class.path" : "jdk.module.path"; 138 String bootclasspath = System.getProperty(propertyName); 139 Assert.assertNotNull("Cannot find value of " + propertyName, bootclasspath); 140 141 final List<String> classNames = new ArrayList<>(); 142 for (String path : bootclasspath.split(File.pathSeparator)) { 143 if (shouldProcess(path)) { 144 try { 145 final ZipFile zipFile = new ZipFile(new File(path)); 146 for (final Enumeration<? extends ZipEntry> entry = zipFile.entries(); entry.hasMoreElements();) { 147 final ZipEntry zipEntry = entry.nextElement(); 148 String name = zipEntry.getName(); 149 if (name.endsWith(".class")) { 150 String className = name.substring(0, name.length() - ".class".length()).replace('/', '.'); 151 classNames.add(className); 152 } 153 } 154 } catch (IOException ex) { 155 Assert.fail(ex.toString()); 156 } 157 } 158 } 159 Assert.assertFalse("Could not find graal jars on boot class path: " + bootclasspath, classNames.isEmpty()); 160 161 // Allows a subset of methods to be checked through use of a system property 162 String property = System.getProperty(CheckGraalInvariants.class.getName() + ".filters"); 163 String[] filters = property == null ? null : property.split(","); 164 165 CompilerThreadFactory factory = new CompilerThreadFactory("CheckInvariantsThread", new DebugConfigAccess() { 166 @Override 167 public GraalDebugConfig getDebugConfig() { 168 return DebugEnvironment.initialize(System.out); 169 } 170 }); 171 int availableProcessors = Runtime.getRuntime().availableProcessors(); 172 ThreadPoolExecutor executor = new ThreadPoolExecutor(availableProcessors, availableProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory); 173 174 List<String> errors = Collections.synchronizedList(new ArrayList<>()); 175 // Order outer classes before the inner classes 176 classNames.sort((String a, String b) -> a.compareTo(b)); 177 // Initialize classes in single thread to avoid deadlocking issues during initialization 178 List<Class<?>> classes = initializeClasses(classNames); 179 for (Class<?> c : classes) { 180 String className = c.getName(); 181 executor.execute(() -> { 182 try { 183 checkClass(c, metaAccess); 184 } catch (Throwable e) { 185 errors.add(String.format("Error while checking %s:%n%s", className, printStackTraceToString(e))); 186 } 187 }); 188 189 for (Method m : c.getDeclaredMethods()) { 190 if (Modifier.isNative(m.getModifiers()) || Modifier.isAbstract(m.getModifiers())) { 191 // ignore 192 } else { 193 String methodName = className + "." + m.getName(); 194 if (matches(filters, methodName)) { 195 executor.execute(() -> { 196 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 197 StructuredGraph graph = new StructuredGraph(method, AllowAssumptions.NO, INVALID_COMPILATION_ID); 198 try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT)); Debug.Scope ds = Debug.scope("CheckingGraph", graph, method)) { 199 graphBuilderSuite.apply(graph, context); 200 // update phi stamps 201 graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp); 202 checkGraph(context, graph); 203 } catch (VerificationError e) { 204 errors.add(e.getMessage()); 205 } catch (LinkageError e) { 206 // suppress linkages errors resulting from eager resolution 207 } catch (BailoutException e) { 208 // Graal bail outs on certain patterns in Java bytecode (e.g., 209 // unbalanced monitors introduced by jacoco). 210 } catch (Throwable e) { 211 errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e))); 212 } 213 }); 214 } 215 } 216 } 217 } 218 executor.shutdown(); 219 try { 220 executor.awaitTermination(1, TimeUnit.HOURS); 221 } catch (InterruptedException e1) { 222 throw new RuntimeException(e1); 223 } 224 225 if (!errors.isEmpty()) { 226 StringBuilder msg = new StringBuilder(); 227 String nl = String.format("%n"); 228 for (String e : errors) { 229 if (msg.length() != 0) { 230 msg.append(nl); 231 } 232 msg.append(e); 233 } 234 Assert.fail(msg.toString()); 235 } 236 } 237 238 private static List<Class<?>> initializeClasses(List<String> classNames) { 239 List<Class<?>> classes = new ArrayList<>(classNames.size()); 240 for (String className : classNames) { 241 if (className.equals("module-info")) { 242 continue; 243 } 244 try { 245 Class<?> c = Class.forName(className, true, CheckGraalInvariants.class.getClassLoader()); 246 classes.add(c); 247 } catch (ClassNotFoundException e) { 248 e.printStackTrace(); 249 } 250 } 251 return classes; 252 } 253 254 /** 255 * @param metaAccess 256 */ 257 private static void checkClass(Class<?> c, MetaAccessProvider metaAccess) { 258 if (Node.class.isAssignableFrom(c)) { 259 if (c.getAnnotation(NodeInfo.class) == null) { 260 throw new AssertionError(String.format("Node subclass %s requires %s annotation", c.getName(), NodeClass.class.getSimpleName())); 261 } 262 } 263 } 264 265 /** 266 * Checks the invariants for a single graph. 267 */ 268 private static void checkGraph(HighTierContext context, StructuredGraph graph) { 269 if (shouldVerifyEquals(graph.method())) { 270 new VerifyUsageWithEquals(Value.class).apply(graph, context); 271 new VerifyUsageWithEquals(Register.class).apply(graph, context); 272 new VerifyUsageWithEquals(RegisterCategory.class).apply(graph, context); 273 new VerifyUsageWithEquals(JavaType.class).apply(graph, context); 274 new VerifyUsageWithEquals(JavaMethod.class).apply(graph, context); 275 new VerifyUsageWithEquals(JavaField.class).apply(graph, context); 276 new VerifyUsageWithEquals(LocationIdentity.class).apply(graph, context); 277 new VerifyUsageWithEquals(LIRKind.class).apply(graph, context); 278 new VerifyUsageWithEquals(ArithmeticOpTable.class).apply(graph, context); 279 new VerifyUsageWithEquals(ArithmeticOpTable.Op.class).apply(graph, context); 280 } 281 new VerifyDebugUsage().apply(graph, context); 282 new VerifyCallerSensitiveMethods().apply(graph, context); 283 new VerifyVirtualizableUsage().apply(graph, context); 284 new VerifyUpdateUsages().apply(graph, context); 285 new VerifyBailoutUsage().apply(graph, context); 286 if (graph.method().isBridge()) { 287 BridgeMethodUtils.getBridgedMethod(graph.method()); 288 } 289 } 290 291 private static boolean matches(String[] filters, String s) { 292 if (filters == null || filters.length == 0) { 293 return true; 294 } 295 for (String filter : filters) { 296 if (s.contains(filter)) { 297 return true; 298 } 299 } 300 return false; 301 } 302 303 private static String printStackTraceToString(Throwable t) { 304 StringWriter sw = new StringWriter(); 305 t.printStackTrace(new PrintWriter(sw)); 306 return sw.toString(); 307 } 308 }