1 /* 2 * Copyright (c) 2011, 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.printer; 24 25 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.CanonicalGraphStringsCheckConstants; 26 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.CanonicalGraphStringsExcludeVirtuals; 27 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.CanonicalGraphStringsRemoveIdentities; 28 import static org.graalvm.compiler.debug.GraalDebugConfig.Options.PrintCanonicalGraphStringFlavor; 29 30 import java.io.BufferedWriter; 31 import java.io.FileWriter; 32 import java.io.IOException; 33 import java.io.PrintWriter; 34 import java.io.StringWriter; 35 import java.nio.file.Path; 36 import java.util.ArrayList; 37 import java.util.Collections; 38 import java.util.Iterator; 39 import java.util.List; 40 import java.util.Map; 41 import java.util.regex.Pattern; 42 43 import org.graalvm.compiler.api.replacements.SnippetReflectionProvider; 44 import org.graalvm.compiler.core.common.Fields; 45 import org.graalvm.compiler.debug.TTY; 46 import org.graalvm.compiler.graph.Graph; 47 import org.graalvm.compiler.graph.Node; 48 import org.graalvm.compiler.graph.NodeMap; 49 import org.graalvm.compiler.graph.Position; 50 import org.graalvm.compiler.nodeinfo.Verbosity; 51 import org.graalvm.compiler.nodes.ConstantNode; 52 import org.graalvm.compiler.nodes.FixedNode; 53 import org.graalvm.compiler.nodes.FixedWithNextNode; 54 import org.graalvm.compiler.nodes.FrameState; 55 import org.graalvm.compiler.nodes.FullInfopointNode; 56 import org.graalvm.compiler.nodes.PhiNode; 57 import org.graalvm.compiler.nodes.ProxyNode; 58 import org.graalvm.compiler.nodes.StructuredGraph; 59 import org.graalvm.compiler.nodes.ValueNode; 60 import org.graalvm.compiler.nodes.cfg.Block; 61 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph; 62 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode; 63 import org.graalvm.compiler.options.OptionValues; 64 import org.graalvm.compiler.phases.schedule.SchedulePhase; 65 66 import jdk.vm.ci.meta.ResolvedJavaMethod; 67 68 public class CanonicalStringGraphPrinter implements GraphPrinter { 69 private static final Pattern IDENTITY_PATTERN = Pattern.compile("([A-Za-z0-9$_]+)@[0-9a-f]+"); 70 private Path currentDirectory; 71 private Path root; 72 private SnippetReflectionProvider snippetReflection; 73 74 public CanonicalStringGraphPrinter(Path directory) { 75 this.currentDirectory = directory; 76 this.root = directory; 77 } 78 79 @Override 80 public void setSnippetReflectionProvider(SnippetReflectionProvider snippetReflection) { 81 this.snippetReflection = snippetReflection; 82 } 83 84 @Override 85 public SnippetReflectionProvider getSnippetReflectionProvider() { 86 return snippetReflection; 87 } 88 89 protected static String escapeFileName(String name) { 90 byte[] bytes = name.getBytes(); 91 StringBuilder sb = new StringBuilder(); 92 for (byte b : bytes) { 93 if ((b >= 'a' && b <= 'z') || (b >= 'A' && b <= 'Z') || (b >= '0' && b <= '9') || b == '.' || b == '-' || b == '_') { 94 sb.append((char) b); 95 } else { 96 sb.append('%'); 97 sb.append(Integer.toHexString(b)); 98 } 99 } 100 return sb.toString(); 101 } 102 103 private static String removeIdentities(String str) { 104 return IDENTITY_PATTERN.matcher(str).replaceAll("$1"); 105 } 106 107 protected static void writeCanonicalGraphExpressionString(ValueNode node, boolean checkConstants, boolean removeIdentities, PrintWriter writer) { 108 writer.print(node.getClass().getSimpleName()); 109 writer.print("("); 110 Fields properties = node.getNodeClass().getData(); 111 for (int i = 0; i < properties.getCount(); i++) { 112 String dataStr = String.valueOf(properties.get(node, i)); 113 if (removeIdentities) { 114 dataStr = removeIdentities(dataStr); 115 } 116 writer.print(dataStr); 117 if (i + 1 < properties.getCount() || node.inputPositions().iterator().hasNext()) { 118 writer.print(", "); 119 } 120 } 121 Iterator<Position> iterator = node.inputPositions().iterator(); 122 while (iterator.hasNext()) { 123 Position position = iterator.next(); 124 Node input = position.get(node); 125 if (checkConstants && input instanceof ConstantNode) { 126 ConstantNode constantNode = (ConstantNode) input; 127 String valueString = constantNode.getValue().toValueString(); 128 if (removeIdentities) { 129 valueString = removeIdentities(valueString); 130 } 131 writer.print(valueString); 132 } else if (input instanceof ValueNode && !(input instanceof PhiNode) && !(input instanceof FixedNode)) { 133 writeCanonicalGraphExpressionString((ValueNode) input, checkConstants, removeIdentities, writer); 134 } else if (input == null) { 135 writer.print("null"); 136 } else { 137 writer.print(input.getClass().getSimpleName()); 138 } 139 if (iterator.hasNext()) { 140 writer.print(", "); 141 } 142 } 143 writer.print(")"); 144 } 145 146 protected static void writeCanonicalExpressionCFGString(StructuredGraph graph, boolean checkConstants, boolean removeIdentities, PrintWriter writer) { 147 ControlFlowGraph controlFlowGraph = ControlFlowGraph.compute(graph, true, true, false, false); 148 for (Block block : controlFlowGraph.getBlocks()) { 149 writer.print("Block "); 150 writer.print(block); 151 writer.print(" "); 152 if (block == controlFlowGraph.getStartBlock()) { 153 writer.print("* "); 154 } 155 writer.print("-> "); 156 for (Block successor : block.getSuccessors()) { 157 writer.print(successor); 158 writer.print(" "); 159 } 160 writer.println(); 161 FixedNode node = block.getBeginNode(); 162 while (node != null) { 163 writeCanonicalGraphExpressionString(node, checkConstants, removeIdentities, writer); 164 writer.println(); 165 if (node instanceof FixedWithNextNode) { 166 node = ((FixedWithNextNode) node).next(); 167 } else { 168 node = null; 169 } 170 } 171 } 172 } 173 174 protected static void writeCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants, PrintWriter writer) { 175 SchedulePhase schedule = new SchedulePhase(SchedulePhase.SchedulingStrategy.EARLIEST); 176 schedule.apply(graph); 177 StructuredGraph.ScheduleResult scheduleResult = graph.getLastSchedule(); 178 179 NodeMap<Integer> canonicalId = graph.createNodeMap(); 180 int nextId = 0; 181 182 List<String> constantsLines = null; 183 if (checkConstants) { 184 constantsLines = new ArrayList<>(); 185 } 186 187 for (Block block : scheduleResult.getCFG().getBlocks()) { 188 writer.print("Block "); 189 writer.print(block); 190 writer.print(" "); 191 if (block == scheduleResult.getCFG().getStartBlock()) { 192 writer.print("* "); 193 } 194 writer.print("-> "); 195 for (Block successor : block.getSuccessors()) { 196 writer.print(successor); 197 writer.print(" "); 198 } 199 writer.println(); 200 for (Node node : scheduleResult.getBlockToNodesMap().get(block)) { 201 if (node instanceof ValueNode && node.isAlive()) { 202 if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof FullInfopointNode)) { 203 if (node instanceof ConstantNode) { 204 if (constantsLines != null) { 205 String name = node.toString(Verbosity.Name); 206 String str = name + (excludeVirtual ? "" : " (" + filteredUsageCount(node) + ")"); 207 constantsLines.add(str); 208 } 209 } else { 210 int id; 211 if (canonicalId.get(node) != null) { 212 id = canonicalId.get(node); 213 } else { 214 id = nextId++; 215 canonicalId.set(node, id); 216 } 217 String name = node.getClass().getSimpleName(); 218 writer.print(" "); 219 writer.print(id); 220 writer.print("|"); 221 writer.print(name); 222 if (!excludeVirtual) { 223 writer.print(" ("); 224 writer.print(filteredUsageCount(node)); 225 writer.print(")"); 226 } 227 writer.println(); 228 } 229 } 230 } 231 } 232 } 233 if (constantsLines != null) { 234 writer.print(constantsLines.size()); 235 writer.println(" constants:"); 236 Collections.sort(constantsLines); 237 for (String s : constantsLines) { 238 writer.println(s); 239 } 240 } 241 } 242 243 public static String getCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants) { 244 StringWriter stringWriter = new StringWriter(); 245 PrintWriter writer = new PrintWriter(stringWriter); 246 writeCanonicalGraphString(graph, excludeVirtual, checkConstants, writer); 247 writer.flush(); 248 return stringWriter.toString(); 249 } 250 251 private static int filteredUsageCount(Node node) { 252 return node.usages().filter(n -> !(n instanceof FrameState)).count(); 253 } 254 255 @Override 256 public void beginGroup(String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException { 257 currentDirectory = currentDirectory.resolve(escapeFileName(name)); 258 } 259 260 @Override 261 public void print(Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException { 262 if (graph instanceof StructuredGraph) { 263 OptionValues options = graph.getOptions(); 264 StructuredGraph structuredGraph = (StructuredGraph) graph; 265 currentDirectory.toFile().mkdirs(); 266 if (this.root != null) { 267 TTY.println("Dumping string graphs in %s", this.root); 268 this.root = null; 269 } 270 String title = formatTitle(id, format, args); 271 Path filePath = currentDirectory.resolve(escapeFileName(title)); 272 try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filePath.toFile())))) { 273 switch (PrintCanonicalGraphStringFlavor.getValue(options)) { 274 case 1: 275 writeCanonicalExpressionCFGString(structuredGraph, CanonicalGraphStringsCheckConstants.getValue(options), CanonicalGraphStringsRemoveIdentities.getValue(options), writer); 276 break; 277 case 0: 278 default: 279 writeCanonicalGraphString(structuredGraph, CanonicalGraphStringsExcludeVirtuals.getValue(options), CanonicalGraphStringsCheckConstants.getValue(options), writer); 280 break; 281 } 282 } 283 } 284 } 285 286 @Override 287 public void endGroup() throws IOException { 288 currentDirectory = currentDirectory.getParent(); 289 assert currentDirectory != null; 290 } 291 292 @Override 293 public void close() { 294 } 295 }