001/* 002 * Copyright (c) 2014, Oracle and/or its affiliates. All rights reserved. 003 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 004 * 005 * This code is free software; you can redistribute it and/or modify it 006 * under the terms of the GNU General Public License version 2 only, as 007 * published by the Free Software Foundation. 008 * 009 * This code is distributed in the hope that it will be useful, but WITHOUT 010 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 011 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 012 * version 2 for more details (a copy is included in the LICENSE file that 013 * accompanied this code). 014 * 015 * You should have received a copy of the GNU General Public License version 016 * 2 along with this work; if not, write to the Free Software Foundation, 017 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 018 * 019 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 020 * or visit www.oracle.com if you need additional information or have any 021 * questions. 022 */ 023package com.oracle.graal.compiler.test; 024 025import static com.oracle.graal.debug.DelegatingDebugConfig.Feature.*; 026 027import java.io.*; 028import java.lang.reflect.*; 029import java.util.*; 030import java.util.concurrent.*; 031import java.util.zip.*; 032 033import jdk.internal.jvmci.code.*; 034import jdk.internal.jvmci.code.Register.RegisterCategory; 035import jdk.internal.jvmci.meta.*; 036 037import org.junit.*; 038 039import com.oracle.graal.api.runtime.*; 040import com.oracle.graal.compiler.*; 041import com.oracle.graal.compiler.CompilerThreadFactory.DebugConfigAccess; 042import com.oracle.graal.compiler.common.type.*; 043import com.oracle.graal.debug.*; 044import com.oracle.graal.graph.*; 045import com.oracle.graal.graphbuilderconf.*; 046import com.oracle.graal.graphbuilderconf.GraphBuilderConfiguration.Plugins; 047import com.oracle.graal.java.*; 048import com.oracle.graal.nodeinfo.*; 049import com.oracle.graal.nodes.*; 050import com.oracle.graal.nodes.StructuredGraph.AllowAssumptions; 051import com.oracle.graal.phases.*; 052import com.oracle.graal.phases.VerifyPhase.VerificationError; 053import com.oracle.graal.phases.tiers.*; 054import com.oracle.graal.phases.util.*; 055import com.oracle.graal.phases.verify.*; 056import com.oracle.graal.runtime.*; 057import com.oracle.graal.test.*; 058 059/** 060 * Checks that all classes in *graal*.jar and *jvmci*.jar entries on the boot class path comply with 061 * global invariants such as using {@link Object#equals(Object)} to compare certain types instead of 062 * identity comparisons. 063 */ 064public class CheckGraalInvariants extends GraalTest { 065 066 private static boolean shouldVerifyEquals(ResolvedJavaMethod m) { 067 if (m.getName().equals("identityEquals")) { 068 ResolvedJavaType c = m.getDeclaringClass(); 069 if (c.getName().equals("Ljdk/internal/jvmci/meta/AbstractValue;") || c.getName().equals("jdk/internal/jvmci/meta/Value")) { 070 return false; 071 } 072 } 073 074 return true; 075 } 076 077 private static boolean shouldProcess(String classpathEntry) { 078 if (classpathEntry.endsWith(".jar")) { 079 String name = new File(classpathEntry).getName(); 080 return name.contains("jvmci") || name.contains("graal"); 081 } 082 return false; 083 } 084 085 @Test 086 public void test() { 087 RuntimeProvider rt = Graal.getRequiredCapability(RuntimeProvider.class); 088 Providers providers = rt.getHostBackend().getProviders(); 089 MetaAccessProvider metaAccess = providers.getMetaAccess(); 090 091 PhaseSuite<HighTierContext> graphBuilderSuite = new PhaseSuite<>(); 092 GraphBuilderConfiguration config = GraphBuilderConfiguration.getEagerDefault(new Plugins(new InvocationPlugins(metaAccess))); 093 graphBuilderSuite.appendPhase(new GraphBuilderPhase(config)); 094 HighTierContext context = new HighTierContext(providers, graphBuilderSuite, OptimisticOptimizations.NONE); 095 096 Assume.assumeTrue(VerifyPhase.class.desiredAssertionStatus()); 097 098 String bootclasspath = System.getProperty("sun.boot.class.path"); 099 Assert.assertNotNull("Cannot find value of boot class path", bootclasspath); 100 101 bootclasspath.split(File.pathSeparator); 102 103 final List<String> classNames = new ArrayList<>(); 104 for (String path : bootclasspath.split(File.pathSeparator)) { 105 if (shouldProcess(path)) { 106 try { 107 final ZipFile zipFile = new ZipFile(new File(path)); 108 for (final Enumeration<? extends ZipEntry> entry = zipFile.entries(); entry.hasMoreElements();) { 109 final ZipEntry zipEntry = entry.nextElement(); 110 String name = zipEntry.getName(); 111 if (name.endsWith(".class")) { 112 String className = name.substring(0, name.length() - ".class".length()).replace('/', '.'); 113 classNames.add(className); 114 } 115 } 116 } catch (IOException ex) { 117 Assert.fail(ex.toString()); 118 } 119 } 120 } 121 Assert.assertFalse("Could not find graal jars on boot class path: " + bootclasspath, classNames.isEmpty()); 122 123 // Allows a subset of methods to be checked through use of a system property 124 String property = System.getProperty(CheckGraalInvariants.class.getName() + ".filters"); 125 String[] filters = property == null ? null : property.split(","); 126 127 CompilerThreadFactory factory = new CompilerThreadFactory("CheckInvariantsThread", new DebugConfigAccess() { 128 public GraalDebugConfig getDebugConfig() { 129 return DebugEnvironment.initialize(System.out); 130 } 131 }); 132 int availableProcessors = Runtime.getRuntime().availableProcessors(); 133 ThreadPoolExecutor executor = new ThreadPoolExecutor(availableProcessors, availableProcessors, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>(), factory); 134 135 List<String> errors = Collections.synchronizedList(new ArrayList<>()); 136 for (String className : classNames) { 137 try { 138 Class<?> c = Class.forName(className, false, CheckGraalInvariants.class.getClassLoader()); 139 executor.execute(() -> { 140 try { 141 checkClass(c, metaAccess); 142 } catch (Throwable e) { 143 errors.add(String.format("Error while checking %s:%n%s", className, printStackTraceToString(e))); 144 } 145 }); 146 147 for (Method m : c.getDeclaredMethods()) { 148 if (Modifier.isNative(m.getModifiers()) || Modifier.isAbstract(m.getModifiers())) { 149 // ignore 150 } else { 151 String methodName = className + "." + m.getName(); 152 if (matches(filters, methodName)) { 153 executor.execute(() -> { 154 ResolvedJavaMethod method = metaAccess.lookupJavaMethod(m); 155 StructuredGraph graph = new StructuredGraph(method, AllowAssumptions.NO); 156 try (DebugConfigScope s = Debug.setConfig(new DelegatingDebugConfig().disable(INTERCEPT)); Debug.Scope ds = Debug.scope("CheckingGraph", graph, method)) { 157 graphBuilderSuite.apply(graph, context); 158 // update phi stamps 159 graph.getNodes().filter(PhiNode.class).forEach(PhiNode::inferStamp); 160 checkGraph(context, graph); 161 } catch (VerificationError e) { 162 errors.add(e.getMessage()); 163 } catch (LinkageError e) { 164 // suppress linkages errors resulting from eager resolution 165 } catch (BailoutException e) { 166 // Graal bail outs on certain patterns in Java bytecode (e.g., 167 // unbalanced monitors introduced by jacoco). 168 } catch (Throwable e) { 169 errors.add(String.format("Error while checking %s:%n%s", methodName, printStackTraceToString(e))); 170 } 171 }); 172 } 173 } 174 } 175 176 } catch (ClassNotFoundException e) { 177 e.printStackTrace(); 178 } 179 } 180 executor.shutdown(); 181 try { 182 executor.awaitTermination(1, TimeUnit.HOURS); 183 } catch (InterruptedException e1) { 184 throw new RuntimeException(e1); 185 } 186 187 if (!errors.isEmpty()) { 188 StringBuilder msg = new StringBuilder(); 189 String nl = String.format("%n"); 190 for (String e : errors) { 191 if (msg.length() != 0) { 192 msg.append(nl); 193 } 194 msg.append(e); 195 } 196 Assert.fail(msg.toString()); 197 } 198 } 199 200 /** 201 * @param metaAccess 202 */ 203 private static void checkClass(Class<?> c, MetaAccessProvider metaAccess) { 204 if (Node.class.isAssignableFrom(c)) { 205 if (c.getAnnotation(NodeInfo.class) == null) { 206 throw new AssertionError(String.format("Node subclass %s requires %s annotation", c.getName(), NodeClass.class.getSimpleName())); 207 } 208 } 209 } 210 211 /** 212 * Checks the invariants for a single graph. 213 */ 214 private static void checkGraph(HighTierContext context, StructuredGraph graph) { 215 if (shouldVerifyEquals(graph.method())) { 216 new VerifyUsageWithEquals(Value.class).apply(graph, context); 217 new VerifyUsageWithEquals(Register.class).apply(graph, context); 218 new VerifyUsageWithEquals(RegisterCategory.class).apply(graph, context); 219 new VerifyUsageWithEquals(JavaType.class).apply(graph, context); 220 new VerifyUsageWithEquals(JavaMethod.class).apply(graph, context); 221 new VerifyUsageWithEquals(JavaField.class).apply(graph, context); 222 new VerifyUsageWithEquals(LocationIdentity.class).apply(graph, context); 223 new VerifyUsageWithEquals(LIRKind.class).apply(graph, context); 224 new VerifyUsageWithEquals(ArithmeticOpTable.class).apply(graph, context); 225 new VerifyUsageWithEquals(ArithmeticOpTable.Op.class).apply(graph, context); 226 } 227 new VerifyDebugUsage().apply(graph, context); 228 } 229 230 private static boolean matches(String[] filters, String s) { 231 if (filters == null || filters.length == 0) { 232 return true; 233 } 234 for (String filter : filters) { 235 if (s.contains(filter)) { 236 return true; 237 } 238 } 239 return false; 240 } 241 242 private static String printStackTraceToString(Throwable t) { 243 StringWriter sw = new StringWriter(); 244 t.printStackTrace(new PrintWriter(sw)); 245 return sw.toString(); 246 } 247}