下面是在利用JDK的Instrument来编写调试工具的时候出现的一些问题总结
1、java.io.Console 类的读取操作将会阻塞掉写入操作,造成写入操作不能异步进行。
原因是该类中加入了读写锁。代码如下:
public String readLine(String fmt, Object ... args) {
String line = null;
synchronized (writeLock) {
synchronized(readLock) {
if (fmt.length() != 0)
pw.format(fmt, args);
try {
char[] ca = readline(false);
if (ca != null)
line = new String(ca);
} catch (IOException x) {
throw new IOError(x);
}
}
}
return line;
}
2、Class.getSimpleName 方法在scala 下有可能抛出异常
例如:
Exception in thread "agent thread" java.lang.InternalError: Malformed class name
at java.lang.Class.getSimpleName(Class.java:1133)
at cn.zhxing.trace.agent.instrument.ClassFilter.match(ClassFilter.java:41)
at cn.zhxing.trace.util.InstrumentUtil.findMatchClassAndMethods(InstrumentUtil.java:111)
at cn.zhxing.trace.agent.command.LoaderCommand.run(LoaderCommand.java:40)
at cn.zhxing.trace.agent.Client.listen(Client.java:43)
at cn.zhxing.trace.agent.Main$1.run(Main.java:62)
at java.lang.Thread.run(Thread.java:662)
经过查询发现该类为:scala.collection.SeqLike$$anonfun$occCounts$1,这类有个特征是连续有两个$符号,正是这个情况导致。网上也有类似的错误:http://www.scala-lang.org/node/7691
3、asm.jar使用中有可能出现ClassNotFoundException 的异常(在使用instrument的时候容易出现)
例如下面的方法,调用accept的时候抛出
ClassReader reader = new ClassReader(classfileBuffer);
ClassWriter writer = new ClassWriter(reader, ClassWriter.COMPUTE_FRAMES);
reader.accept(vistor, ClassReader.SKIP_FRAMES);//error
异常堆栈部分如下:
TraceTransformer:reader.acceptjava.lang.RuntimeException: java.lang.ClassNotFoundException: exceptions/ServiceException
at org.objectweb.asm.ClassWriter.getCommonSuperClass(Unknown Source)
at org.objectweb.asm.ClassWriter.a(Unknown Source)
at org.objectweb.asm.Frame.a(Unknown Source)
at org.objectweb.asm.Frame.a(Unknown Source)
at org.objectweb.asm.MethodWriter.visitMaxs(Unknown Source)
at org.objectweb.asm.commons.LocalVariablesSorter.visitMaxs(Unknown Source)
at cn.zhxing.trace.agent.instrument.MethodInstrument.visitMaxs(MethodInstrument.java:137)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
at org.objectweb.asm.ClassReader.accept(Unknown Source)
仔细分析了asm的代码发现,代码如下:
protected String getCommonSuperClass(final String type1, final String type2)
{
Class> c, d;
ClassLoader classLoader = getClass().getClassLoader();
try {
//这里可以看出classloader是直接在getClass().getClassLoader();中获取的本地classloader,如果该class不在该classloader中时就会出现异常
c = Class.forName(type1.replace('/', '.'), false, classLoader);
d = Class.forName(type2.replace('/', '.'), false, classLoader);
} catch (Exception e) {
throw new RuntimeException(e.toString());
}
if (c.isAssignableFrom(d)) {
return type1;
}
if (d.isAssignableFrom(c)) {
return type2;
}
if (c.isInterface() || d.isInterface()) {
return "java/lang/Object";
} else {
do {
c = c.getSuperclass();
} while (!c.isAssignableFrom(d));
return c.getName().replace('.', '/');
}
}
修改如下:
新建一个新的Class 继承ClassWriter,重写getCommonSuperClass 方法,如下:
public class TraceClassWriter extends ClassWriter {
//省略其他代码
public TraceClassWriter(ClassReader classReader, int flags, ClassLoader loader) {
super(classReader, flags);
this.loader = loader;
}
protected String getCommonSuperClass(final String type1, final String type2) {
Class c, d;
try {
c = Class.forName(type1.replace('/', '.'), true, loader);
d = Class.forName(type2.replace('/', '.'), true, loader);
} catch (Exception e) {
logger.error(e, "type1=%s,type2=%s,loader=%s", type1, type2, loader);
throw new RuntimeException(e.toString());
}
if (c.isAssignableFrom(d)) {
return type1;
}
if (d.isAssignableFrom(c)) {
return type2;
}
if (c.isInterface() || d.isInterface()) {
return "java/lang/Object";
} else {
do {
c = c.getSuperclass();
} while (!c.isAssignableFrom(d));
return c.getName().replace('.', '/');
}
}
}
使用的时候是这样:
ClassReader reader = new ClassReader(classfileBuffer);
//由外部传入classloader
ClassWriter writer = new TraceClassWriter(reader, ClassWriter.COMPUTE_FRAMES,loader);
reader.accept(vistor, ClassReader.SKIP_FRAMES);
类似错误也可看:http://www.avaje.org/topic-180.html