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:
Manu Sridharan 2017-05-26 15:25:42 -07:00
parent 24fb1f6d10
commit 554a6e7ee9
5 changed files with 119 additions and 32 deletions

View File

@ -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) {

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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> {