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:
parent
c4a04d7eec
commit
837cb5aee5
|
@ -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");
|
||||
|
|
|
@ -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}));
|
||||
}
|
||||
});
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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");
|
||||
|
|
|
@ -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));
|
||||
|
|
Loading…
Reference in New Issue