/* Soot - a J*va Optimization Framework * Copyright (C) 1999-2010 Hossein Sadat-Mohtasham * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307, USA. */ package soot.toolkits.graph.pdg; import java.util.ArrayList; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import soot.Body; import soot.G; import soot.SootClass; import soot.SootMethod; import soot.Unit; import soot.options.Options; import soot.toolkits.graph.Block; import soot.toolkits.graph.BlockGraph; import soot.toolkits.graph.BriefBlockGraph; import soot.toolkits.graph.BriefUnitGraph; import soot.toolkits.graph.DirectedGraph; import soot.toolkits.graph.DominatorNode; import soot.toolkits.graph.DominatorTree; import soot.toolkits.graph.ExceptionalBlockGraph; import soot.toolkits.graph.ExceptionalUnitGraph; import soot.toolkits.graph.MHGDominatorsFinder; import soot.toolkits.graph.MHGPostDominatorsFinder; import soot.toolkits.graph.UnitGraph; /** * This class computes the set of weak regions for a given method. It is based on the * algorithm given in the following paper: * * Ball, T. 1993. What's in a region?: or computing control dependence regions in near-linear * time for reducible control flow. ACM Lett. Program. Lang. Syst. 2, 1-4 (Mar. 1993), * 1-16. DOI= http://doi.acm.org/10.1145/176454.176456 * * @author Hossein Sadat-Mohtasham * Jan 2009 */ @SuppressWarnings("unchecked") public class RegionAnalysis{ protected SootClass m_class = null; protected SootMethod m_method = null; protected Body m_methodBody; protected UnitGraph m_cfg; protected UnitGraph m_reverseCFG; protected BlockGraph m_blockCFG; protected BlockGraph m_reverseBlockCFG; protected Hashtable<Integer, Region> m_regions = new Hashtable<Integer, Region>(); protected List<Region> m_regionsList = null; private int m_regCount = 0; private MHGDominatorTree m_dom; //this would actually be the postdominator tree in the original CFG private MHGDominatorTree m_pdom; protected Region m_topLevelRegion = null; protected Hashtable<Block, Region> m_block2region = null; public RegionAnalysis(UnitGraph cfg, SootMethod m, SootClass c) { this.m_methodBody = cfg.getBody(); this.m_cfg = cfg; this.m_method = m; this.m_class = c; if(Options.v().verbose()) G.v().out.println("[RegionAnalysis]~~~~~~~~~~~~~~~ Begin Region Analsis for method: " + m.getName() +" ~~~~~~~~~~~~~~~~~~~~"); this.findWeakRegions(); if(Options.v().verbose()) G.v().out.println("[RegionAnalysis]~~~~~~~~~~~~~~~ End:" + m.getName() +" ~~~~~~~~~~~~~~~~~~~~"); } private void findWeakRegions() { /* * Check to see what kind of CFG has been passed in and create * the appropriate block CFG. Note that almost all of the processing * is done on the block CFG. */ if(this.m_cfg instanceof ExceptionalUnitGraph) this.m_blockCFG = new ExceptionalBlockGraph((ExceptionalUnitGraph)this.m_cfg); else if(this.m_cfg instanceof EnhancedUnitGraph) this.m_blockCFG = new EnhancedBlockGraph((EnhancedUnitGraph)this.m_cfg); else if(this.m_cfg instanceof BriefUnitGraph) this.m_blockCFG = new BriefBlockGraph((BriefUnitGraph)this.m_cfg); else throw new RuntimeException("Unsupported CFG passed into the RegionAnalyis constructor!"); this.m_dom = new MHGDominatorTree(new MHGDominatorsFinder(this.m_blockCFG)); String s = dominatorTreeToString(this.m_dom, this.m_dom.getHead()); if(Options.v().verbose()) G.v().out.println("[RegionAnalysis] Dominator tree: " + s); try{ this.m_pdom = new MHGDominatorTree(new MHGPostDominatorsFinder(m_blockCFG)); if(Options.v().verbose()) G.v().out.println("[RegionAnalysis] PostDominator tree: "); List heads = this.m_pdom.getHeads(); for(Iterator headItr = heads.iterator(); headItr.hasNext(); ) { s = dominatorTreeToString(this.m_pdom, (DominatorNode) headItr.next()); if(Options.v().verbose()) G.v().out.println(s); } this.m_regCount = -1; /* * If a Brief graph or Exceptional graph is used, the CFG might be multi-headed and/or * multi-tailed, which in turn, could result in a post-dominator forest instead of tree. * If the post-dominator tree has multiple heads, the weakRegionDFS does not work correctly * because it is designed based on the assumption that there is an auxiliary STOP node in the * CFG that post-dominates all other nodes. In fact, most of the CFG algorithms augment * the control flow graph with two nodes: ENTRY and EXIT (or START and STOP) nodes. We have * not added these nodes since the CFG here is created from the Jimple code and to be * consistent we'd have to transform the code to reflect these nodes. Instead, we implemted * the EnhancedUnitGraph (EnhancedBlockGraph) which is guaranteed to be single-headed single-tailed. * But note that EnhancedUnitGraph represents exceptional flow differently. * * */ if(this.m_blockCFG.getHeads().size() == 1) { this.m_regCount++; this.m_regions.put(new Integer(this.m_regCount), this.createRegion(this.m_regCount)); this.weakRegionDFS2((Block)this.m_blockCFG.getHeads().get(0), this.m_regCount); } else if(this.m_blockCFG.getTails().size() == 1) { this.m_regCount++; this.m_regions.put(new Integer(this.m_regCount), this.createRegion(this.m_regCount)); this.weakRegionDFS((Block)this.m_blockCFG.getTails().get(0), this.m_regCount); } else { if(Options.v().verbose()) G.v().out.println("WARNING: RegionAnalysis: the CFG is multi-headed and tailed, so, the results of this analysis might not be reliable!"); for(int i = 0; i < this.m_blockCFG.getTails().size(); i++) { this.m_regCount++; this.m_regions.put(new Integer(this.m_regCount), this.createRegion(this.m_regCount)); this.weakRegionDFS((Block)this.m_blockCFG.getTails().get(i), this.m_regCount); } //throw new RuntimeException("RegionAnalysis: cannot properly deal with multi-headed and tailed CFG!"); } } catch(RuntimeException e) { G.v().out.println("[RegionAnalysis] Exception in findWeakRegions: " + e); } } /** * This algorithms assumes that the first time it's called with a tail of the CFG. Then for each * child node w of v in the post-dominator tree, it compares the parent of v in the dominator tree * with w and if they are the same, that means w belongs to the same region as v, so weakRegionDFS * is recursively called with w and the same region id as v. * If the comparison fails, then a new region is created and weakRegionDFS is called recursively with * w but this time with the newly created region id. * * @param v * @param r */ private void weakRegionDFS(Block v, int r) { try{ //System.out.println("##entered weakRegionDFS for region " + r); this.m_regions.get(new Integer(r)).add(v); DominatorNode parentOfV = this.m_dom.getParentOf(this.m_dom.getDode(v)); Block u2 = (parentOfV == null) ? null : (Block)parentOfV.getGode(); List children = this.m_pdom.getChildrenOf(this.m_pdom.getDode(v)); for(int i = 0; i < children.size(); i++) { DominatorNode w = (DominatorNode)children.get(i); Block u1 = (Block)w.getGode(); if(u2 != null && u1.equals(u2)) { this.weakRegionDFS((Block)w.getGode(), r); } else { this.m_regCount++; this.m_regions.put(new Integer(this.m_regCount), this.createRegion(this.m_regCount)); this.weakRegionDFS((Block)w.getGode(), this.m_regCount); } } } catch(RuntimeException e) { G.v().out.println("[RegionAnalysis] Exception in weakRegionDFS: " + e); G.v().out.println("v is " + v.toShortString() + " in region " + r); G.v().out.flush(); } } /** * This algorithm starts from a head node in the CFG and is exactly the same as the above * with the difference that post dominator and dominator trees switch positions. * @param v * @param r */ private void weakRegionDFS2(Block v, int r) { //regions keep an implicit order of the contained blocks so it matters where blocks are added //below. this.m_regions.get(new Integer(r)).add2Back(v); DominatorNode parentOfV = this.m_pdom.getParentOf(this.m_pdom.getDode(v)); Block u2 = (parentOfV == null) ? null : (Block)parentOfV.getGode(); List children = this.m_dom.getChildrenOf(this.m_dom.getDode(v)); for(int i = 0; i < children.size(); i++) { DominatorNode w = (DominatorNode)children.get(i); Block u1 = (Block)w.getGode(); if(u2 != null && u1.equals(u2)) { this.weakRegionDFS2((Block)w.getGode(), r); } else { this.m_regCount++; this.m_regions.put(new Integer(this.m_regCount), this.createRegion(this.m_regCount)); this.weakRegionDFS2((Block)w.getGode(), this.m_regCount); } } } public List<Region> getRegions() { if(this.m_regionsList == null) { this.m_regionsList = new ArrayList<Region>(); Collection values = this.m_regions.values(); for(Iterator itr = values.iterator(); itr.hasNext();) { Region region = (Region) itr.next(); this.m_regionsList.add(region); } } return this.m_regionsList; } public Hashtable<Unit, Region> getUnit2RegionMap() { Hashtable<Unit, Region> unit2region = new Hashtable<Unit, Region>(); List<Region> regions = this.getRegions(); for(Iterator<Region> itr = regions.iterator(); itr.hasNext();) { Region r = itr.next(); List<Unit> units = r.getUnits(); for (Iterator<Unit> itr1 = units.iterator(); itr1.hasNext();) { Unit u = itr1.next(); unit2region.put(u, r); } } return unit2region; } public Hashtable<Block, Region> getBlock2RegionMap() { if(this.m_block2region == null) { this.m_block2region = new Hashtable<Block, Region>(); List<Region> regions = this.getRegions(); for(Iterator<Region> itr = regions.iterator(); itr.hasNext();) { Region r = itr.next(); List<Block> blocks = r.getBlocks(); for (Iterator<Block> itr1 = blocks.iterator(); itr1.hasNext();) { Block u = itr1.next(); m_block2region.put(u, r); } } } return this.m_block2region; } public BlockGraph getBlockCFG() { return this.m_blockCFG; } public DominatorTree getPostDominatorTree() { return this.m_pdom; } public DominatorTree getDominatorTree() { return this.m_dom; } public void reset() { this.m_regions.clear(); this.m_regionsList.clear(); this.m_regionsList = null; this.m_block2region.clear(); this.m_block2region = null; m_regCount = 0; } /** * Create a region */ protected Region createRegion(int id) { Region region = new Region(id, this.m_method, this.m_class, this.m_cfg); if(id == 0) this.m_topLevelRegion = region; return region; } public Region getTopLevelRegion() { return this.m_topLevelRegion; } public String CFGtoString(DirectedGraph cfg, boolean blockDetail) { String s = new String(""); s += "Headers: " + cfg.getHeads().size() + " " + cfg.getHeads(); for (Iterator<Block> it = cfg.iterator(); it.hasNext(); ) { Block node = it.next(); s += "Node = " + node.toShortString() + "\n"; s += "Preds:\n"; for (Iterator<Block> predsIt = cfg.getPredsOf(node).iterator(); predsIt.hasNext(); ) { s += " "; s += predsIt.next().toShortString() + "\n"; } s += "Succs:\n"; for (Iterator<Block> succsIt = cfg.getSuccsOf(node).iterator(); succsIt.hasNext(); ) { s += " "; s += succsIt.next().toShortString() + "\n"; } } if(blockDetail) { s += "Blocks Detail:"; for (Iterator<Block> it = cfg.iterator(); it.hasNext(); ) { Block node = it.next(); s += node + "\n"; } } return s; } private String dominatorTreeToString(DominatorTree dom, DominatorNode root) { String s = new String(); s += "\n Begin " + ((Block)root.getGode()).toShortString() + " ( "; List children = dom.getChildrenOf(root); for(int i = 0; i < children.size(); i++) { s += dominatorTreeToString(dom, (DominatorNode) children.get(i)); } s += " ) end of " + ((Block)root.getGode()).toShortString(); return s; } }