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.DebugOptions.CanonicalGraphStringsCheckConstants;
  26 import static org.graalvm.compiler.debug.DebugOptions.CanonicalGraphStringsExcludeVirtuals;
  27 import static org.graalvm.compiler.debug.DebugOptions.CanonicalGraphStringsRemoveIdentities;
  28 import static org.graalvm.compiler.debug.DebugOptions.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.DebugContext;
  46 import org.graalvm.compiler.debug.PathUtilities;
  47 import org.graalvm.compiler.graph.Graph;
  48 import org.graalvm.compiler.graph.Node;
  49 import org.graalvm.compiler.graph.NodeMap;
  50 import org.graalvm.compiler.graph.Position;
  51 import org.graalvm.compiler.nodeinfo.Verbosity;
  52 import org.graalvm.compiler.nodes.ConstantNode;
  53 import org.graalvm.compiler.nodes.FixedNode;
  54 import org.graalvm.compiler.nodes.FixedWithNextNode;
  55 import org.graalvm.compiler.nodes.FrameState;
  56 import org.graalvm.compiler.nodes.FullInfopointNode;
  57 import org.graalvm.compiler.nodes.PhiNode;
  58 import org.graalvm.compiler.nodes.ProxyNode;
  59 import org.graalvm.compiler.nodes.StructuredGraph;
  60 import org.graalvm.compiler.nodes.ValueNode;
  61 import org.graalvm.compiler.nodes.cfg.Block;
  62 import org.graalvm.compiler.nodes.cfg.ControlFlowGraph;
  63 import org.graalvm.compiler.nodes.virtual.VirtualObjectNode;
  64 import org.graalvm.compiler.options.OptionValues;
  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 final SnippetReflectionProvider snippetReflection;
  71 
  72     public CanonicalStringGraphPrinter(SnippetReflectionProvider snippetReflection) {
  73         this.snippetReflection = snippetReflection;
  74     }
  75 
  76     @Override
  77     public SnippetReflectionProvider getSnippetReflectionProvider() {
  78         return snippetReflection;
  79     }
  80 
  81     private static String removeIdentities(String str) {
  82         return IDENTITY_PATTERN.matcher(str).replaceAll("$1");
  83     }
  84 
  85     protected static void writeCanonicalGraphExpressionString(ValueNode node, boolean checkConstants, boolean removeIdentities, PrintWriter writer) {
  86         writer.print(node.getClass().getSimpleName());
  87         writer.print("(");
  88         Fields properties = node.getNodeClass().getData();
  89         for (int i = 0; i < properties.getCount(); i++) {
  90             String dataStr = String.valueOf(properties.get(node, i));
  91             if (removeIdentities) {
  92                 dataStr = removeIdentities(dataStr);
  93             }
  94             writer.print(dataStr);
  95             if (i + 1 < properties.getCount() || node.inputPositions().iterator().hasNext()) {
  96                 writer.print(", ");
  97             }
  98         }
  99         Iterator<Position> iterator = node.inputPositions().iterator();
 100         while (iterator.hasNext()) {
 101             Position position = iterator.next();
 102             Node input = position.get(node);
 103             if (checkConstants && input instanceof ConstantNode) {
 104                 ConstantNode constantNode = (ConstantNode) input;
 105                 String valueString = constantNode.getValue().toValueString();
 106                 if (removeIdentities) {
 107                     valueString = removeIdentities(valueString);
 108                 }
 109                 writer.print(valueString);
 110             } else if (input instanceof ValueNode && !(input instanceof PhiNode) && !(input instanceof FixedNode)) {
 111                 writeCanonicalGraphExpressionString((ValueNode) input, checkConstants, removeIdentities, writer);
 112             } else if (input == null) {
 113                 writer.print("null");
 114             } else {
 115                 writer.print(input.getClass().getSimpleName());
 116             }
 117             if (iterator.hasNext()) {
 118                 writer.print(", ");
 119             }
 120         }
 121         writer.print(")");
 122     }
 123 
 124     protected static void writeCanonicalExpressionCFGString(StructuredGraph graph, boolean checkConstants, boolean removeIdentities, PrintWriter writer) {
 125         ControlFlowGraph controlFlowGraph = getControlFlowGraph(graph);
 126         if (controlFlowGraph == null) {
 127             return;
 128         }
 129         try {
 130             for (Block block : controlFlowGraph.getBlocks()) {
 131                 writer.print("Block ");
 132                 writer.print(block);
 133                 writer.print(" ");
 134                 if (block == controlFlowGraph.getStartBlock()) {
 135                     writer.print("* ");
 136                 }
 137                 writer.print("-> ");
 138                 for (Block successor : block.getSuccessors()) {
 139                     writer.print(successor);
 140                     writer.print(" ");
 141                 }
 142                 writer.println();
 143                 FixedNode node = block.getBeginNode();
 144                 while (node != null) {
 145                     writeCanonicalGraphExpressionString(node, checkConstants, removeIdentities, writer);
 146                     writer.println();
 147                     if (node instanceof FixedWithNextNode) {
 148                         node = ((FixedWithNextNode) node).next();
 149                     } else {
 150                         node = null;
 151                     }
 152                 }
 153             }
 154         } catch (Throwable e) {
 155             writer.println();
 156             e.printStackTrace(writer);
 157         }
 158     }
 159 
 160     protected static ControlFlowGraph getControlFlowGraph(StructuredGraph graph) {
 161         try {
 162             return ControlFlowGraph.compute(graph, true, true, false, false);
 163         } catch (Throwable e) {
 164             // Ignore a non-well formed graph
 165             return null;
 166         }
 167     }
 168 
 169     protected static void writeCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants, PrintWriter writer) {
 170         StructuredGraph.ScheduleResult scheduleResult = GraphPrinter.getScheduleOrNull(graph);
 171         if (scheduleResult == null) {
 172             return;
 173         }
 174         try {
 175 
 176             NodeMap<Integer> canonicalId = graph.createNodeMap();
 177             int nextId = 0;
 178 
 179             List<String> constantsLines = null;
 180             if (checkConstants) {
 181                 constantsLines = new ArrayList<>();
 182             }
 183 
 184             for (Block block : scheduleResult.getCFG().getBlocks()) {
 185                 writer.print("Block ");
 186                 writer.print(block);
 187                 writer.print(" ");
 188                 if (block == scheduleResult.getCFG().getStartBlock()) {
 189                     writer.print("* ");
 190                 }
 191                 writer.print("-> ");
 192                 for (Block successor : block.getSuccessors()) {
 193                     writer.print(successor);
 194                     writer.print(" ");
 195                 }
 196                 writer.println();
 197                 for (Node node : scheduleResult.getBlockToNodesMap().get(block)) {
 198                     if (node instanceof ValueNode && node.isAlive()) {
 199                         if (!excludeVirtual || !(node instanceof VirtualObjectNode || node instanceof ProxyNode || node instanceof FullInfopointNode)) {
 200                             if (node instanceof ConstantNode) {
 201                                 if (constantsLines != null) {
 202                                     String name = node.toString(Verbosity.Name);
 203                                     String str = name + (excludeVirtual ? "" : "    (" + filteredUsageCount(node) + ")");
 204                                     constantsLines.add(str);
 205                                 }
 206                             } else {
 207                                 int id;
 208                                 if (canonicalId.get(node) != null) {
 209                                     id = canonicalId.get(node);
 210                                 } else {
 211                                     id = nextId++;
 212                                     canonicalId.set(node, id);
 213                                 }
 214                                 String name = node.getClass().getSimpleName();
 215                                 writer.print("  ");
 216                                 writer.print(id);
 217                                 writer.print("|");
 218                                 writer.print(name);
 219                                 if (!excludeVirtual) {
 220                                     writer.print("    (");
 221                                     writer.print(filteredUsageCount(node));
 222                                     writer.print(")");
 223                                 }
 224                                 writer.println();
 225                             }
 226                         }
 227                     }
 228                 }
 229             }
 230             if (constantsLines != null) {
 231                 writer.print(constantsLines.size());
 232                 writer.println(" constants:");
 233                 Collections.sort(constantsLines);
 234                 for (String s : constantsLines) {
 235                     writer.println(s);
 236                 }
 237             }
 238         } catch (Throwable t) {
 239             writer.println();
 240             t.printStackTrace(writer);
 241         }
 242     }
 243 
 244     public static String getCanonicalGraphString(StructuredGraph graph, boolean excludeVirtual, boolean checkConstants) {
 245         StringWriter stringWriter = new StringWriter();
 246         PrintWriter writer = new PrintWriter(stringWriter);
 247         writeCanonicalGraphString(graph, excludeVirtual, checkConstants, writer);
 248         writer.flush();
 249         return stringWriter.toString();
 250     }
 251 
 252     private static int filteredUsageCount(Node node) {
 253         return node.usages().filter(n -> !(n instanceof FrameState)).count();
 254     }
 255 
 256     @Override
 257     public void beginGroup(DebugContext debug, String name, String shortName, ResolvedJavaMethod method, int bci, Map<Object, Object> properties) throws IOException {
 258     }
 259 
 260     private StructuredGraph currentGraph;
 261     private Path currentDirectory;
 262 
 263     private Path getDirectory(DebugContext debug, StructuredGraph graph) {
 264         if (graph == currentGraph) {
 265             return currentDirectory;
 266         }
 267         currentDirectory = debug.getDumpPath(".graph-strings", true);
 268         currentGraph = graph;
 269         return currentDirectory;
 270     }
 271 
 272     @Override
 273     public void print(DebugContext debug, Graph graph, Map<Object, Object> properties, int id, String format, Object... args) throws IOException {
 274         if (graph instanceof StructuredGraph) {
 275             OptionValues options = graph.getOptions();
 276             StructuredGraph structuredGraph = (StructuredGraph) graph;
 277             Path outDirectory = getDirectory(debug, structuredGraph);
 278             String title = String.format("%03d-%s.txt", id, String.format(format, simplifyClassArgs(args)));
 279             Path filePath = outDirectory.resolve(PathUtilities.sanitizeFileName(title));
 280             try (PrintWriter writer = new PrintWriter(new BufferedWriter(new FileWriter(filePath.toFile())))) {
 281                 switch (PrintCanonicalGraphStringFlavor.getValue(options)) {
 282                     case 1:
 283                         writeCanonicalExpressionCFGString(structuredGraph, CanonicalGraphStringsCheckConstants.getValue(options), CanonicalGraphStringsRemoveIdentities.getValue(options), writer);
 284                         break;
 285                     case 0:
 286                     default:
 287                         writeCanonicalGraphString(structuredGraph, CanonicalGraphStringsExcludeVirtuals.getValue(options), CanonicalGraphStringsCheckConstants.getValue(options), writer);
 288                         break;
 289                 }
 290             }
 291         }
 292     }
 293 
 294     @Override
 295     public void endGroup() throws IOException {
 296     }
 297 
 298     @Override
 299     public void close() {
 300     }
 301 }