package xsched.instrumentation;
import java.io.IOException;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import xsched.Task;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.expr.ExprEditor;
import javassist.expr.MethodCall;
public class ScheduleSiteRewriter implements ClassFileTransformer {
final ClassPool classPool;
public ScheduleSiteRewriter() {
classPool = ClassPool.getDefault();
}
private void instrumentMethod(final CtMethod method) throws CannotCompileException {
//there are GeneratedMethodAccessor2.invoke() methods out there (from the use of reflection in Task)
//that call xschedTask methods
//and if we rewrite them we get weird behavior...
if(method.getLongName().startsWith("sun.reflect."))
return;
method.instrument(new ExprEditor() {
@Override
public void edit(MethodCall m) throws CannotCompileException {
if(m.getMethodName().startsWith(Task.MainTaskMethodPrefix)) {
String statement = "{ xsched.Runtime.scheduleMainTask($0, \"" + m.getMethodName() + "\", $args); }";
if(Task.DEBUG)
System.out.println("found schedule site: " + m.getMethodName() + " in " + method.getLongName() + "; replacing it with " + statement);
m.replace(statement);
} else if (m.getMethodName().startsWith(Task.NormalTaskMethodPrefix)) {
String statement = "{ xsched.Runtime.scheduleNormalTask($0, \"" + m.getMethodName() + "\", $args); }";
if(Task.DEBUG)
System.out.println("found schedule site: " + m.getMethodName() + " in " + method.getLongName() + "; replacing it with " + statement);
m.replace(statement);
}
}
});
}
@Override
public byte[] transform(ClassLoader loader, String className,
Class<?> classBeingRedefined, ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
//System.out.println("about to really instrument class " + className);
CtClass cc = null;
String javaClassName = className.replace('/', '.');
try {
cc = classPool.makeClass(new java.io.ByteArrayInputStream(classfileBuffer));
//System.out.println("did insert class in class pool");
//cc = classPool.get(className);
//System.out.println("got class from class pool ");
//Thread.sleep(1000);
CtMethod[] methods = cc.getMethods();
for (int k=0; k<methods.length; k++) {
if (methods[k].getLongName().startsWith(javaClassName)) {
//System.out.println("instrumenting method " + methods[k]);
instrumentMethod(methods[k]);
} else {
//System.out.println("no body; cannot instrument method " + methods[k].getLongName() + " (class name = " + javaClassName + ")");
}
}
// return the new bytecode array:
byte[] newClassfileBuffer = cc.toBytecode();
return newClassfileBuffer;
} catch (CannotCompileException e) {
System.err.println("cannot compile: " + e.getMessage() + " transforming class " + className + "; returning uninstrumented class");
} catch (IOException e) {
System.err.println("IO exception: " + e.getMessage() + " transforming class " + className + "; returning uninstrumented class");
} catch (Throwable e) {
e.printStackTrace();
System.err.println("Exception " + e.getClass() + ": " + e.getMessage() + " transforming class " + className + "; returning uninstrumented class");
}
return null;
}
}