From 554a6e7ee9a31bc2d016268ae089145bf0a993ff Mon Sep 17 00:00:00 2001 From: Manu Sridharan Date: Fri, 26 May 2017 15:25:42 -0700 Subject: [PATCH] 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. --- .../wala/core/tests/slicer/SlicerTest.java | 77 ++++++++++++++----- .../src/com/ibm/wala/ipa/slicer/PDG.java | 2 +- .../src/com/ibm/wala/ipa/slicer/SDG.java | 6 +- .../ibm/wala/ipa/slicer/SDGSupergraph.java | 2 +- .../src/com/ibm/wala/ipa/slicer/Slicer.java | 64 ++++++++++++--- 5 files changed, 119 insertions(+), 32 deletions(-) diff --git a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/slicer/SlicerTest.java b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/slicer/SlicerTest.java index 18f7edbc3..ed3d0331b 100644 --- a/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/slicer/SlicerTest.java +++ b/com.ibm.wala.core.tests/src/com/ibm/wala/core/tests/slicer/SlicerTest.java @@ -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 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 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 pointerAnalysis = builder.getPointerAnalysis(); + Collection 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 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 slice) { int count = 0; for (Statement s : slice) { diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/PDG.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/PDG.java index 7ac64441c..3f97d695e 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/PDG.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/PDG.java @@ -233,7 +233,7 @@ public class PDG implements NumberedGraph { return; } ControlFlowGraph controlFlowGraph = ir.getControlFlowGraph(); - if (cOptions.equals(ControlDependenceOptions.NO_EXCEPTIONAL_EDGES)) { + if (cOptions.isIgnoreExceptions()) { PrunedCFG prunedCFG = ExceptionPrunedCFG.make(controlFlowGraph); // In case the CFG has only the entry and exit nodes left // and no edges because the only control dependencies diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/SDG.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/SDG.java index e693c73e5..0fe89198b 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/SDG.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/SDG.java @@ -425,7 +425,7 @@ public class SDG extends AbstractNumberedGraph } case METHOD_ENTRY: Collection result = HashSetFactory.make(5); - if (!cOptions.equals(ControlDependenceOptions.NONE)) { + if (!cOptions.isIgnoreInterproc()) { for (Iterator it = cg.getPredNodes(N.getNode()); it.hasNext();) { CGNode caller = it.next(); for (Iterator it2 = cg.getPossibleSites(caller, N.getNode()); it2.hasNext();) { @@ -461,7 +461,7 @@ public class SDG extends AbstractNumberedGraph 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 extends AbstractNumberedGraph 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; diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/SDGSupergraph.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/SDGSupergraph.java index 17dbfd023..ff5d797eb 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/SDGSupergraph.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/SDGSupergraph.java @@ -244,7 +244,7 @@ class SDGSupergraph implements ISupergraph case PARAM_CALLER: return true; case NORMAL: - if (sdg.getCOptions().equals(ControlDependenceOptions.NONE)) { + if (sdg.getCOptions().isIgnoreInterproc()) { return false; } else { NormalStatement s = (NormalStatement) n; diff --git a/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/Slicer.java b/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/Slicer.java index 706a14256..c714b4772 100644 --- a/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/Slicer.java +++ b/com.ibm.wala.core/src/com/ibm/wala/ipa/slicer/Slicer.java @@ -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 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 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 computeBackwardSlice(SDG sdg, Collection 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, Object> {