changes for handling of 'callbacks' in dynamic CGs
This commit is contained in:
parent
93a522eecd
commit
a6a060ed25
@ -2,6 +2,6 @@
|
||||
<classpath>
|
||||
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
|
||||
<classpathentry kind="src" path="src"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.7"/>
|
||||
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
|
||||
<classpathentry kind="output" path="bin"/>
|
||||
</classpath>
|
||||
|
||||
@ -7,9 +7,9 @@ org.eclipse.jdt.core.compiler.annotation.nullable=org.eclipse.jdt.annotation.Nul
|
||||
org.eclipse.jdt.core.compiler.annotation.nullanalysis=disabled
|
||||
org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled
|
||||
org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7
|
||||
org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8
|
||||
org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve
|
||||
org.eclipse.jdt.core.compiler.compliance=1.7
|
||||
org.eclipse.jdt.core.compiler.compliance=1.8
|
||||
org.eclipse.jdt.core.compiler.debug.lineNumber=generate
|
||||
org.eclipse.jdt.core.compiler.debug.localVariable=generate
|
||||
org.eclipse.jdt.core.compiler.debug.sourceFile=generate
|
||||
@ -106,7 +106,7 @@ org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disa
|
||||
org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=warning
|
||||
org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning
|
||||
org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=warning
|
||||
org.eclipse.jdt.core.compiler.source=1.7
|
||||
org.eclipse.jdt.core.compiler.source=1.8
|
||||
org.eclipse.jdt.core.formatter.align_type_members_on_columns=false
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_allocation_expression=16
|
||||
org.eclipse.jdt.core.formatter.alignment_for_arguments_in_annotation=0
|
||||
|
||||
@ -0,0 +1,33 @@
|
||||
package dynamicCG;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
public class CallbacksMainClass {
|
||||
|
||||
private static CallbacksMainClass instance;
|
||||
|
||||
static {
|
||||
callSomethingStatic();
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
Set<CallbacksMainClass> junk = new HashSet<CallbacksMainClass>();
|
||||
junk.add(instance);
|
||||
System.err.println(junk.iterator().next().toString());
|
||||
}
|
||||
|
||||
private static void callSomethingStatic() {
|
||||
instance = new CallbacksMainClass();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return callSomething();
|
||||
}
|
||||
|
||||
private String callSomething() {
|
||||
return "string";
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,5 +1,3 @@
|
||||
apple\/.*
|
||||
com\/apple\/.*
|
||||
java\/awt\/.*
|
||||
javax\/swing\/.*
|
||||
sun\/awt\/.*
|
||||
@ -13,6 +11,8 @@ com\/ibm\/security\/.*
|
||||
org\/apache\/xerces\/.*
|
||||
dalvik\/.*
|
||||
java\/io\/ObjectStreamClass*
|
||||
apple\/.*
|
||||
com\/apple\/.*
|
||||
jdk\/.*
|
||||
org\/omg\/.*
|
||||
org\/w3c\/.*
|
||||
|
||||
@ -32,13 +32,11 @@ import com.ibm.wala.util.CancelException;
|
||||
public class DynamicCallGraphTest extends DynamicCallGraphTestBase {
|
||||
|
||||
private static String testJarLocation = getClasspathEntry("com.ibm.wala.core.testdata");
|
||||
|
||||
private static String testMain = "dynamicCG.MainClass";
|
||||
|
||||
private CallGraph staticCG(String exclusionsFile) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException {
|
||||
|
||||
private CallGraph staticCG(String mainClass, String exclusionsFile) throws IOException, ClassHierarchyException, IllegalArgumentException, CancelException {
|
||||
AnalysisScope scope = CallGraphTestUtil.makeJ2SEAnalysisScope(TestConstants.WALA_TESTDATA, exclusionsFile != null? exclusionsFile: CallGraphTestUtil.REGRESSION_EXCLUSIONS);
|
||||
ClassHierarchy cha = ClassHierarchy.make(scope);
|
||||
Iterable<Entrypoint> entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha, "LdynamicCG/MainClass");
|
||||
Iterable<Entrypoint> entrypoints = com.ibm.wala.ipa.callgraph.impl.Util.makeMainEntrypoints(scope, cha, mainClass);
|
||||
AnalysisOptions options = CallGraphTestUtil.makeAnalysisOptions(scope, entrypoints);
|
||||
return CallGraphTestUtil.buildZeroOneCFA(options, new AnalysisCache(), cha, scope, false);
|
||||
}
|
||||
@ -46,16 +44,24 @@ public class DynamicCallGraphTest extends DynamicCallGraphTestBase {
|
||||
@Test
|
||||
public void testGraph() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassHierarchyException, CancelException, InterruptedException {
|
||||
instrument(testJarLocation);
|
||||
run(testMain, null);
|
||||
CallGraph staticCG = staticCG(null);
|
||||
run("dynamicCG.MainClass", null);
|
||||
CallGraph staticCG = staticCG("LdynamicCG/MainClass", null);
|
||||
checkEdges(staticCG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCallbacks() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassHierarchyException, CancelException, InterruptedException {
|
||||
instrument(testJarLocation);
|
||||
run("dynamicCG.CallbacksMainClass", null);
|
||||
CallGraph staticCG = staticCG("LdynamicCG/CallbacksMainClass", null);
|
||||
checkEdges(staticCG);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testExclusions() throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException, ClassHierarchyException, CancelException, InterruptedException {
|
||||
instrument(testJarLocation);
|
||||
run(testMain, "ShrikeTestExclusions.txt");
|
||||
CallGraph staticCG = staticCG("ShrikeTestExclusions.txt");
|
||||
run("dynamicCG.MainClass", "ShrikeTestExclusions.txt");
|
||||
CallGraph staticCG = staticCG("LdynamicCG/MainClass", "ShrikeTestExclusions.txt");
|
||||
checkEdges(staticCG);
|
||||
}
|
||||
|
||||
|
||||
@ -61,9 +61,9 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase {
|
||||
|
||||
private boolean instrumentedJarBuilt = false;
|
||||
|
||||
private static String instrumentedJarLocation = System.getProperty("java.io.tmpdir") + File.separator + "test.jar";
|
||||
private String instrumentedJarLocation = System.getProperty("java.io.tmpdir") + File.separator + "test.jar";
|
||||
|
||||
private static String cgLocation = System.getProperty("java.io.tmpdir") + File.separator + "cg.txt";
|
||||
private String cgLocation = System.getProperty("java.io.tmpdir") + File.separator + "cg.txt";
|
||||
|
||||
protected void instrument(String testJarLocation) throws IOException, ClassNotFoundException, InvalidClassFileException, FailureException {
|
||||
if (! instrumentedJarBuilt) {
|
||||
@ -116,8 +116,14 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase {
|
||||
childJvm.setFailonerror(true);
|
||||
childJvm.setFork(true);
|
||||
|
||||
if (new File(cgLocation).exists()) {
|
||||
new File(cgLocation).delete();
|
||||
}
|
||||
|
||||
childJvm.init();
|
||||
Process x = Runtime.getRuntime().exec(childJvm.getCommandLine().toString());
|
||||
String commandLine = childJvm.getCommandLine().toString();
|
||||
System.err.println(commandLine);
|
||||
Process x = Runtime.getRuntime().exec(commandLine);
|
||||
x.waitFor();
|
||||
|
||||
Assert.assertTrue("expected to create call graph", new File(cgLocation).exists());
|
||||
@ -149,7 +155,7 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase {
|
||||
Pair<CGNode,CGNode> x = Pair.make(caller, callee);
|
||||
if (! edges.contains(x)) {
|
||||
edges.add(x);
|
||||
System.err.println("found expected edge" + caller + " --> " + callee);
|
||||
System.err.println("found expected edge " + caller + " --> " + callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -189,6 +195,8 @@ public abstract class DynamicCallGraphTestBase extends WalaTestCase {
|
||||
String callerClass = edge.nextToken();
|
||||
if ("root".equals(callerClass)) {
|
||||
caller = staticCG.getFakeRootNode();
|
||||
} else if ("callbacks".equals(callerClass)) {
|
||||
continue loop;
|
||||
} else {
|
||||
String callerMethod = edge.nextToken();
|
||||
MethodReference callerRef = MethodReference.findOrCreate(TypeReference.findOrCreate(ClassLoaderReference.Application, "L" + callerClass), Selector.make(callerMethod));
|
||||
|
||||
@ -51,7 +51,6 @@ import com.ibm.wala.util.functions.Function;
|
||||
import com.ibm.wala.util.intset.IntSet;
|
||||
import com.ibm.wala.util.intset.IntSetUtil;
|
||||
|
||||
@SuppressWarnings("deprecation")
|
||||
public class MethodHandles {
|
||||
|
||||
private static final IntSet self = IntSetUtil.make(new int[0]);
|
||||
|
||||
@ -14,6 +14,7 @@ import java.lang.ref.SoftReference;
|
||||
import java.util.Iterator;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.Stack;
|
||||
|
||||
import com.ibm.wala.classLoader.CallSiteReference;
|
||||
import com.ibm.wala.classLoader.IMethod;
|
||||
@ -32,9 +33,9 @@ import com.ibm.wala.shrikeBT.IInvokeInstruction;
|
||||
import com.ibm.wala.ssa.DefUse;
|
||||
import com.ibm.wala.ssa.IR;
|
||||
import com.ibm.wala.util.CancelException;
|
||||
import com.ibm.wala.util.Predicate;
|
||||
import com.ibm.wala.util.collections.ComposedIterator;
|
||||
import com.ibm.wala.util.collections.EmptyIterator;
|
||||
import com.ibm.wala.util.Predicate;
|
||||
import com.ibm.wala.util.collections.FilterIterator;
|
||||
import com.ibm.wala.util.collections.HashMapFactory;
|
||||
import com.ibm.wala.util.collections.HashSetFactory;
|
||||
@ -116,7 +117,8 @@ public class CHACallGraph extends BasicCallGraph<CHAContextInterpreter> {
|
||||
for(Entrypoint e : entrypoints) {
|
||||
root.addTarget(e.makeSite(programCounter++), null);
|
||||
}
|
||||
closure(root, true);
|
||||
newNodes.push(root);
|
||||
closure();
|
||||
isInitialized = true;
|
||||
}
|
||||
|
||||
@ -206,24 +208,6 @@ public class CHACallGraph extends BasicCallGraph<CHAContextInterpreter> {
|
||||
protected CGNode makeFakeWorldClinitNode() throws CancelException {
|
||||
return new CHARootNode(new FakeWorldClinitMethod(cha, options, cache), Everywhere.EVERYWHERE);
|
||||
}
|
||||
|
||||
private void closure(CGNode n, boolean fromRoot) throws CancelException {
|
||||
for(Iterator<CallSiteReference> sites = n.iterateCallSites(); sites.hasNext(); ) {
|
||||
Iterator<IMethod> methods = getPossibleTargets(sites.next());
|
||||
while (methods.hasNext()) {
|
||||
IMethod target = methods.next();
|
||||
if (!target.isAbstract()) {
|
||||
CGNode callee = getNode(target, Everywhere.EVERYWHERE);
|
||||
if (callee == null) {
|
||||
callee = findOrCreateNode(target, Everywhere.EVERYWHERE);
|
||||
if (fromRoot) {
|
||||
registerEntrypoint(callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int clinitPC = 0;
|
||||
|
||||
@ -247,12 +231,35 @@ public class CHACallGraph extends BasicCallGraph<CHAContextInterpreter> {
|
||||
return n;
|
||||
}
|
||||
|
||||
private Stack<CGNode> newNodes = new Stack<CGNode>();
|
||||
|
||||
private void closure() throws CancelException {
|
||||
while (! newNodes.isEmpty()) {
|
||||
CGNode n = newNodes.pop();
|
||||
for(Iterator<CallSiteReference> sites = n.iterateCallSites(); sites.hasNext(); ) {
|
||||
Iterator<IMethod> methods = getPossibleTargets(sites.next());
|
||||
while (methods.hasNext()) {
|
||||
IMethod target = methods.next();
|
||||
if (!target.isAbstract()) {
|
||||
CGNode callee = getNode(target, Everywhere.EVERYWHERE);
|
||||
if (callee == null) {
|
||||
callee = findOrCreateNode(target, Everywhere.EVERYWHERE);
|
||||
if (n == getFakeRootNode()) {
|
||||
registerEntrypoint(callee);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CGNode makeNewNode(IMethod method, Context C) throws CancelException {
|
||||
CGNode n;
|
||||
Key k = new Key(method, C);
|
||||
n = new CHANode(method, C);
|
||||
registerNode(k, n);
|
||||
closure(n, false);
|
||||
newNodes.push(n);
|
||||
return n;
|
||||
}
|
||||
|
||||
|
||||
@ -260,11 +260,8 @@ public abstract class PropagationCallGraphBuilder implements CallGraphBuilder {
|
||||
|
||||
/** BEGIN Custom change: throw exception on empty entry points. This is a severe issue that should not go undetected! */
|
||||
if (entrypointCallSites.isEmpty()) {
|
||||
throw new IllegalStateException("Could not create a entrypoint callsites."
|
||||
+ " This happens when some parameters of the method can not be generated automatically "
|
||||
+ "(e.g. when they refer to an interface or an abstract class).");
|
||||
}
|
||||
|
||||
throw new IllegalStateException("Could not create a entrypoint callsites: " + Warnings.asString());
|
||||
}
|
||||
/** END Custom change: throw exception on empty entry points. This is a severe issue that should not go undetected! */
|
||||
customInit();
|
||||
|
||||
|
||||
@ -146,17 +146,19 @@ public abstract class DroidBenchCGTest extends DalvikCallGraphTestBase {
|
||||
skipTests.add("Button2.apk");
|
||||
}
|
||||
|
||||
public static Collection<Object[]> generateData(final URI[] androidLibs, final File androidJavaJar, final String filter) {
|
||||
String f = walaProperties.getProperty("droidbench.root");
|
||||
if (f == null || !new File(f).exists()) {
|
||||
f = "/tmp/DroidBench";
|
||||
}
|
||||
|
||||
System.err.println("Use " + f + " as droid bench root");
|
||||
assert new File(f).exists() : "Use " + f + " as droid bench root";
|
||||
assert new File(f + "/apk/").exists() : "Use " + f + " as droid bench root";
|
||||
String droidBenchRoot = f;
|
||||
|
||||
public static Collection<Object[]> generateData(final URI[] androidLibs, final File androidJavaJar, final String filter) {
|
||||
String f = walaProperties.getProperty("droidbench.root");
|
||||
if (f == null || !new File(f).exists()) {
|
||||
f = "/tmp/DroidBench";
|
||||
}
|
||||
|
||||
System.err.println("Use " + f + " as droid bench root");
|
||||
assert new File(f).exists() : "Use " + f + " as droid bench root";
|
||||
assert new File(f + "/apk/").exists() : "Use " + f + " as droid bench root";
|
||||
return generateData(f, androidLibs, androidJavaJar, filter);
|
||||
}
|
||||
|
||||
public static Collection<Object[]> generateData(String droidBenchRoot, final URI[] androidLibs, final File androidJavaJar, final String filter) {
|
||||
final List<Object[]> files = new LinkedList<Object[]>();
|
||||
FileUtil.recurseFiles(new VoidFunction<File>() {
|
||||
@Override
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -23,12 +23,42 @@ import com.ibm.wala.util.config.FileOfClasses;
|
||||
import com.ibm.wala.util.config.SetOfClasses;
|
||||
|
||||
public class Runtime {
|
||||
public interface Policy {
|
||||
void callback(StackTraceElement[] stack, String klass, String method, Object receiver);
|
||||
}
|
||||
|
||||
private static class DefaultCallbackPolicy implements Policy {
|
||||
// if not found:
|
||||
// look up the stack for expected caller
|
||||
// if found:
|
||||
// (callback case)
|
||||
// record real target of expected caller (or not)
|
||||
// policy-based edge for call to current method
|
||||
// if not found:
|
||||
// (async system edge)
|
||||
// policy-based edge for call to current method
|
||||
@Override
|
||||
public void callback(StackTraceElement[] stack, String klass, String method, Object receiver) {
|
||||
// stack frames: Runtime.execution(0), callee(1), caller(2)
|
||||
String root = "<clinit>".equals(stack[1].getMethodName())? "clinit": "callbacks";
|
||||
String line = root + "\t" + bashToDescriptor(klass) + "\t" + String.valueOf(method) + "\n";
|
||||
synchronized (runtime) {
|
||||
if (runtime.output != null) {
|
||||
runtime.output.printf(line);
|
||||
runtime.output.flush();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private static final Runtime runtime =
|
||||
new Runtime(System.getProperty("dynamicCGFile"), System.getProperty("dynamicCGFilter"));
|
||||
new Runtime(System.getProperty("dynamicCGFile"),
|
||||
System.getProperty("dynamicCGFilter"),
|
||||
System.getProperty("policyClass", "com.ibm.wala.shrike.cg.Runtime$DefaultPolicy"));
|
||||
|
||||
private PrintWriter output;
|
||||
private SetOfClasses filter;
|
||||
private boolean handleUninstrumentedCode = false;
|
||||
private Policy handleCallback;
|
||||
|
||||
private ThreadLocal<Stack<String>> callStacks = new ThreadLocal<Stack<String>>() {
|
||||
|
||||
@ -41,7 +71,7 @@ public class Runtime {
|
||||
|
||||
};
|
||||
|
||||
private Runtime(String fileName, String filterFileName) {
|
||||
private Runtime(String fileName, String filterFileName, String policyClassName) {
|
||||
try {
|
||||
filter = new FileOfClasses(new FileInputStream(filterFileName));
|
||||
} catch (Exception e) {
|
||||
@ -54,7 +84,11 @@ public class Runtime {
|
||||
output = new PrintWriter(System.err);
|
||||
}
|
||||
|
||||
handleUninstrumentedCode = Boolean.parseBoolean(System.getProperty("dynamicCGHandleMissing", "false"));
|
||||
try {
|
||||
handleCallback = (Policy) Class.forName(policyClassName).newInstance();
|
||||
} catch (InstantiationException | IllegalAccessException | ClassNotFoundException e) {
|
||||
handleCallback = new DefaultCallbackPolicy();
|
||||
}
|
||||
|
||||
java.lang.Runtime.getRuntime().addShutdownHook(new Thread() {
|
||||
@Override
|
||||
@ -96,13 +130,17 @@ public class Runtime {
|
||||
String caller = runtime.callStacks.get().peek();
|
||||
|
||||
checkValid: {
|
||||
if (runtime.handleUninstrumentedCode) {
|
||||
//
|
||||
// check for expected caller
|
||||
//
|
||||
if (runtime.handleCallback != null) {
|
||||
StackTraceElement[] stack = (new Throwable()).getStackTrace();
|
||||
if (stack.length > 2) {
|
||||
// frames: me(0), callee(1), caller(2)
|
||||
// frames: Runtime.execution(0), callee(1), caller(2)
|
||||
StackTraceElement callerFrame = stack[2];
|
||||
if (! caller.contains(callerFrame.getMethodName()) ||
|
||||
! caller.contains(bashToDescriptor(callerFrame.getClassName()))) {
|
||||
runtime.handleCallback.callback(stack, klass, method, receiver);
|
||||
break checkValid;
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user