001/* 002 * This file is part of the Jikes RVM project (http://jikesrvm.org). 003 * 004 * This file is licensed to You under the Eclipse Public License (EPL); 005 * You may not use this file except in compliance with the License. You 006 * may obtain a copy of the License at 007 * 008 * http://www.opensource.org/licenses/eclipse-1.0.php 009 * 010 * See the COPYRIGHT.txt file distributed with this work for information 011 * regarding copyright ownership. 012 */ 013package org.jikesrvm.compilers.opt.driver; 014 015import java.io.BufferedWriter; 016import java.io.File; 017import java.io.FileWriter; 018import java.io.IOException; 019import java.util.Enumeration; 020 021import org.jikesrvm.classloader.RVMMethod; 022import org.jikesrvm.compilers.opt.inlining.InlineSequence; 023import org.jikesrvm.compilers.opt.ir.BasicBlock; 024import org.jikesrvm.compilers.opt.ir.Call; 025import org.jikesrvm.compilers.opt.ir.IR; 026import org.jikesrvm.compilers.opt.ir.Instruction; 027import org.jikesrvm.compilers.opt.ir.operand.MethodOperand; 028import org.jikesrvm.runtime.Time; 029import org.jikesrvm.util.Pair; 030 031/** 032 * Prints a CFG visualization to a file using the dot output format. Further processing 033 * can be done using tools such as <a href="http://graphviz.org">Graphviz</a> 034 * or graphical frontends such as <a href="http://zvtm.sourceforge.net/zgrviewer.html">ZGRviewer</a>. 035 */ 036public class CFGVisualization { 037 038 private static final String CENTERED = "\\n"; 039 private static final String LEFT_JUSTIFIED = "\\l"; 040 041 @SuppressWarnings("unused") 042 // just here as documentation to make it clear that 0 is taken as a value 043 private static final byte UNVISITED = 0; 044 045 private static final byte HIGHLIGHTED = 1; 046 private static final byte GRAY = 2; 047 private static final byte BLACK = 3; 048 049 private final BufferedWriter out; 050 private final IR ir; 051 052 private final byte[] nodeToColour; 053 054 public CFGVisualization(IR ir, String tag) throws SecurityException, IOException { 055 RVMMethod method = ir.getMethod(); 056 String fileName = determineFileName(ir, tag, method); 057 String dir = ir.options.VISUALIZE_IR_DIRECTORY; 058 if (dir.trim().isEmpty()) { 059 dir = System.getProperty("user.dir"); 060 } 061 File directory = new File(dir); 062 File file = new File(directory, fileName); 063 this.out = new BufferedWriter(new FileWriter(file)); 064 this.ir = ir; 065 nodeToColour = new byte[ir.cfg.numberOfNodes()]; 066 } 067 068 private String determineFileName(IR ir, String tag, RVMMethod method) { 069 return tag.replace(' ', '-').replace('/', '-') + "_" + 070 method.getDeclaringClass().getDescriptor().classNameFromDescriptor() + "_" + 071 method.getName() + "_" + 072 "opt" + ir.options.getOptLevel() + "-" + Time.currentTimeMillis() + ".graph"; 073 } 074 075 public void visualizeCFG() throws IOException { 076 out.write("digraph G {\n node [shape=box];\n"); 077 out.write("ENTRY" + "[ label=\"" + "ENTRY" + CENTERED + "\n"); 078 BasicBlock entry = ir.cfg.entry(); 079 out.write(enumerateAndFormatInstructions(entry)); 080 out.write("\"" + formatHighlighting(entry) + "];\n"); 081 dfsCFG(entry, ir); 082 out.write("}"); 083 out.close(); 084 } 085 086 protected void dfsCFG(BasicBlock bb, IR ir) throws IOException { 087 Enumeration<BasicBlock> successors = bb.getOutNodes(); 088 nodeToColour[bb.getIndex()] = GRAY; 089 while (successors.hasMoreElements()) { 090 BasicBlock succBB = successors.nextElement(); 091 StringWrapper returnObj = setDirectionalEdges(succBB, bb); 092 out.write(returnObj.getStr()); 093 String to = returnObj.getTo(); 094 boolean toNotEmpty = !to.isEmpty(); 095 if (toNotEmpty) { 096 out.write(to + "[ label=\"" + to + CENTERED); 097 } 098 out.write(enumerateAndFormatInstructions(succBB)); 099 if (toNotEmpty) { 100 out.write("\"" + formatHighlighting(succBB) + "];\n"); 101 } 102 byte colour = nodeToColour[succBB.getIndex()]; 103 if (colour != GRAY && colour != BLACK) { 104 dfsCFG(succBB,ir); 105 } 106 } 107 nodeToColour[bb.getIndex()] = BLACK; 108 } 109 110 /** 111 * Generates control-flow edge descriptions for basic blocks. 112 * @param succBB successor basic block 113 * @param bb current basic block 114 * @return a pair of strings 115 */ 116 protected StringWrapper setDirectionalEdges(BasicBlock succBB, BasicBlock bb) { 117 String to = null; 118 String str = null; 119 int bbNumber = bb.getNumber(); 120 BasicBlock entry = ir.cfg.entry(); 121 boolean succBBIsExit = succBB.isExit(); 122 boolean bbIsEntry = bb == entry; 123 124 if (bbIsEntry || succBBIsExit) { 125 if (bbIsEntry && !succBBIsExit) { 126 to = "BB" + succBB.getNumber(); 127 str = "ENTRY" + "->" + "{" + to + "}" + ";\n"; 128 } else if (!bbIsEntry && succBBIsExit) { 129 to = "EXIT"; 130 str = "BB" + bbNumber + "->" + "{" + to + "}" + ";\n"; 131 } else { // bbIsEntry && succBBisExit 132 to = "EXIT"; 133 str = "ENTRY" + "->" + "{" + to + "}" + ";\n"; 134 } 135 } else { 136 to = "BB" + succBB.getNumber(); 137 if (succBB == entry) { 138 to = "ENTRY"; 139 } 140 str = "BB" + bbNumber + "->" + "{" + to + "}" + ";\n"; 141 } 142 return new StringWrapper(to, str); 143 } 144 145 protected String enumerateAndFormatInstructions(BasicBlock succBB) { 146 StringBuilder next = new StringBuilder(); 147 for (Enumeration<Instruction> e = succBB.forwardInstrEnumerator(); e.hasMoreElements();) { 148 Instruction inst = e.nextElement(); 149 int lineNumber = 0; 150 String s = formatInstruction(inst); 151 s = s.replaceAll("\n", " "); 152 s = s.replaceAll("\"", "\\\\\""); 153 InlineSequence position = inst.position; 154 int bytecodeIndex = inst.getBytecodeIndex(); 155 if (position != null) { 156 lineNumber = position.getMethod().getLineNumberForBCIndex(bytecodeIndex); 157 } 158 next.append(s); 159 next.append(" "); 160 next.append(bytecodeIndex); 161 next.append(","); 162 next.append(lineNumber); 163 next.append(LEFT_JUSTIFIED); 164 next.append("\n"); 165 } 166 return next.toString(); 167 } 168 169 /** 170 * Formats instructions. Currently only calls are specially formatted, 171 * everything else is just printed. 172 * 173 * @param inst an instruction 174 * @return the String for the instruction 175 */ 176 protected String formatInstruction(Instruction inst) { 177 if (Call.conforms(inst)) { 178 return "CALL " + formatCall(inst); 179 } else { 180 return inst.toString(); 181 } 182 } 183 184 protected String formatCall(Instruction inst) { 185 StringBuilder buf = new StringBuilder(); 186 MethodOperand mop = Call.getMethod(inst); 187 if (mop != null && mop.hasTarget()) { 188 RVMMethod method = mop.getTarget(); 189 buf.append(method.getDeclaringClass()); 190 buf.append(":"); 191 buf.append(method.getName()); 192 buf.append(":"); 193 buf.append(method.getDescriptor()); 194 buf.append(":"); 195 int params = Call.getNumberOfParams(inst); 196 for (int i = 1; i <= params; i++) { 197 buf.append(Call.getParam(inst, i - 1)); 198 if (i < params) { 199 buf.append(", "); 200 } else { 201 buf.append("; "); 202 } 203 } 204 } 205 return buf.toString(); 206 } 207 208 public void markBlockAsHighlighted(BasicBlock bb) { 209 if (bb != null) { 210 nodeToColour[bb.getIndex()] = HIGHLIGHTED; 211 } 212 } 213 214 protected String formatHighlighting(BasicBlock bb) { 215 if (nodeToColour[bb.getIndex()] == HIGHLIGHTED) { 216 return " style=\"filled\", color=\"lightblue\""; 217 } else { 218 return ""; 219 } 220 } 221 222 protected static final class StringWrapper extends Pair<String, String> { 223 224 public StringWrapper(String to, String str) { 225 super(to, str); 226 } 227 228 public String getTo() { 229 return first; 230 } 231 232 public String getStr() { 233 return second; 234 } 235 236 } 237 238}