WALA/com.ibm.wala.core.tests/src/com/ibm/wala/examples/drivers/PDFSlice.java

320 lines
13 KiB
Java

/*******************************************************************************
* Copyright (c) 2002 - 2006 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.examples.drivers;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Properties;
import com.ibm.wala.classLoader.Language;
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
import com.ibm.wala.core.tests.slicer.SlicerTest;
import com.ibm.wala.examples.properties.WalaExamplesProperties;
import com.ibm.wala.ipa.callgraph.AnalysisCacheImpl;
import com.ibm.wala.ipa.callgraph.AnalysisOptions;
import com.ibm.wala.ipa.callgraph.AnalysisScope;
import com.ibm.wala.ipa.callgraph.CGNode;
import com.ibm.wala.ipa.callgraph.CallGraph;
import com.ibm.wala.ipa.callgraph.CallGraphBuilder;
import com.ibm.wala.ipa.callgraph.Entrypoint;
import com.ibm.wala.ipa.callgraph.impl.Util;
import com.ibm.wala.ipa.callgraph.propagation.InstanceKey;
import com.ibm.wala.ipa.callgraph.propagation.PointerAnalysis;
import com.ibm.wala.ipa.cha.ClassHierarchy;
import com.ibm.wala.ipa.cha.ClassHierarchyFactory;
import com.ibm.wala.ipa.slicer.HeapStatement;
import com.ibm.wala.ipa.slicer.NormalReturnCaller;
import com.ibm.wala.ipa.slicer.NormalStatement;
import com.ibm.wala.ipa.slicer.ParamCallee;
import com.ibm.wala.ipa.slicer.ParamCaller;
import com.ibm.wala.ipa.slicer.SDG;
import com.ibm.wala.ipa.slicer.Slicer;
import com.ibm.wala.ipa.slicer.Slicer.ControlDependenceOptions;
import com.ibm.wala.ipa.slicer.Slicer.DataDependenceOptions;
import com.ibm.wala.ipa.slicer.Statement;
import com.ibm.wala.ipa.slicer.Statement.Kind;
import com.ibm.wala.properties.WalaProperties;
import com.ibm.wala.ssa.SSAAbstractInvokeInstruction;
import com.ibm.wala.ssa.SSAInstruction;
import com.ibm.wala.ssa.SSAInvokeInstruction;
import com.ibm.wala.types.TypeReference;
import com.ibm.wala.util.CancelException;
import com.ibm.wala.util.WalaException;
import com.ibm.wala.util.config.AnalysisScopeReader;
import com.ibm.wala.util.debug.Assertions;
import com.ibm.wala.util.graph.Graph;
import com.ibm.wala.util.graph.GraphIntegrity;
import com.ibm.wala.util.graph.GraphIntegrity.UnsoundGraphException;
import com.ibm.wala.util.graph.GraphSlicer;
import com.ibm.wala.util.io.CommandLine;
import com.ibm.wala.util.io.FileProvider;
import com.ibm.wala.viz.DotUtil;
import com.ibm.wala.viz.NodeDecorator;
import com.ibm.wala.viz.PDFViewUtil;
/**
*
* This simple example WALA application computes a slice (see {@link Slicer}) and fires off the PDF viewer to view a dot-ted
* representation of the slice.
*
* This is an example program on how to use the slicer.
*
* See the 'PDFSlice' launcher included in the 'launchers' directory.
*
* @see Slicer
* @author sfink
*/
public class PDFSlice {
/**
* Name of the postscript file generated by dot
*/
private final static String PDF_FILE = "slice.pdf";
/**
* Usage: PDFSlice -appJar [jar file name] -mainClass [main class] -srcCaller [method name] -srcCallee [method name] -dd [data
* dependence options] -cd [control dependence options] -dir [forward|backward]
*
* <ul>
* <li>"jar file name" should be something like "c:/temp/testdata/java_cup.jar"
* <li>"main class" should beshould be something like "c:/temp/testdata/java_cup.jar"
* <li>"method name" should be the name of a method. This takes a slice from the statement that calls "srcCallee" from "srcCaller"
* <li>"data dependence options" can be one of "-full", "-no_base_ptrs", "-no_base_no_heap", "-no_heap",
* "-no_base_no_heap_no_cast", or "-none".
* </ul>
*
* @throws CancelException
* @throws IllegalArgumentException
* @throws IOException
*
* @see com.ibm.wala.ipa.slicer.Slicer.DataDependenceOptions <li>"control dependence options" can be "-full" or "-none" <li>the
* -dir argument tells whether to compute a forwards or backwards slice. </ul>
*
*/
public static void main(String[] args) throws IllegalArgumentException, CancelException, IOException {
run(args);
}
/**
* see {@link #main(String[])} for command-line arguments
*
* @throws CancelException
* @throws IllegalArgumentException
* @throws IOException
*/
public static Process run(String[] args) throws IllegalArgumentException, CancelException, IOException {
// parse the command-line into a Properties object
Properties p = CommandLine.parse(args);
// validate that the command-line has the expected format
validateCommandLine(p);
// run the applications
return run(p.getProperty("appJar"), p.getProperty("mainClass"), p.getProperty("srcCaller"), p.getProperty("srcCallee"),
goBackward(p), PDFSDG.getDataDependenceOptions(p), PDFSDG.getControlDependenceOptions(p));
}
/**
* Should the slice be a backwards slice?
*/
private static boolean goBackward(Properties p) {
return !p.getProperty("dir", "backward").equals("forward");
}
/**
* Compute a slice from a call statements, dot it, and fire off the PDF viewer to visualize the result
*
* @param appJar should be something like "c:/temp/testdata/java_cup.jar"
* @param mainClass should be something like "c:/temp/testdata/java_cup.jar"
* @param srcCaller name of the method containing the statement of interest
* @param srcCallee name of the method called by the statement of interest
* @param goBackward do a backward slice?
* @param dOptions options controlling data dependence
* @param cOptions options controlling control dependence
* @return a Process running the PDF viewer to visualize the dot'ted representation of the slice
* @throws CancelException
* @throws IllegalArgumentException
*/
public static Process run(String appJar, String mainClass, String srcCaller, String srcCallee, boolean goBackward,
DataDependenceOptions dOptions, ControlDependenceOptions cOptions) throws IllegalArgumentException, CancelException,
IOException {
try {
// create an analysis scope representing the appJar as a J2SE application
AnalysisScope scope = AnalysisScopeReader.makeJavaBinaryAnalysisScope(appJar, (new FileProvider())
.getFile(CallGraphTestUtil.REGRESSION_EXCLUSIONS));
// build a class hierarchy, call graph, and system dependence graph
ClassHierarchy cha = ClassHierarchyFactory.make(scope);
Iterable<Entrypoint> entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha, mainClass);
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);
CallGraphBuilder<InstanceKey> builder = Util.makeVanillaZeroOneCFABuilder(Language.JAVA, options, new AnalysisCacheImpl(), cha, scope);
// CallGraphBuilder builder = Util.makeZeroOneCFABuilder(options, new
// AnalysisCache(), cha, scope);
CallGraph cg = builder.makeCallGraph(options, null);
SDG<InstanceKey> sdg = new SDG<>(cg, builder.getPointerAnalysis(), dOptions, cOptions);
// find the call statement of interest
CGNode callerNode = SlicerTest.findMethod(cg, srcCaller);
Statement s = SlicerTest.findCallTo(callerNode, srcCallee);
System.err.println("Statement: " + s);
// compute the slice as a collection of statements
Collection<Statement> slice = null;
if (goBackward) {
final PointerAnalysis<InstanceKey> pointerAnalysis = builder.getPointerAnalysis();
slice = Slicer.computeBackwardSlice(s, cg, pointerAnalysis, dOptions, cOptions);
} else {
// for forward slices ... we actually slice from the return value of
// calls.
s = getReturnStatementForCall(s);
final PointerAnalysis<InstanceKey> pointerAnalysis = builder.getPointerAnalysis();
slice = Slicer.computeForwardSlice(s, cg, pointerAnalysis, dOptions, cOptions);
}
SlicerTest.dumpSlice(slice);
// create a view of the SDG restricted to nodes in the slice
Graph<Statement> g = pruneSDG(sdg, slice);
sanityCheck(slice, g);
// load Properties from standard WALA and the WALA examples project
Properties p = null;
try {
p = WalaExamplesProperties.loadProperties();
p.putAll(WalaProperties.loadProperties());
} catch (WalaException e) {
e.printStackTrace();
Assertions.UNREACHABLE();
}
// create a dot representation.
String psFile = p.getProperty(WalaProperties.OUTPUT_DIR) + File.separatorChar + PDF_FILE;
String dotExe = p.getProperty(WalaExamplesProperties.DOT_EXE);
DotUtil.dotify(g, makeNodeDecorator(), PDFTypeHierarchy.DOT_FILE, psFile, dotExe);
// fire off the PDF viewer
String gvExe = p.getProperty(WalaExamplesProperties.PDFVIEW_EXE);
return PDFViewUtil.launchPDFView(psFile, gvExe);
} catch (WalaException e) {
// something bad happened.
e.printStackTrace();
return null;
}
}
/**
* check that g is a well-formed graph, and that it contains exactly the number of nodes in the slice
*/
private static void sanityCheck(Collection<Statement> slice, Graph<Statement> g) {
try {
GraphIntegrity.check(g);
} catch (UnsoundGraphException e1) {
e1.printStackTrace();
Assertions.UNREACHABLE();
}
Assertions.productionAssertion(g.getNumberOfNodes() == slice.size(), "panic " + g.getNumberOfNodes() + " " + slice.size());
}
/**
* If s is a call statement, return the statement representing the normal return from s
*/
public static Statement getReturnStatementForCall(Statement s) {
if (s.getKind() == Kind.NORMAL) {
NormalStatement n = (NormalStatement) s;
SSAInstruction st = n.getInstruction();
if (st instanceof SSAInvokeInstruction) {
SSAAbstractInvokeInstruction call = (SSAAbstractInvokeInstruction) st;
if (call.getCallSite().getDeclaredTarget().getReturnType().equals(TypeReference.Void)) {
throw new IllegalArgumentException("this driver computes forward slices from the return value of calls.\n" + ""
+ "Method " + call.getCallSite().getDeclaredTarget().getSignature() + " returns void.");
}
return new NormalReturnCaller(s.getNode(), n.getInstructionIndex());
} else {
return s;
}
} else {
return s;
}
}
/**
* return a view of the sdg restricted to the statements in the slice
*/
public static Graph<Statement> pruneSDG(SDG<InstanceKey> sdg, final Collection<Statement> slice) {
return GraphSlicer.prune(sdg, slice::contains);
}
/**
* @return a NodeDecorator that decorates statements in a slice for a dot-ted representation
*/
public static NodeDecorator<Statement> makeNodeDecorator() {
return s -> {
switch (s.getKind()) {
case HEAP_PARAM_CALLEE:
case HEAP_PARAM_CALLER:
case HEAP_RET_CALLEE:
case HEAP_RET_CALLER:
HeapStatement h = (HeapStatement) s;
return s.getKind() + "\\n" + h.getNode() + "\\n" + h.getLocation();
case NORMAL:
NormalStatement n = (NormalStatement) s;
return n.getInstruction() + "\\n" + n.getNode().getMethod().getSignature();
case PARAM_CALLEE:
ParamCallee paramCallee = (ParamCallee) s;
return s.getKind() + " " + paramCallee.getValueNumber() + "\\n" + s.getNode().getMethod().getName();
case PARAM_CALLER:
ParamCaller paramCaller = (ParamCaller) s;
return s.getKind() + " " + paramCaller.getValueNumber() + "\\n" + s.getNode().getMethod().getName() + "\\n"
+ paramCaller.getInstruction().getCallSite().getDeclaredTarget().getName();
case EXC_RET_CALLEE:
case EXC_RET_CALLER:
case NORMAL_RET_CALLEE:
case NORMAL_RET_CALLER:
case PHI:
default:
return s.toString();
}
};
}
/**
* Validate that the command-line arguments obey the expected usage.
*
* Usage:
* <ul>
* <li>args[0] : "-appJar"
* <li>args[1] : something like "c:/temp/testdata/java_cup.jar"
* <li>args[2] : "-mainClass"
* <li>args[3] : something like "Lslice/TestRecursion" *
* <li>args[4] : "-srcCallee"
* <li>args[5] : something like "print" *
* <li>args[4] : "-srcCaller"
* <li>args[5] : something like "main"
* </ul>
*
* @throws UnsupportedOperationException if command-line is malformed.
*/
static void validateCommandLine(Properties p) {
if (p.get("appJar") == null) {
throw new UnsupportedOperationException("expected command-line to include -appJar");
}
if (p.get("mainClass") == null) {
throw new UnsupportedOperationException("expected command-line to include -mainClass");
}
if (p.get("srcCallee") == null) {
throw new UnsupportedOperationException("expected command-line to include -srcCallee");
}
if (p.get("srcCaller") == null) {
throw new UnsupportedOperationException("expected command-line to include -srcCaller");
}
}
}