/******************************************************************************
* Copyright (c) 2009 - 2015 IBM Corporation.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* Contributors:
* IBM Corporation - initial API and implementation
*****************************************************************************/
/**
*
*/
package com.ibm.wala.memsat.util;
import static com.ibm.wala.memsat.util.Graphs.reflexiveTransitiveClosure;
import static com.ibm.wala.memsat.util.Graphs.transitiveClosure;
import static com.ibm.wala.memsat.util.Graphs.union;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.EnumSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import com.ibm.wala.classLoader.IField;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.rta.CallSite;
import com.ibm.wala.ipa.cha.IClassHierarchy;
import com.ibm.wala.memsat.frontEnd.InlinedInstruction;
import com.ibm.wala.memsat.frontEnd.InlinedInstruction.Action;
import com.ibm.wala.memsat.frontEnd.WalaConcurrentInformation;
import com.ibm.wala.memsat.frontEnd.WalaInformation;
import com.ibm.wala.ssa.SSAArrayLoadInstruction;
import com.ibm.wala.ssa.SSAArrayReferenceInstruction;
import com.ibm.wala.ssa.SSAArrayStoreInstruction;
import com.ibm.wala.ssa.SSAFieldAccessInstruction;
import com.ibm.wala.ssa.SSAGetInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAMonitorInstruction;
import com.ibm.wala.ssa.SSAPutInstruction;
import com.ibm.wala.util.Predicate;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.traverse.DFS;
/**
* Provides a set of utility methods for working with Wala data structures
* for concurrent programs.
*
* @see InlinedInstruction
* @see WalaInformation
* @see WalaConcurrentInformation
*
* @author etorlak
*/
public final class Programs {
private Programs() {}
/**
* Returns a set of instance keys that represent the value that is read or written by the given instruction
* or the empty set if the value is a primitive.
* @requires some n : info.threads | info.concurrentInformation(n).actions().contains(inst)
* @requires inst.action in NORMAL_READ + VOLATILE_READ + NORMAL_WRITE + VOLATILE_WRITE
* @return set of instance keys that represent the value that is read or written by the given instruction
* or the empty set if the value is a primitive.
*/
public static Set<InstanceKey> valueFor(WalaInformation info, InlinedInstruction inst) {
final int use;
final SSAInstruction obj = inst.instruction();
if (obj instanceof SSAGetInstruction || obj instanceof SSAArrayLoadInstruction) {
use = obj.getDef();
} else if (obj instanceof SSAPutInstruction) {
use = ((SSAPutInstruction)obj).getVal();
} else if (obj instanceof SSAArrayStoreInstruction) {
use = ((SSAArrayStoreInstruction)obj).getValue();
} else {
throw new IllegalArgumentException(inst + " is not a read or write instruction.");
}
return info.pointsTo(info.cgNodeInformation(inst.cgNode()).pointerKeyFor(use));
}
/**
* Returns a set of instance keys that represent the object locked or unlocked by the given instruction.
* @requires some n : info.threads | info.concurrentInformation(n).actions().contains(inst)
* @requires inst.action instanceof SSAMonitorInstruction
* @return a set of instance keys that represent the object locked or unlocked by the given instruction.
*/
public static Set<InstanceKey> referencedMonitor(WalaInformation info, InlinedInstruction inst) {
final int use = ((SSAMonitorInstruction)inst.instruction()).getRef();
return info.pointsTo(info.cgNodeInformation(inst.cgNode()).pointerKeyFor(use));
}
/**
* Returns a set of instance keys that represent the object whose field is being read or written
* by the instruction, or the empty set if the accessed field is static.
* @requires some n : info.threads | info.concurrentInformation(n).actions().contains(inst)
* @requires inst.instruction instanceof SSAFieldAccessInstruction
* @return a set of instance keys that represent the object whose field is being read or written
* by the instruction, or the empty set if the accessed field is static.
*/
public static Set<InstanceKey> referencedInstance(WalaInformation info, InlinedInstruction inst) {
final int use = ((SSAFieldAccessInstruction)inst.instruction()).getRef();
if (use<1)
return Collections.emptySet();
else
return info.pointsTo(info.cgNodeInformation(inst.cgNode()).pointerKeyFor(use));
}
/**
* Returns a set of instance keys that represent the array instance being read or written by the given instruction.
* @requires some n : info.threads | info.concurrentInformation(n).actions().contains(inst)
* @requires inst.instruction instanceof SSAArrayReferenceInstruction
* @return a set of instance keys that represent the array instance being read or written by the given instruction.
*/
public static Set<InstanceKey> referencedArray(WalaInformation info, InlinedInstruction inst) {
final int use = ((SSAArrayReferenceInstruction)inst.instruction()).getArrayRef();
return info.pointsTo(info.cgNodeInformation(inst.cgNode()).pointerKeyFor(use));
}
/**
* Returns the set of all methods m such that m is the governing node for one of given instructions or
* m is in the call stack for one the given instructions.
* @return set of methods as described above
*/
public static Set<CGNode> relevantMethods(Set<InlinedInstruction> insts) {
final Set<CGNode> methods = new LinkedHashSet<CGNode>();
for(InlinedInstruction inst : insts) {
methods.add(inst.cgNode());
for(CallSite cs : inst.callStack()) {
methods.add(cs.getNode());
}
}
return methods;
}
/**
* Returns a map from each read/write instruction in the given info to the field it accesses.
* @return { i: InlinedInstruction, f: IField | some t: info.threads() |
* info.concurrentInformation(t).actions().contains(i) and
* i.instruction instanceof SSAFieldAccessInstruction and
* f = base.info.callGraph().getClassHierarchy().resolveField( i.getDeclaredField() ) }
*/
public static Map<InlinedInstruction,IField> referencedFields(WalaInformation info) {
final Map<InlinedInstruction,IField> fields = new LinkedHashMap<InlinedInstruction, IField>();
final IClassHierarchy cha = info.callGraph().getClassHierarchy();
for(CGNode thread : info.threads()) {
for(InlinedInstruction inst : info.concurrentInformation(thread).actions()) {
if (inst.instruction() instanceof SSAFieldAccessInstruction) {
final SSAFieldAccessInstruction access = (SSAFieldAccessInstruction) inst.instruction();
final IField field = cha.resolveField(access.getDeclaredField());
assert field != null;
fields.put(inst, field);
}
}
}
return fields;
}
/**
* Returns the entry method of the thread that contains the given inlined instruction.
* @return inst.cgNode if inst.callStack is empty, otherwise returns the bottom of inst.callStack
*/
public static CGNode thread(InlinedInstruction inst) {
if (inst.callStack().empty())
return inst.cgNode();
else {
CallSite last = null;
for(CallSite cs : inst.callStack()) {
last = cs;
}
return last.getNode();
}
}
/**
* Returns the set of all inlined instructions in info.threads. The instructions
* are ordered in the DFS discovery order (both with respect to the threads
* graph and the individual threadOrder graphs).
* @requires info.threads.size() > 1
* @return info.threads.actions
*/
public static Set<InlinedInstruction> instructions(WalaInformation info) {
final Set<InlinedInstruction> ret = new LinkedHashSet<InlinedInstruction>();
final Graph<CGNode> threads = info.threads();
for(Iterator<CGNode> tItr = DFS.iterateDiscoverTime(threads, Graphs.root(threads)); tItr.hasNext(); ) {
final WalaConcurrentInformation cInfo = info.concurrentInformation(tItr.next());
for(Iterator<InlinedInstruction> insts = DFS.iterateDiscoverTime(cInfo.threadOrder(), cInfo.start()); insts.hasNext(); ) {
ret.add(insts.next());
}
}
return ret;
}
/**
* Returns a subset of the given set that contains only the instructions that produce the given kind of action.
* @return a subset of the given set that contains only the instructions that produce the given kind of action.
*/
public static Set<InlinedInstruction> instructionsOfType(Set<InlinedInstruction> insts, Set<Action> actions) {
final Set<InlinedInstruction> ret = new LinkedHashSet<InlinedInstruction>();
for(InlinedInstruction inst : insts) {
if (actions.contains(inst.action()))
ret.add(inst);
}
return ret;
}
/**
* Returns the transitive closure of the union of the thread orders of all threads in info.threads.
* @return the transitive closure of the union of the thread orders of all threads in info.threads.
*/
public static Graph<InlinedInstruction> programOrder(WalaInformation info) {
final Graph<CGNode> threads = info.threads();
final Collection<Graph<InlinedInstruction>> ords = new ArrayList<Graph<InlinedInstruction>>(threads.getNumberOfNodes());
for(CGNode t : threads) {
ords.add(info.concurrentInformation(t).threadOrder());
}
return transitiveClosure(union(ords));
}
/**
* Returns a filter that accepts instructions that produce one of the given actions.
* @return a filter that accepts instructions that produce one of the given actions.
*/
public static final Predicate<InlinedInstruction> filter(final Action act, final Action...acts) {
return new Predicate<InlinedInstruction>() {
final Set<Action> accepted = EnumSet.of(act, acts);
public boolean test(InlinedInstruction o) {
return accepted.contains(o.action());
}
};
}
// /**
// * Returns a graph that represents the upper bound on the
// * synchronization order over the specified synchronization actions of info.threads.
// * Synchronization actions must always include the start and end actions. The synchronization
// * order graph is obtained by taking the
// * the {@linkplain #programOrder(WalaInformation) program order} of info.threads, restricting
// * it to synchronization instructions, and then adding appropriate edges between synchronization
// * instructions in different threads. In particular, given two instructions i1 and i2 from threads
// * t1 and t2, the edge i1->i2 is present iff there is no path from t2 to t1 in the info.threads graph.
// * @requires START + END in syncs
// * @return a graph that represents the upper bound on the
// * synchronization order over the specified synchronization actions of info.threads.
// */
// public static Graph<InlinedInstruction> syncOrder(final WalaInformation info, final Set<Action> syncs) {
// assert syncs.contains(Action.START) && syncs.contains(Action.END);
// final Filter<InlinedInstruction> syncFilter = new Filter<InlinedInstruction>() {
// public boolean accepts(InlinedInstruction o) { return syncs.contains(o.action()); }
// };
//
// final Map<CGNode, Graph<InlinedInstruction>> threadSync = new LinkedHashMap<CGNode, Graph<InlinedInstruction>>();
// for(CGNode n : info.threads()) {
// final Graph<InlinedInstruction> po = reflexiveTransitiveClosure(info.concurrentInformation(n).threadOrder());
// threadSync.put(n, Graphs.restrict(po, syncFilter));
// }
//
// final Graph<InlinedInstruction> syncOrd = union(threadSync.values());
// final Graph<CGNode> threadOrd = reflexiveTransitiveClosure(info.threads());
// for(CGNode n1 : threadOrd) {
// final Graph<InlinedInstruction> s1 = threadSync.get(n1);
// for(CGNode n2 : threadOrd) {
// if (!threadOrd.hasEdge(n2, n1)) {
// for(InlinedInstruction i1 : s1) {
// for(InlinedInstruction i2 : threadSync.get(n2)) {
// syncOrd.addEdge(i1, i2);
// }
// }
// }
// }
// }
// return syncOrd;
// }
/**
* Returns a graph that represents the upper bound on the execution order of the instructions in info.threads.
* The execution order graph is obtained by taking the {@linkplain #programOrder(WalaInformation) program order}
* of info.threads and then adding appropriate edges between
* instructions in different threads. In particular, given two instructions i1 and i2 from threads
* t1 and t2, the edge i1->i2 is present iff there is no path from t2 to t1 in the info.threads graph.
* @param info
* @return a graph that represents the upper bound on the execution order of the instructions in info.threads.
*/
public static Graph<InlinedInstruction> executionOrder(final WalaInformation info) {
final Graph<InlinedInstruction> exec = programOrder(info);
final Graph<CGNode> threadOrd = reflexiveTransitiveClosure(info.threads());
for(CGNode n1 : threadOrd) {
final Set<InlinedInstruction> s1 = info.concurrentInformation(n1).actions();
for(CGNode n2 : threadOrd) {
if (!threadOrd.hasEdge(n2, n1)) {
final Set<InlinedInstruction> s2 = info.concurrentInformation(n2).actions();
for(InlinedInstruction i1 : s1) {
for(InlinedInstruction i2 : s2) {
exec.addEdge(i1, i2);
}
}
}
}
}
return exec;
}
/**
* Returns a graph that maps each read in info.threads to the set of writes that it may see.
* @return a graph that maps each read in info.threads to the set of writes that it may see.
*/
public static Graph<InlinedInstruction> visibleWrites(WalaInformation info) {
final Graph<InlinedInstruction> ret = new LinkedHashGraph<InlinedInstruction>();
for(CGNode n : info.threads()) {
final WalaConcurrentInformation cinfo = info.concurrentInformation(n);
for(InlinedInstruction inst : cinfo.actions()) {
if (inst.action()==Action.NORMAL_READ || inst.action()==Action.VOLATILE_READ) {
System.out.println("VISIBLE WRITES FOR " + inst + ":");
for(InlinedInstruction write : cinfo.visibleWrites(inst)) {
System.out.println(" " + write.action() + " " + write);
ret.addEdge(inst, write);
}
}
}
}
return ret;
}
}