/* * This file is part of JOP, the Java Optimized Processor * see <http://www.jopdesign.com/> * * Copyright (C) 2008, Benedikt Huber (benedikt.huber@gmail.com) * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.jopdesign.common.code; import com.jopdesign.common.code.ControlFlowGraph.BasicBlockNode; import com.jopdesign.common.code.ControlFlowGraph.CFGEdge; import com.jopdesign.common.code.ControlFlowGraph.CFGNode; import com.jopdesign.common.graphutils.AdvancedDOTExporter; import com.jopdesign.common.graphutils.AdvancedDOTExporter.DOTLabeller; import com.jopdesign.common.graphutils.AdvancedDOTExporter.DOTNodeLabeller; import com.jopdesign.common.graphutils.LoopColoring; import com.jopdesign.common.logger.LogConfig; import org.apache.bcel.generic.BranchInstruction; import org.apache.bcel.generic.Instruction; import org.apache.bcel.generic.ReturnInstruction; import org.apache.log4j.Logger; import org.jgrapht.DirectedGraph; import java.io.IOException; import java.io.Writer; import java.util.Map; /** * Export information about the flow graph and create a DOT graph * * @author Benedikt Huber (benedikt.huber@gmail.com) */ public class CFGExport { private static final Logger logger = Logger.getLogger(LogConfig.LOG_CFG + ".CFGExport"); public class FGCustomNodeLabeller extends AdvancedDOTExporter.MapLabeller<CFGNode> implements AdvancedDOTExporter.DOTNodeLabeller<CFGNode> { public FGCustomNodeLabeller(Map<CFGNode, ?> nodeAnnotations) { super(nodeAnnotations); } public String getLabel(CFGNode object) { if (annots.containsKey(object)) { return "[" + object.getName() + "] " + annots.get(object); } else { return super.getLabel(object); } } public int getID(CFGNode node) { return node.getId(); } } public class FGEdgeLabeller extends AdvancedDOTExporter.DefaultDOTLabeller<ControlFlowGraph.CFGEdge> { public boolean setAttributes(ControlFlowGraph.CFGEdge edge, Map<String, String> ht) { super.setAttributes(edge, ht); if (flowGraph.getLoopColoring().isBackEdge(edge)) ht.put("arrowhead", "empty"); if (flowGraph.getLoopColoring().getLoopEntrySet(edge).size() > 0) ht.put("arrowhead", "diamond"); return true; } public String getLabel(ControlFlowGraph.CFGEdge edge) { StringBuilder lab = new StringBuilder(); switch (edge.getKind()) { case EXIT_EDGE: lab.append("return"); break; case ENTRY_EDGE: lab.append("entry"); break; default: break; } LoopColoring.IterationBranchLabel<CFGNode> branchLabels = flowGraph.getLoopColoring().getIterationBranchEdges().get(edge); if (branchLabels != null && !branchLabels.isEmpty()) { lab.append("{"); boolean mark = false; if (!branchLabels.getContinues().isEmpty()) { lab.append("cont:"); for (CFGNode n : branchLabels.getContinues()) { lab.append(n.getId() + " "); } mark = true; } if (!branchLabels.getExits().isEmpty()) { if (mark) lab.append(";"); lab.append("exit:"); for (CFGNode n : branchLabels.getExits()) { lab.append(n.getId() + " "); } } lab.append("}"); } return lab.toString(); } } public static class FGNodeLabeller extends AdvancedDOTExporter.DefaultNodeLabeller<CFGNode> { @Override public boolean setAttributes(CFGNode node, Map<String, String> ht) { if (node instanceof BasicBlockNode) { setBasicBlockAttributes((BasicBlockNode) node, ht); } else { ht.put("label", node.toString()); } return true; } protected void addNodeLabel(BasicBlockNode n, StringBuilder nodeInfo) { } private void setBasicBlockAttributes(BasicBlockNode n, Map<String, String> ht) { BasicBlock codeBlock = n.getBasicBlock(); ControlFlowGraph flowGraph = n.getControlFlowGraph(); Instruction lastInstr = codeBlock.getLastInstruction().getInstruction(); boolean isReturn = lastInstr instanceof ReturnInstruction; LoopColoring<CFGNode, ControlFlowGraph.CFGEdge> loops = flowGraph.getLoopColoring(); StringBuilder nodeInfo = new StringBuilder(); nodeInfo.append('#'); nodeInfo.append(n.getId()); nodeInfo.append(' '); String infoHeader; if (n instanceof ControlFlowGraph.InvokeNode) { ControlFlowGraph.InvokeNode in = (ControlFlowGraph.InvokeNode) n; infoHeader = "{invoke " + (in.isVirtual() ? ("virtual " + in.getReferenced()) : in.getImplementingMethod().getFQMethodName()) + "}"; } else if (isReturn) { infoHeader = "{return}"; } else if (codeBlock.getBranchInstruction() != null) { BranchInstruction instr = codeBlock.getBranchInstruction(); infoHeader = "{" + instr.getName() + "}"; } else { infoHeader = "{simple}"; } nodeInfo.append(infoHeader); nodeInfo.append("{" + codeBlock.getNumberOfBytes() + " By, "); addNodeLabel(n, nodeInfo); if (loops.getHeadOfLoops().contains(n)) { nodeInfo.append("LOOP " + n.getId() + "/" + n.getLoopBound()); } nodeInfo.append("}\n"); nodeInfo.append(codeBlock.dump()); ht.put("label", nodeInfo.toString()); if (!loops.getHeadOfLoops().isEmpty()) { if (flowGraph.getLoopColoring().getLoopColors().get(n) == null) { logger.error("No loop coloring for node " + n + " (dead code?)"); } else { ht.put("peripheries", "" + (1 + flowGraph.getLoopColoring().getLoopColors().get(n).size())); } } } } private ControlFlowGraph flowGraph; private AdvancedDOTExporter.DOTNodeLabeller<CFGNode> nl; private AdvancedDOTExporter.DOTLabeller<ControlFlowGraph.CFGEdge> el; public CFGExport(ControlFlowGraph g) { this.flowGraph = g; } public CFGExport(ControlFlowGraph flowGraph, DOTNodeLabeller<CFGNode> nl, DOTLabeller<CFGEdge> el) { this.flowGraph = flowGraph; this.nl = nl; this.el = el; } public CFGExport(ControlFlowGraph graph, Map<CFGNode, ?> nodeAnnotations, Map<ControlFlowGraph.CFGEdge, ?> edgeAnnotations) { this(graph); if (nodeAnnotations != null) { this.nl = new FGCustomNodeLabeller(nodeAnnotations); } if (edgeAnnotations != null) { this.el = new AdvancedDOTExporter.MapLabeller<ControlFlowGraph.CFGEdge>(edgeAnnotations); } } public void exportDOT(Writer writer, DirectedGraph<CFGNode, ControlFlowGraph.CFGEdge> graph) throws IOException { if (nl == null) nl = new FGNodeLabeller(); if (el == null) el = new FGEdgeLabeller(); AdvancedDOTExporter<CFGNode, ControlFlowGraph.CFGEdge> dotExport = new AdvancedDOTExporter<CFGNode, ControlFlowGraph.CFGEdge>(nl, el); dotExport.exportDOT(writer, flowGraph.getGraph()); } }