/*
This file is part of JOP, the Java Optimized Processor
see <http://www.jopdesign.com/>
Copyright (C) 2011, Benedikt Huber (benedikt@vmars.tuwien.ac.at)
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 java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Stack;
import java.util.Vector;
import org.apache.bcel.generic.InstructionHandle;
import org.apache.bcel.generic.MONITORENTER;
import org.apache.bcel.generic.MONITOREXIT;
import com.jopdesign.common.MethodInfo;
import com.jopdesign.common.code.ControlFlowGraph.CFGEdge;
import com.jopdesign.common.code.ControlFlowGraph.CFGNode;
import com.jopdesign.common.code.SuperGraph.ContextCFG;
import com.jopdesign.common.code.SuperGraph.SuperEdge;
import com.jopdesign.common.code.SuperGraph.SuperGraphEdge;
import com.jopdesign.common.code.SuperGraph.SuperGraphNode;
import com.jopdesign.common.code.SuperGraph.SuperInvokeEdge;
import com.jopdesign.common.code.SuperGraph.SuperReturnEdge;
import com.jopdesign.common.graphutils.AdvancedDOTExporter;
import com.jopdesign.common.graphutils.AdvancedDOTExporter.DOTLabeller;
import com.jopdesign.common.graphutils.Pair;
import com.jopdesign.common.misc.Filter;
import com.jopdesign.common.misc.Iterators;
import com.jopdesign.common.misc.MiscUtils;
import com.jopdesign.common.misc.MiscUtils.F1;
import com.jopdesign.wcet.WCETTool;
/**
* Purpose: A segment represents subsets of execution traces.
* It is characterized by a set of entry edges (begin of segment),
* a set of exit edges (end of segment). Together with the supergraph
* this implies the set of nodes which are part of the segment.
*
* @author Benedikt Huber (benedikt@vmars.tuwien.ac.at)
*
*/
public class Segment {
private SuperGraph sg;
private Set<SuperGraphEdge> entries;
private Set<SuperGraphEdge> exits;
private Map<ContextCFG, List<SuperGraphNode>> nodes;
private Set<SuperGraphEdge> edges;
private Filter<SuperGraphEdge> edgeFilter;
private HashSet<SuperGraphEdge> otherEntries;
/** Construct a semi-closed segment (all exit edges are explicitly given and are part of the segment) */
public Segment(SuperGraph sg, Set<SuperGraphEdge> entries, Set<SuperGraphEdge> exits) {
this.sg = sg;
this.entries = entries;
this.exits = exits;
buildSegment();
if(this.getEntryEdges().size() == 0) {
throw new AssertionError("Segment: no entry edges");
}
if(this.getExitEdges().size() == 0) {
throw new AssertionError("Segment: no exit edges");
}
this.edgeFilter = Filter.isContainedIn(this.edges);
}
/**
* @return the set of entry edges
*/
public Set<SuperGraphEdge> getEntryEdges() {
return entries;
}
/**
* @param e a supergraph edge
* @return true, if the edge is an entry edge of the segment
*/
public boolean isEntryEdge(SuperGraphEdge e) {
return this.getEntryEdges().contains(e);
}
/**
* @return The set { contextCfg (target e) | e <- entry-edges }
*/
public Set<ContextCFG> getEntryCFGs() {
Iterable<ContextCFG> entryCFGs = Iterators.mapEntries(getEntryEdges(), new F1<SuperGraphEdge, ContextCFG>() {
@Override
public ContextCFG apply(SuperGraphEdge v) {
return v.getTarget().getContextCFG();
}
});
return Iterators.addAll(new HashSet<ContextCFG>(), entryCFGs);
}
/**
* @return the set of exit edges
*/
public Set<SuperGraphEdge> getExitEdges() {
return exits;
}
/**
* @param e
* @return true if the edge is an exit edge of the segment
*/
public boolean isExitEdge(SuperGraphEdge e) {
return getExitEdges().contains(e);
}
public boolean includesEdge(SuperGraphEdge e) {
return edges.contains(e);
}
/**
* @return the underlying supergraph, which may include many more methods
*/
public SuperGraph getSuperGraph() {
return sg;
}
/**
* Collect all supergraph edges which are part of the segment.
* As segments are allowed to be interprocedural, we require that
* control flow graphs have return edges, and that return edges
* are in the exit set if they are not part of the segment.
*/
private Set<SuperGraphEdge> buildSegment() {
nodes = new HashMap<SuperGraph.ContextCFG, List<SuperGraphNode>>();
edges = new HashSet<SuperGraphEdge>();
otherEntries = new HashSet<SuperGraphEdge>();
HashSet<SuperGraphEdge> actualExits = new HashSet<SuperGraphEdge>();
Stack<SuperGraphEdge> worklist = new Stack<SuperGraphEdge>();
/* push all targets of entry edges on the worklist */
worklist.addAll(entries);
while(! worklist.isEmpty()) {
SuperGraphEdge current = worklist.pop();
if(edges.contains(current)) continue; /* continue if marked black */
edges.add(current); /* mark black */
/* If this is an exit egde, remember that it has been visited, and continue */
if(exits.contains(current)) {
actualExits.add(current);
continue;
}
/* Otherwise add the target node and push all successors on the worklist */
SuperGraphNode target = current.getTarget();
MiscUtils.addToList(nodes, target.getContextCFG(), target);
Iterators.addAll(worklist, sg.getSuccessorEdges(current));
}
exits = actualExits;
for(SuperGraphNode node : getNodes()) {
for(SuperGraphEdge edge: sg.incomingEdgesOf(node)) {
if(! edges.contains(edge)) {
otherEntries.add(edge);
}
}
}
/* for all nodes find entries which are not part of the segment; this are "otherEntries" */
return edges;
}
/**
* Create an interprocedural segment for a method
* @param targetMethod The method to build a segment for
* @param callString The context for the method
* @param cfgProvider A control flow graph provider
* @param callStringLength Length of the callstrings
* @param infeasibles Information about infeasible edges (null if no information available)
*
* @return a segment representing executions of the method (not including the virtual entry and exit nodes)
*/
public static Segment methodSegment(MethodInfo targetMethod, CallString callString,
CFGProvider cfgProvider, int callStringLength, InfeasibleEdgeProvider infeasibles) {
if(infeasibles == null) {
infeasibles = InfeasibleEdgeProvider.NO_INFEASIBLES;
}
SuperGraph superGraph = new SuperGraph(cfgProvider, cfgProvider.getFlowGraph(targetMethod), callString, callStringLength, infeasibles);
ContextCFG rootMethod = superGraph.getRootNode();
Set<SuperGraphEdge> entryEdges = Iterators.addAll(new HashSet<SuperGraphEdge>(), superGraph.getCFGEntryEdges(rootMethod)),
exitEdges = Iterators.addAll(new HashSet<SuperGraphEdge>(), superGraph.getCFGExitEdges(rootMethod));
return new Segment(superGraph, entryEdges, exitEdges);
}
/**
* Create an interprocedural segment for a synchronized block. Currently we do
* not split basic blocks here, so either you are happy with basic block granularity,
* or you split the basic block while loading.
* @param targetBlock The block containing the monitorenter instruction
* @param monitorEnter The monitor enter instruction
* @param callString The context for the method
* @param cfgProvider A control flow graph provider
* @param callStringLength Length of the callstrings
* @param infeasibles Information about infeasible edges (null if no information available)
*
* @return a segment representing executions of the synchronized block
*/
public static Segment synchronizedSegment(ContextCFG ccfg, CFGNode entryNode, InstructionHandle monitorEnter,
CFGProvider cfgProvider, int callStringLength, InfeasibleEdgeProvider infeasibles) {
if(infeasibles == null) {
infeasibles = InfeasibleEdgeProvider.NO_INFEASIBLES;
}
ControlFlowGraph cfg = ccfg.getCfg();
SuperGraph superGraph = new SuperGraph(cfgProvider, cfg, ccfg.getCallString(), callStringLength, infeasibles);
ContextCFG rootMethod = superGraph.getRootNode();
/* lift entry edges */
Set<SuperGraphEdge> entryEdges = Iterators.addAll(new HashSet<SuperGraphEdge>(),
superGraph.liftCFGEdges(rootMethod, cfg.incomingEdgesOf(entryNode)));
/* find exit blocks (might also be in the same block) */
/* monitorenter followed bei monitorexit in same block => segment only contains this block */
Set<CFGEdge> monitorExitEdges = new HashSet<CFGEdge>();
CFGNode currentNode = entryNode;
int currentNestingLevel = 1;
Iterator<InstructionHandle> insIter = currentNode.getBasicBlock().getInstructions().iterator();
while(insIter.hasNext()) {
if(insIter.next() == monitorEnter) break;
}
Stack<Pair<CFGNode,Integer>> todo = new Stack<Pair<CFGNode,Integer>>();
Set<CFGNode> visited = new HashSet<CFGNode>();
do {
boolean isExit = false;
while(insIter.hasNext()) {
InstructionHandle ih = insIter.next();
if(ih.getInstruction() instanceof MONITOREXIT) {
/* blocks outgoing edges terminate segment */
currentNestingLevel--;
if(currentNestingLevel == 0) {
isExit = true;
// If monitorexit is not implemented in Java, the outgoing edges of the
// basic block that contains monitorexit end the synchronized block.
// In order to avoid imprecision, it is advisable that monitorexit is the
// last statement in the basic block.
// We also handle the case when monitorexit is implemented in Java. In this case,
// currentNode will be a SpecialInvokeNode, urrentNode's only
// successor will be the corresponding ReturnNode, and the outgoing edges of
// the ReturnNode are the exit edges for the synchronized segment.
CFGNode onlySuccessor =
Iterators.fromSingleton(cfg.getSuccessors(currentNode));
if(onlySuccessor != null && onlySuccessor instanceof ControlFlowGraph.ReturnNode) {
Iterators.addAll(monitorExitEdges, cfg.outgoingEdgesOf(onlySuccessor));
} else {
Iterators.addAll(monitorExitEdges, cfg.outgoingEdgesOf(currentNode));
}
break;
}
} else if(ih.getInstruction() instanceof MONITORENTER) {
currentNestingLevel++;
}
}
if(! isExit) {
for(CFGNode node : cfg.getSuccessors(currentNode)) {
todo.add(new Pair<CFGNode, Integer>(node, currentNestingLevel));
}
}
currentNode = null;
while(! todo.isEmpty()) {
Pair<CFGNode, Integer> nextPair = todo.pop();
CFGNode nextNode = nextPair.first();
if(! visited.contains(nextNode)) {
visited.add(nextNode);
if(cfg.outgoingEdgesOf(nextNode).isEmpty()) {
throw new AssertionError("Found monitor-exit free path from monitorenter to the end of a function. In: "+cfg);
} else if(nextNode.getBasicBlock() == null) {
for(CFGNode node : cfg.getSuccessors(nextNode)) {
todo.add(new Pair<CFGNode, Integer>(node, nextPair.second()));
}
} else {
currentNode = nextNode;
currentNestingLevel = nextPair.second();
insIter = currentNode.getBasicBlock().getInstructions().iterator();
break;
}
}
}
} while(currentNode != null);
Set<SuperGraphEdge> exitEdges = Iterators.addAll(new HashSet<SuperGraphEdge>(),
superGraph.liftCFGEdges(rootMethod, monitorExitEdges));
return new Segment(superGraph, entryEdges, exitEdges);
}
/**
* Create a (sub-)segment for a method invocation (in a certain context)<br/>
* FIXME: Currently, this is terribly inefficient. We need to work on a good and
* fast support for subsegments.
*
* @param callee root node
* @param supergraph The supergraph we are operating on
* @return a segment representing executions of the method
* (neither including the invoke instruction and the return to the caller)
* @return
*/
public static Segment methodSegment(ContextCFG callee, SuperGraph superGraph) {
return methodSegment(callee.getCfg().getMethodInfo(), callee.getCallString(),
superGraph.getCFGProvider(), superGraph.getCallStringLength(), superGraph.getInfeasibleEdgeProvider());
}
/**
* An intraprocedural segment for a single node
* FIXME: horribly inefficient
* @param node The node to create the segment for
* @param supergraph The supergraph we are operating on
* @return a segment representing executions of the node
*/
public static Segment nodeSegment(SuperGraphNode target, SuperGraph supergraph) {
SuperGraph superGraph = new SuperGraph(supergraph.getCFGProvider(), target.getCfg(), target.getContextCFG().getCallString(),
supergraph.getCallStringLength(), supergraph.getInfeasibleEdgeProvider());
Set<SuperGraphEdge> entryEdges = Iterators.addAll(new HashSet<SuperGraphEdge>(), supergraph.incomingEdgesOf(target)),
exitEdges = Iterators.addAll(new HashSet<SuperGraphEdge>(), supergraph.outgoingEdgesOf(target));
return new Segment(superGraph, entryEdges, exitEdges);
}
/**
* @return set of all nodes in the segment
*/
public Iterable<SuperGraphNode> getNodes() {
return Iterators.concat(nodes.values());
}
/**
* @param ccfg a cfg instance
* @return all supergraph nodes for the CFG instances
*/
public Collection<SuperGraphNode> getNodes(ContextCFG ccfg) {
return nodes.get(ccfg);
}
/**
* @return set of all edges in the segment
*/
public Iterable<SuperGraphEdge> getEdges() {
return edges;
}
/**
* @param node
* @return
*/
public Iterable<SuperGraphEdge> incomingEdgesOf(SuperGraphNode node) {
return edgeFilter.filter(sg.incomingEdgesOf(node));
}
/**
* @param ccfg
* @return
*/
public Iterable<SuperEdge> incomingSuperEdgesOf(ContextCFG ccfg) {
return edgeFilter.filter(sg.getCallGraph().incomingEdgesOf(ccfg));
}
/**
* @param node
* @return
*/
public Iterable<SuperGraphEdge> outgoingEdgesOf(SuperGraphNode node) {
return edgeFilter.filter(sg.outgoingEdgesOf(node));
}
final Filter<Pair<SuperInvokeEdge, SuperReturnEdge>> superEdgePairFilter =
new Filter<Pair<SuperInvokeEdge, SuperReturnEdge>>() {
@Override
protected boolean include(Pair<SuperInvokeEdge, SuperReturnEdge> e) {
return(edges.contains(e.first()) && edges.contains(e.second()));
}
};
/**
* @return all pairs of invoke/return superedges
*/
public Iterable<Pair<SuperInvokeEdge, SuperReturnEdge>> getCallSites() {
Iterable<Pair<SuperInvokeEdge, SuperReturnEdge>> callSites = Iterators.concat(sg.getCallSites().values());
return superEdgePairFilter.filter(callSites);
}
/**
* @param caller restriction on the call site
* @return those pairs of invoke/return superedges with the specified caller
*/
public Iterable<Pair<SuperInvokeEdge, SuperReturnEdge>> getCallSitesFrom(ContextCFG caller) {
return superEdgePairFilter.filter(sg.getCallSitesFrom(caller));
}
/* delegates to supergraph */
public Iterable<SuperGraphEdge> liftCFGEdges(ContextCFG node, Iterable<CFGEdge> cfgEdges) {
return sg.liftCFGEdges(node, cfgEdges);
}
public Iterable<ContextCFG> getCallGraphNodes() {
return nodes.keySet();
}
@Override
public String toString() {
StringBuilder sb = new StringBuilder();
sb.append("Segment@");
sb.append(super.hashCode());
sb.append("(");
sb.append(this.getEntryEdges());
sb.append(" -- ");
sb.append(this.getExitEdges());
sb.append(")");
return sb.toString();
}
/**
* Export to DOT file
* @param dotFile
* @throws IOException
*/
public void exportDOT(File dotFile) throws IOException {
FileWriter dotWriter = new FileWriter(dotFile);
AdvancedDOTExporter.DOTNodeLabeller<SuperGraphNode> nodeLabeller =
new AdvancedDOTExporter.DefaultNodeLabeller<SuperGraphNode>() {
@Override
public String getLabel(SuperGraphNode node) {
StringBuilder sb = new StringBuilder();
/* for entry nodes: method + call string */
if(node.getCFGNode().getId() == 0) {
sb.append(node.getContextCFG().getCfg().getMethodInfo().getFQMethodName()+"\n");
int i = 1;
for(InvokeSite is : node.getContextCFG().getCallString()) {
sb.append(" #"+i+" "+is.getInvoker().getFQMethodName()+" / "+is.getInstructionHandle().getPosition()+"\n");
i+=1;
}
}
/* for other nodes: basic block export */
else {
sb.append(node.getCFGNode().toString());
}
return sb.toString();
}
};
DOTLabeller<SuperGraphEdge> edgeLabeller =
new AdvancedDOTExporter.DefaultDOTLabeller<SuperGraphEdge>() {
@Override
public String getLabel(SuperGraphEdge edge) {
return "";
}
@Override
public boolean setAttributes(SuperGraphEdge edge,
Map<String, String> ht) {
super.setAttributes(edge, ht);
if (edge instanceof SuperReturnEdge) {
ht.put("style", "dotted");
ht.put("arrowhead", "empty");
} else if(edge instanceof SuperInvokeEdge) {
ht.put("style", "dotted");
}
return true;
}
};
AdvancedDOTExporter<SuperGraphNode, SuperGraphEdge> de = new AdvancedDOTExporter<SuperGraphNode, SuperGraphEdge>(
nodeLabeller, edgeLabeller );
de.exportDOTDiGraph(dotWriter, this.getNodes(), this.getEdges(),
new AdvancedDOTExporter.GraphAdapter<SuperGraphNode, SuperGraphEdge>() {
@Override
public SuperGraphNode getEdgeSource(SuperGraphEdge e) {
return e.getSource();
}
@Override
public SuperGraphNode getEdgeTarget(SuperGraphEdge e) {
return e.getTarget();
}
});
dotWriter.close();
}
}