com.ibm.wala.examples.drivers.PDFSlice Maven / Gradle / Ivy
/*
* 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 com.ibm.wala.classLoader.Language;
import com.ibm.wala.core.tests.callGraph.CallGraphTestUtil;
import com.ibm.wala.core.util.config.AnalysisScopeReader;
import com.ibm.wala.core.util.io.FileProvider;
import com.ibm.wala.core.viz.PDFViewUtil;
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.callgraph.util.CallGraphSearchUtil;
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.SlicerUtil;
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.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.viz.DotUtil;
import com.ibm.wala.util.viz.NodeDecorator;
import java.io.File;
import java.io.IOException;
import java.util.Collection;
import java.util.Properties;
/**
* 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 static final 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]
*
*
* - "jar file name" should be something like "c:/temp/testdata/java_cup.jar"
*
- "main class" should beshould be something like "c:/temp/testdata/java_cup.jar"
*
- "method name" should be the name of a method. This takes a slice from the statement that
* calls "srcCallee" from "srcCaller"
*
- "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".
*
- "control dependence options" can be "-full" or "-none"
*
- the -dir argument tells whether to compute a forwards or backwards slice.
*
*
* @see com.ibm.wala.ipa.slicer.Slicer.DataDependenceOptions
*/
public static void main(String[] args)
throws IllegalArgumentException, CancelException, IOException {
run(args);
}
/** see {@link #main(String[])} for command-line arguments */
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
*/
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.instance.makeJavaBinaryAnalysisScope(
appJar, new FileProvider().getFile(CallGraphTestUtil.REGRESSION_EXCLUSIONS));
// build a class hierarchy, call graph, and system dependence graph
ClassHierarchy cha = ClassHierarchyFactory.make(scope);
Iterable entrypoints =
com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(cha, mainClass);
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);
CallGraphBuilder builder =
Util.makeVanillaZeroOneCFABuilder(Language.JAVA, options, new AnalysisCacheImpl(), cha);
// CallGraphBuilder builder = Util.makeZeroOneCFABuilder(options, new
// AnalysisCache(), cha, scope);
CallGraph cg = builder.makeCallGraph(options, null);
SDG sdg = new SDG<>(cg, builder.getPointerAnalysis(), dOptions, cOptions);
// find the call statement of interest
CGNode callerNode = CallGraphSearchUtil.findMethod(cg, srcCaller);
Statement s = SlicerUtil.findCallTo(callerNode, srcCallee);
System.err.println("Statement: " + s);
// compute the slice as a collection of statements
final Collection slice;
if (goBackward) {
final PointerAnalysis 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 pointerAnalysis = builder.getPointerAnalysis();
slice = Slicer.computeForwardSlice(s, cg, pointerAnalysis, dOptions, cOptions);
}
SlicerUtil.dumpSlice(slice);
// create a view of the SDG restricted to nodes in the slice
Graph 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 slice, Graph 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 pruneSDG(SDG sdg, final Collection slice) {
return GraphSlicer.prune(sdg, slice::contains);
}
/**
* @return a NodeDecorator that decorates statements in a slice for a dot-ted representation
*/
public static NodeDecorator 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:
*
*
* - args[0] : "-appJar"
*
- args[1] : something like "c:/temp/testdata/java_cup.jar"
*
- args[2] : "-mainClass"
*
- args[3] : something like "Lslice/TestRecursion" *
*
- args[4] : "-srcCallee"
*
- args[5] : something like "print" *
*
- args[4] : "-srcCaller"
*
- args[5] : something like "main"
*
*
* @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");
}
}
}