1) compress dumped call graph edges

2) make instrumentor preserve the names of jar entries and classes as
they are input, rather than recomputing class names when writing the
output jar.  This usually makes no difference, but can preserve broken
structures when the input jar file has mismatches between class names
and its entry names.
This commit is contained in:
Julian Dolby 2014-01-03 10:10:03 -05:00 committed by Michael Heilmann
parent c4a04d7eec
commit 837cb5aee5
6 changed files with 67 additions and 25 deletions

View File

@ -2,14 +2,16 @@ package com.ibm.wala.core.tests.shrike;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.zip.GZIPInputStream;
import org.junit.Assert;
import org.junit.Test;
@ -77,7 +79,7 @@ public class DynamicCallGraphTests extends WalaTestCase {
}
private void check(CallGraph staticCG) throws IOException {
BufferedReader dynamicEdgesFile = new BufferedReader(new FileReader(System.getProperty("dynamicCGFile")));
BufferedReader dynamicEdgesFile = new BufferedReader(new InputStreamReader(new GZIPInputStream(new FileInputStream(System.getProperty("dynamicCGFile")))));
String line;
while ((line = dynamicEdgesFile.readLine()) != null) {
StringTokenizer edge = new StringTokenizer(line, "\t");

View File

@ -43,9 +43,6 @@ import com.ibm.wala.util.config.SetOfClasses;
* System.err.println() at ever method call, and a System.err.println() at every
* method entry.
*
* In Unix, I run it like this: java -cp ~/dev/shrike/shrike
* com.ibm.dynHLRace.shrikeInstrumentor test.jar -o output.jar
*
* The instrumented classes are placed in the directory "output" under the
* current directory. Disassembled code is written to the file "report" under
* the current directory.
@ -90,7 +87,7 @@ public class DynamicCallGraph {
private static void doClass(final ClassInstrumenter ci, Writer w) throws InvalidClassFileException, IOException, FailureException {
final String className = ci.getReader().getName();
if (filter != null && ! filter.contains(className)) {
if (filter != null && filter.contains(className)) {
return;
}
w.write("Class: " + className + "\n");
@ -102,6 +99,10 @@ public class DynamicCallGraph {
// d could be null, e.g., if the method is abstract or native
if (d != null) {
if (filter != null && filter.contains(className + "." + ci.getReader().getMethodName(m))) {
return;
}
w.write("Instrumenting " + ci.getReader().getMethodName(m) + " " + ci.getReader().getMethodType(m) + ":\n");
w.flush();
@ -131,7 +132,8 @@ public class DynamicCallGraph {
if (nonStatic && !isConstructor)
w.emit(LoadInstruction.make(Constants.TYPE_Object, 0)); //load this
else
w.emit(ConstantInstruction.make(Constants.TYPE_null, null));
w.emit(Util.makeGet(runtime, "NULL_TAG"));
// w.emit(ConstantInstruction.make(Constants.TYPE_null, null));
w.emit(Util.makeInvoke(runtime, "execution", new Class[] {String.class, String.class, Object.class}));
}
});
@ -147,7 +149,8 @@ public class DynamicCallGraph {
if (nonStatic)
w.emit(LoadInstruction.make(Constants.TYPE_Object, 0)); //load this
else
w.emit(ConstantInstruction.make(Constants.TYPE_null, null));
w.emit(Util.makeGet(runtime, "NULL_TAG"));
// w.emit(ConstantInstruction.make(Constants.TYPE, null));
w.emit(ConstantInstruction.make(0)); // false
w.emit(Util.makeInvoke(runtime, "termination", new Class[] {String.class, String.class, Object.class, boolean.class}));
}
@ -164,7 +167,8 @@ public class DynamicCallGraph {
if (nonStatic)
w.emit(LoadInstruction.make(Constants.TYPE_Object, 0)); //load this
else
w.emit(ConstantInstruction.make(Constants.TYPE_null, null));
w.emit(Util.makeGet(runtime, "NULL_TAG"));
// w.emit(ConstantInstruction.make(Constants.TYPE_null, null));
w.emit(ConstantInstruction.make(1)); // true
w.emit(Util.makeInvoke(runtime, "termination", new Class[] {String.class, String.class, Object.class, boolean.class}));
}
@ -190,7 +194,8 @@ public class DynamicCallGraph {
w.emit(ConstantInstruction.makeString(calleeClass));
w.emit(ConstantInstruction.makeString(calleeMethod));
// target unknown
w.emit(ConstantInstruction.make(Constants.TYPE_null, null));
w.emit(Util.makeGet(runtime, "NULL_TAG"));
// w.emit(ConstantInstruction.make(Constants.TYPE_null, null));
w.emit(Util.makeInvoke(runtime, "addToCallStack", new Class[] {String.class, String.class, Object.class}));
}
});

View File

@ -1,10 +1,12 @@
package com.ibm.wala.shrike.cg;
import java.io.FileInputStream;
import java.io.FileWriter;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.OutputStreamWriter;
import java.io.PrintWriter;
import java.util.Stack;
import java.util.zip.GZIPOutputStream;
import com.ibm.wala.util.config.FileOfClasses;
import com.ibm.wala.util.config.SetOfClasses;
@ -29,16 +31,22 @@ public class Runtime {
}
try {
output = new PrintWriter(new FileWriter(fileName));
output = new PrintWriter(new OutputStreamWriter(new GZIPOutputStream(new FileOutputStream(fileName))));
} catch (IOException e) {
output = new PrintWriter(System.err);
}
}
public static Object NULL_TAG = new Object() {
@Override
public String toString() {
return "NULL TAG";
}
};
public static void execution(String klass, String method, Object receiver) {
if (runtime.filter == null || ! runtime.filter.contains(klass)) {
runtime.output.printf(runtime.callStack.peek() + "\t" + klass + "\t" + method + "\n");
runtime.output.flush();
}
runtime.callStack.push(klass + "\t" + method);
@ -46,6 +54,9 @@ public class Runtime {
public static void termination(String klass, String method, Object receiver, boolean exception) {
runtime.callStack.pop();
if ("root".equals(runtime.callStack.peek())) {
runtime.output.close();
}
}
public static void pop(String klass, String method) {

View File

@ -52,13 +52,22 @@ final public class ClassInstrumenter {
private int fakeLineOffset;
private final String inputName;
/**
* Create a class instrumenter from raw bytes.
*/
public ClassInstrumenter(byte[] bytes) throws InvalidClassFileException {
this(new ClassReader(bytes));
public ClassInstrumenter(String inputName, byte[] bytes) throws InvalidClassFileException {
this(inputName, new ClassReader(bytes));
}
/**
* @return name of resource from which this class was read
*/
public String getInputName() {
return inputName;
}
/**
* Calling this means that methods without line numbers get fake line numbers added: each bytecode instruction is treated as at
* line 'offset' + the offset of the instruction.
@ -73,7 +82,7 @@ final public class ClassInstrumenter {
*
* @throws IllegalArgumentException if cr is null
*/
public ClassInstrumenter(ClassReader cr) throws InvalidClassFileException {
public ClassInstrumenter(String inputName, ClassReader cr) throws InvalidClassFileException {
if (cr == null) {
throw new IllegalArgumentException("cr is null");
}
@ -82,6 +91,7 @@ final public class ClassInstrumenter {
oldCode = new CodeReader[methods.length];
cpr = CTDecoder.makeConstantPoolReader(cr);
deletedMethods = new boolean[methods.length];
this.inputName = inputName;
}
/**

View File

@ -31,11 +31,11 @@ final public class OfflineInstrumenter extends OfflineInstrumenterBase {
}
@Override
protected Object makeClassFromStream(BufferedInputStream s) throws IOException {
protected Object makeClassFromStream(String inputName, BufferedInputStream s) throws IOException {
byte[] bytes = new byte[s.available()];
Util.readFully(s, bytes);
try {
return new ClassInstrumenter(bytes);
return new ClassInstrumenter(inputName, bytes);
} catch (InvalidClassFileException e) {
throw new IOException("Class is invalid: " + e.getMessage());
}
@ -73,7 +73,7 @@ final public class OfflineInstrumenter extends OfflineInstrumenterBase {
* methods to 'code' (or make other changes) before calling this method.
*/
public void outputModifiedClass(ClassInstrumenter out, ClassWriter code) throws IllegalStateException, IOException {
internalOutputModifiedClass(out, code);
internalOutputModifiedClass(out, out.getInputName(), code);
}
/**
@ -84,7 +84,7 @@ final public class OfflineInstrumenter extends OfflineInstrumenterBase {
throw new IllegalArgumentException();
}
try {
internalOutputModifiedClass(out, out.emitClass());
internalOutputModifiedClass(out, out.getInputName(), out.emitClass());
} catch (InvalidClassFileException e) {
e.printStackTrace();
throw new IOException("Invalid class file");

View File

@ -89,6 +89,11 @@ public abstract class OfflineInstrumenterBase {
return className;
}
/**
* get name of resource used for input
*/
public abstract String getInputName();
/**
* Open the resource for reading as a stream.
*/
@ -135,6 +140,11 @@ public abstract class OfflineInstrumenterBase {
return name.endsWith(".class");
}
@Override
public String getInputName() {
return name;
}
/**
* Get the underlying ZipEntry corresponding to this resource.
*/
@ -181,6 +191,11 @@ public abstract class OfflineInstrumenterBase {
public String toString() {
return file.getPath();
}
@Override
public String getInputName() {
return file.getPath();
}
}
protected OfflineInstrumenterBase() {
@ -360,7 +375,7 @@ public abstract class OfflineInstrumenterBase {
inputIndex = 0;
}
protected abstract Object makeClassFromStream(BufferedInputStream s) throws IOException;
protected abstract Object makeClassFromStream(String inputName, BufferedInputStream s) throws IOException;
protected abstract String getClassName(Object cl);
@ -378,7 +393,7 @@ public abstract class OfflineInstrumenterBase {
}
BufferedInputStream s = new BufferedInputStream(in.open());
try {
Object r = makeClassFromStream(s);
Object r = makeClassFromStream(in.getInputName(), s);
String name = getClassName(r);
in.setClassName(name);
return r;
@ -415,9 +430,8 @@ public abstract class OfflineInstrumenterBase {
return outputFile;
}
final protected boolean internalOutputModifiedClass(Object cf, Object mods) throws IOException {
final protected boolean internalOutputModifiedClass(Object cf, String name, Object mods) throws IOException {
makeOutputJar();
String name = toEntryName(getClassName(cf));
if (entryNames.contains(name)) {
return false;
} else {
@ -549,7 +563,7 @@ public abstract class OfflineInstrumenterBase {
if (name == null) {
BufferedInputStream s = new BufferedInputStream(in.open(), 65536);
try {
Object cl = makeClassFromStream(s);
Object cl = makeClassFromStream(in.getInputName(), s);
String entryName = toEntryName(getClassName(cl));
if (!entryNames.contains(entryName)) {
putNextEntry(new ZipEntry(entryName));