Add options to ignore inter-procedural control dependence
In certain cases, one may want to ignore inter-procedural control dependence. Consider the following example: flag = getFlagVal(); if (flag) { doStuff(); } If we are ignoring interprocedural control dependence, a forward slice from the first statement will *not* include statements inside doStuff() and its transitive callees. This option is useful in scenarios where the effects of statements inside control-dependent callees can be accounted for via some cheaper effect analysis. E.g., if you only care about heap effects of control- dependent callees, you can compute that using mod-ref analysis, rather than sucking all the control-dependent callee statements into the slice. Also added some more detailed comments, a new unit test, and removed some trailing whitespace.
This commit is contained in:
parent
24fb1f6d10
commit
554a6e7ee9
|
@ -64,6 +64,7 @@ import com.ibm.wala.ssa.SSAInstruction;
|
|||
import com.ibm.wala.ssa.SSAInvokeInstruction;
|
||||
import com.ibm.wala.ssa.SSANewInstruction;
|
||||
import com.ibm.wala.ssa.SSAPutInstruction;
|
||||
import com.ibm.wala.ssa.SSAReturnInstruction;
|
||||
import com.ibm.wala.types.ClassLoaderReference;
|
||||
import com.ibm.wala.types.Descriptor;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
|
@ -81,20 +82,20 @@ public class SlicerTest {
|
|||
|
||||
// more aggressive exclusions to avoid library blowup
|
||||
// in interprocedural tests
|
||||
private static final String EXCLUSIONS = "java\\/awt\\/.*\n" +
|
||||
"javax\\/swing\\/.*\n" +
|
||||
"sun\\/awt\\/.*\n" +
|
||||
"sun\\/swing\\/.*\n" +
|
||||
"com\\/sun\\/.*\n" +
|
||||
"sun\\/.*\n" +
|
||||
"org\\/netbeans\\/.*\n" +
|
||||
"org\\/openide\\/.*\n" +
|
||||
"com\\/ibm\\/crypto\\/.*\n" +
|
||||
"com\\/ibm\\/security\\/.*\n" +
|
||||
"org\\/apache\\/xerces\\/.*\n" +
|
||||
"java\\/security\\/.*\n" +
|
||||
private static final String EXCLUSIONS = "java\\/awt\\/.*\n" +
|
||||
"javax\\/swing\\/.*\n" +
|
||||
"sun\\/awt\\/.*\n" +
|
||||
"sun\\/swing\\/.*\n" +
|
||||
"com\\/sun\\/.*\n" +
|
||||
"sun\\/.*\n" +
|
||||
"org\\/netbeans\\/.*\n" +
|
||||
"org\\/openide\\/.*\n" +
|
||||
"com\\/ibm\\/crypto\\/.*\n" +
|
||||
"com\\/ibm\\/security\\/.*\n" +
|
||||
"org\\/apache\\/xerces\\/.*\n" +
|
||||
"java\\/security\\/.*\n" +
|
||||
"";
|
||||
|
||||
|
||||
private static AnalysisScope findOrCreateAnalysisScope() throws IOException {
|
||||
if (cachedScope == null) {
|
||||
cachedScope = AnalysisScopeReader.readJavaScope(TestConstants.WALA_TESTDATA, null, SlicerTest.class.getClassLoader());
|
||||
|
@ -248,7 +249,7 @@ public class SlicerTest {
|
|||
|
||||
/**
|
||||
* test unreproduced bug reported on mailing list by Sameer Madan, 7/3/2007
|
||||
*
|
||||
*
|
||||
* @throws CancelException
|
||||
* @throws IllegalArgumentException
|
||||
* @throws IOException
|
||||
|
@ -277,7 +278,7 @@ public class SlicerTest {
|
|||
|
||||
/**
|
||||
* test bug reported on mailing list by Ravi Chandhran, 4/16/2010
|
||||
*
|
||||
*
|
||||
* @throws CancelException
|
||||
* @throws IllegalArgumentException
|
||||
* @throws IOException
|
||||
|
@ -308,7 +309,7 @@ public class SlicerTest {
|
|||
ControlDependenceOptions.NONE);
|
||||
Assert.assertEquals(slice.toString(), 4, slice.size());
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testSlice9() throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException {
|
||||
AnalysisScope scope = findOrCreateAnalysisScope();
|
||||
|
@ -434,7 +435,7 @@ public class SlicerTest {
|
|||
dumpSlice(slice);
|
||||
Assert.assertEquals(slice.toString(), 1, countConditionals(slice));
|
||||
}
|
||||
|
||||
|
||||
@Test
|
||||
public void testTestCD5() throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException {
|
||||
AnalysisScope scope = findOrCreateAnalysisScope();
|
||||
|
@ -457,7 +458,34 @@ public class SlicerTest {
|
|||
Collection<Statement> slice = Slicer.computeForwardSlice(s, cg, pointerAnalysis,
|
||||
DataDependenceOptions.NONE, ControlDependenceOptions.NO_EXCEPTIONAL_EDGES);
|
||||
dumpSlice(slice);
|
||||
Assert.assertTrue(slice.toString(), slice.size() > 1);
|
||||
Assert.assertEquals(10, slice.size());
|
||||
Assert.assertEquals(3, countReturns(slice));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTestCD5NoInterproc() throws ClassHierarchyException, IllegalArgumentException, CancelException, IOException {
|
||||
AnalysisScope scope = findOrCreateAnalysisScope();
|
||||
|
||||
IClassHierarchy cha = findOrCreateCHA(scope);
|
||||
Iterable<Entrypoint> entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha,
|
||||
TestConstants.SLICE_TESTCD5);
|
||||
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);
|
||||
|
||||
CallGraphBuilder builder = Util.makeZeroOneCFABuilder(options, new AnalysisCacheImpl(), cha, scope);
|
||||
CallGraph cg = builder.makeCallGraph(options, null);
|
||||
|
||||
CGNode main = findMainMethod(cg);
|
||||
|
||||
Statement s = new MethodEntryStatement(main);
|
||||
System.err.println("Statement: " + s);
|
||||
|
||||
// compute a no-data slice
|
||||
final PointerAnalysis<InstanceKey> pointerAnalysis = builder.getPointerAnalysis();
|
||||
Collection<Statement> slice = Slicer.computeForwardSlice(s, cg, pointerAnalysis,
|
||||
DataDependenceOptions.NONE, ControlDependenceOptions.NO_INTERPROC_NO_EXCEPTION);
|
||||
dumpSlice(slice);
|
||||
Assert.assertEquals(8, slice.size());
|
||||
Assert.assertEquals(2, countReturns(slice));
|
||||
}
|
||||
|
||||
@Test
|
||||
|
@ -948,6 +976,19 @@ public class SlicerTest {
|
|||
return count;
|
||||
}
|
||||
|
||||
public static int countReturns(Collection<Statement> slice) {
|
||||
int count = 0;
|
||||
for (Statement s: slice) {
|
||||
if (s.getKind().equals(Statement.Kind.NORMAL)) {
|
||||
NormalStatement ns = (NormalStatement) s;
|
||||
if (ns.getInstruction() instanceof SSAReturnInstruction) {
|
||||
count++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
public static int countGetfields(Collection<Statement> slice) {
|
||||
int count = 0;
|
||||
for (Statement s : slice) {
|
||||
|
|
|
@ -233,7 +233,7 @@ public class PDG<T extends InstanceKey> implements NumberedGraph<Statement> {
|
|||
return;
|
||||
}
|
||||
ControlFlowGraph<SSAInstruction, ISSABasicBlock> controlFlowGraph = ir.getControlFlowGraph();
|
||||
if (cOptions.equals(ControlDependenceOptions.NO_EXCEPTIONAL_EDGES)) {
|
||||
if (cOptions.isIgnoreExceptions()) {
|
||||
PrunedCFG<SSAInstruction, ISSABasicBlock> prunedCFG = ExceptionPrunedCFG.make(controlFlowGraph);
|
||||
// In case the CFG has only the entry and exit nodes left
|
||||
// and no edges because the only control dependencies
|
||||
|
|
|
@ -425,7 +425,7 @@ public class SDG<T extends InstanceKey> extends AbstractNumberedGraph<Statement>
|
|||
}
|
||||
case METHOD_ENTRY:
|
||||
Collection<Statement> result = HashSetFactory.make(5);
|
||||
if (!cOptions.equals(ControlDependenceOptions.NONE)) {
|
||||
if (!cOptions.isIgnoreInterproc()) {
|
||||
for (Iterator<? extends CGNode> it = cg.getPredNodes(N.getNode()); it.hasNext();) {
|
||||
CGNode caller = it.next();
|
||||
for (Iterator<CallSiteReference> it2 = cg.getPossibleSites(caller, N.getNode()); it2.hasNext();) {
|
||||
|
@ -461,7 +461,7 @@ public class SDG<T extends InstanceKey> extends AbstractNumberedGraph<Statement>
|
|||
addPDGStatementNodes(N.getNode());
|
||||
switch (N.getKind()) {
|
||||
case NORMAL:
|
||||
if (cOptions.equals(ControlDependenceOptions.NONE)) {
|
||||
if (cOptions.isIgnoreInterproc()) {
|
||||
return getPDG(N.getNode()).getSuccNodes(N);
|
||||
} else {
|
||||
NormalStatement ns = (NormalStatement) N;
|
||||
|
@ -626,7 +626,7 @@ public class SDG<T extends InstanceKey> extends AbstractNumberedGraph<Statement>
|
|||
addPDGStatementNodes(dst.getNode());
|
||||
switch (src.getKind()) {
|
||||
case NORMAL:
|
||||
if (cOptions.equals(ControlDependenceOptions.NONE)) {
|
||||
if (cOptions.isIgnoreInterproc()) {
|
||||
return getPDG(src.getNode()).hasEdge(src, dst);
|
||||
} else {
|
||||
NormalStatement ns = (NormalStatement) src;
|
||||
|
|
|
@ -244,7 +244,7 @@ class SDGSupergraph implements ISupergraph<Statement, PDG<? extends InstanceKey>
|
|||
case PARAM_CALLER:
|
||||
return true;
|
||||
case NORMAL:
|
||||
if (sdg.getCOptions().equals(ControlDependenceOptions.NONE)) {
|
||||
if (sdg.getCOptions().isIgnoreInterproc()) {
|
||||
return false;
|
||||
} else {
|
||||
NormalStatement s = (NormalStatement) n;
|
||||
|
|
|
@ -33,9 +33,9 @@ import com.ibm.wala.util.collections.HashSetFactory;
|
|||
|
||||
/**
|
||||
* A demand-driven context-sensitive slicer.
|
||||
*
|
||||
*
|
||||
* This computes a context-sensitive slice, building an SDG and finding realizable paths to a statement using tabulation.
|
||||
*
|
||||
*
|
||||
* This implementation uses a preliminary pointer analysis to compute data dependence between heap locations in the SDG.
|
||||
*/
|
||||
public class Slicer {
|
||||
|
@ -113,17 +113,63 @@ public class Slicer {
|
|||
* options to control control dependence edges in the sdg
|
||||
*/
|
||||
public static enum ControlDependenceOptions {
|
||||
FULL("full"), NONE("none"), NO_EXCEPTIONAL_EDGES("no_exceptional_edges");
|
||||
/**
|
||||
* track all control dependencies
|
||||
*/
|
||||
FULL("full", false, false),
|
||||
|
||||
/**
|
||||
* track no control dependencies
|
||||
*/
|
||||
NONE("none", true, true),
|
||||
|
||||
/**
|
||||
* don't track control dependence due to exceptional control flow
|
||||
*/
|
||||
NO_EXCEPTIONAL_EDGES("no_exceptional_edges", true, false),
|
||||
|
||||
/**
|
||||
* don't track control dependence from caller to callee
|
||||
*/
|
||||
NO_INTERPROC_EDGES("no_interproc_edges", false, true),
|
||||
|
||||
/**
|
||||
* don't track interprocedural or exceptional control dependence
|
||||
*/
|
||||
NO_INTERPROC_NO_EXCEPTION("no_interproc_no_exception", true, true);
|
||||
|
||||
|
||||
private final String name;
|
||||
|
||||
ControlDependenceOptions(String name) {
|
||||
/**
|
||||
* ignore control dependence due to exceptional control flow?
|
||||
*/
|
||||
private final boolean ignoreExceptionalEdges;
|
||||
|
||||
/**
|
||||
* ignore interprocedural control dependence, i.e., from caller to callee or the reverse?
|
||||
*/
|
||||
private final boolean ignoreInterprocEdges;
|
||||
|
||||
|
||||
ControlDependenceOptions(String name, boolean ignoreExceptionalEdges, boolean ignoreInterprocEdges) {
|
||||
this.name = name;
|
||||
this.ignoreExceptionalEdges = ignoreExceptionalEdges;
|
||||
this.ignoreInterprocEdges = ignoreInterprocEdges;
|
||||
}
|
||||
|
||||
public final String getName() {
|
||||
return name;
|
||||
}
|
||||
|
||||
public final boolean isIgnoreExceptions() {
|
||||
return ignoreExceptionalEdges;
|
||||
}
|
||||
|
||||
public final boolean isIgnoreInterproc() {
|
||||
return ignoreInterprocEdges;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -149,7 +195,7 @@ public class Slicer {
|
|||
|
||||
/**
|
||||
* Use the passed-in SDG
|
||||
*
|
||||
*
|
||||
* @throws CancelException
|
||||
*/
|
||||
public static Collection<Statement> computeBackwardSlice(SDG sdg, Statement s) throws IllegalArgumentException, CancelException {
|
||||
|
@ -158,7 +204,7 @@ public class Slicer {
|
|||
|
||||
/**
|
||||
* Use the passed-in SDG
|
||||
*
|
||||
*
|
||||
* @throws CancelException
|
||||
*/
|
||||
public static Collection<Statement> computeForwardSlice(SDG sdg, Statement s) throws IllegalArgumentException, CancelException {
|
||||
|
@ -167,7 +213,7 @@ public class Slicer {
|
|||
|
||||
/**
|
||||
* Use the passed-in SDG
|
||||
*
|
||||
*
|
||||
* @throws CancelException
|
||||
*/
|
||||
public static Collection<Statement> computeBackwardSlice(SDG sdg, Collection<Statement> ss) throws IllegalArgumentException,
|
||||
|
@ -188,7 +234,7 @@ public class Slicer {
|
|||
|
||||
/**
|
||||
* Main driver logic.
|
||||
*
|
||||
*
|
||||
* @param sdg governing system dependence graph
|
||||
* @param roots set of roots to slice from
|
||||
* @param backward do a backwards slice?
|
||||
|
@ -250,7 +296,7 @@ public class Slicer {
|
|||
|
||||
/**
|
||||
* Tabulation problem representing slicing
|
||||
*
|
||||
*
|
||||
*/
|
||||
public static class SliceProblem implements PartiallyBalancedTabulationProblem<Statement, PDG<?>, Object> {
|
||||
|
||||
|
|
Loading…
Reference in New Issue