该工具(在this repo中)包含3个类(在下面给出)。问题是如何使我的ParaTracer.Logger
类在每个I类仪器中都可见(如下面显示的java.util.Random
)。声明cp.importPackage("ParaTracer.Logger");
似乎并没有工作,我得到这个错误:使用Javassist记录方法调用和参数值,如何在每个检测类中使日志类可见?
java.lang.NoClassDefFoundError: ParaTracer/Logger at java.util.Random.nextLong(Random.java)
我试图动态加载每个仪表类内部的Logger
类。但似乎我错误地使用了Class.getMethod()
,或者Javassist编译器太原始,无法编译动态类加载代码。我得到这个错误:
javassist.CannotCompileException: [source error] getMethod(java.lang.String,java.lang.Class,java.lang.Class) not found in java.lang.Class
以下3类导出到一个JAR文件与MANIFEST.MF文件定义Premain-Class
和使用开关运行任何检测的程序时,传递给JVM:
-javaagent:/Path/To/ParaTracerAgent.jar
这里有3个类。
package ParaTracer;
import java.lang.instrument.Instrumentation;
import javassist.CannotCompileException;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtField;
import javassist.CtNewConstructor;
import javassist.CtNewMethod;
public class ParaTracer {
private static volatile Instrumentation instr;
public static void premain(String agentArgs, Instrumentation inst) {
instr = inst;
SimpleClassTransformer transformer = new SimpleClassTransformer();
inst.addTransformer(transformer, false);
}
}
变压器类:
package ParaTracer;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.security.ProtectionDomain;
import java.util.HashMap;
import java.util.HashSet;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
public class SimpleClassTransformer implements ClassFileTransformer {
public HashMap< String, HashSet<String> > mInstrumentedMethods;
public SimpleClassTransformer() {
mInstrumentedMethods = new HashMap< String, HashSet<String> >();
mInstrumentedMethods.put("java.util.Random", new HashSet<String>());
mInstrumentedMethods.get("java.util.Random").add("nextLong");
}
@Override
public byte[] transform(
ClassLoader loader,
String className,
Class<?> classBeingRedefined,
ProtectionDomain protectionDomain,
byte[] classfileBuffer) throws IllegalClassFormatException {
System.err.println("---- Instrumenting: " + className);
byte[] byteCode = classfileBuffer;
String normalizedClassName = className.replaceAll("/", ".");
if (mInstrumentedMethods.containsKey(normalizedClassName)) {
try {
ClassPool cp = ClassPool.getDefault();
cp.importPackage("ParaTracer.Logger");
CtClass cc = cp.get(normalizedClassName);
for(String method : mInstrumentedMethods.get(normalizedClassName)) {
CtMethod m = cc.getDeclaredMethod(method);
StringBuilder sbs = new StringBuilder();
sbs.append("long tid = Thread.currentThread().getId();");
sbs.append("StringBuilder sbArgs = new StringBuilder();");
sbs.append("sbArgs.append(System.identityHashCode($0));");
CtClass[] pTypes = m.getParameterTypes();
for(int i=0; i < pTypes.length; ++i) {
CtClass pType = pTypes[i];
if (pType.isPrimitive()) {
sbs.append("sbArgs.append(\", \" + $args[" + i + "]);");
} else {
sbs.append("sbArgs.append(\", \" + System.identityHashCode($args[" + i + "]));");
}
}
sbs.append("ParaTracer.Logger.pushArgs(tid, sbArgs.toString());");
sbs.append("StringBuilder sb = new StringBuilder();");
sbs.append("sb.append(tid + \" : " + m.getLongName() + ".<START>(\");");
sbs.append("sb.append(sbArgs.toString());");
sbs.append("sb.append(\")\");");
sbs.append("ParaTracer.Logger.print(sb.toString());");
m.insertBefore("{" + sbs.toString() + "}");
StringBuilder sbe = new StringBuilder();
sbe.append("long tid = Thread.currentThread().getId();");
sbe.append("String args = ParaTracer.Logger.popArgs(tid);");
sbe.append("StringBuilder sb = new StringBuilder();");
sbe.append("sb.append(tid + \" : " + m.getLongName() + ".<END>(\");");
sbe.append("sb.append(args);");
sbe.append("sb.append(\")\");");
sbe.append("ParaTracer.Logger.print(sb.toString());");
m.insertAfter("{" + sbe.toString() + "}");
}
byteCode = cc.toBytecode();
cc.detach();
} catch (Exception ex) {
ex.printStackTrace();
}
}
return byteCode;
}
}
的线程安全记录器类由下式给出:
package ParaTracer;
import java.io.FileWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.HashMap;
import java.util.Stack;
public class Logger {
private static String loggerFilePath = "\"/some/fixed/path\"";
private static FileWriter fw;
private static PrintWriter out;
private static HashMap< Long, Stack<String> > callStacks;
public static synchronized void pushArgs(long tid, String args) {
try {
init();
} catch (IOException e) {
e.printStackTrace();
}
if (! callStacks.containsKey(tid)) {
callStacks.put(tid, new Stack<String>());
}
callStacks.get(tid).push(args);
}
public static synchronized String popArgs(long tid) {
assert(callStacks.containsKey(tid));
assert(! callStacks.get(tid).empty());
return callStacks.get(tid).pop();
}
public static synchronized void shutdown() {
if (fw == null) return;
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
public static synchronized void print(String str) {
try {
init();
} catch (IOException e) {
e.printStackTrace();
}
out.print(str);
}
private static void init() throws IOException {
if (fw != null) return;
fw = new FileWriter(loggerFilePath);
out = new PrintWriter(fw);
callStacks = new HashMap< Long, Stack<String> >();
}
}
由于Javassist问题似乎没有太多兴趣,所以我完全删除了'ParaTracer.Logger'类,并使用'System.err'来记录方法调用,并使用了'System.setOut()/。 setErr()'将标准输出/错误重定向到一个文件。没有必要在方法返回时打印参数值。它现在有效。 – 2015-04-05 00:58:20