Я динамически подключаю свой агент Java к процессу java, который инструментирует код. Обычно он добавляет вызов stati c к каждому запуску метода:
//method start
AgentClass.staticMethod();
//method body
AgentClass
лежит в .jar
агента. Но после инструментирования процесс начинает выполнение нового кода и выдает NoClassDefFoundError
, он не может найти AgentClass
. Я попытался настроить классы таким образом, чтобы включить блок try-catch и загрузить AgentClass
с forName
следующим образом:
try {
AgentClass.staticMethod();
} catch(NoClassDefFoundError e) {
Class.forName("AgentClass");
}
Но потом у меня появилось несколько ошибок, связанных с пересчетом кадров стека. например: Caused by: java.lang.VerifyError: Inconsistent stackmap frames at branch target 20
Я решил это с помощью visitMaxs()
(я использую ASM
библиотеку). Затем я получил это: StackMapTable error: bad offset
. Это было решено с помощью GOTO вместо RETURN, но затем я получил: ClassFormatError: Illegal local variable table in method
.
Есть ли более простой способ решить мою первоначальную NoClassDefFoundError
ошибку?
UPDATE : Мои классы агентов загружаются с помощью Application Classloader (sun.misc.Launcher$AppClassLoader
), а процесс, который я хотел инструментировать, загружает классы с помощью настраиваемого URL-загрузчика классов.
UPDATE2: Это то, что Я хотел преобразовать в байт-код:
try {
AgentClass agent = AgentClass.staticMethod();
} catch (Throwable e) {
try {
Class.forName("AgentClass");
} catch (ClassNotFoundException ex) {
}
}
My MethodVisitor
(я не очень хорошо разбираюсь в байт-коде, поэтому байт-код был автоматически сгенерирован ASM с использованием TraceClassVisitor
.):
protected MethodVisitor createVisitor(MethodVisitor mv,final String name,final String desc,int access,String signature,String[]exceptions){
int variablesCount = (8 & access) != 0 ? 0 : 1;
Type[]args=Type.getArgumentTypes(desc);
for(int i=0;i<args.length; ++i){
Type arg=args[i];
variablesCount+=arg.getSize();
}
final int varCount=variablesCount;
return new MethodVisitor(458752,mv){
public void visitCode(){
Label label0=new Label();
Label label1=new Label();
Label label2=new Label();
this.mv.visitTryCatchBlock(label0,label1,label2,"java/lang/Throwable");
Label label3=new Label();
Label label4=new Label();
Label label5=new Label();
this.mv.visitTryCatchBlock(label3,label4,label5,"java/lang/ClassNotFoundException");
this.mv.visitLabel(label0);
this.mv.visitLineNumber(42,label0);
this.mv.visitMethodInsn(Opcodes.INVOKESTATIC,"AgentClass","staticMethod","()LAgentClass;",false);
this.mv.visitVarInsn(Opcodes.ASTORE,varCount);
this.mv.visitLabel(label1);
this.mv.visitLineNumber(48,label1);
Label label6=new Label();
this.mv.visitJumpInsn(Opcodes.GOTO,label6);
this.mv.visitLabel(label2);
this.mv.visitLineNumber(43,label2);
this.mv.visitFrame(Opcodes.F_SAME1,0,null,1,new Object[]{"java/lang/Throwable"});
this.mv.visitVarInsn(Opcodes.ASTORE,0);
this.mv.visitLabel(label3);
this.mv.visitLineNumber(45,label3);
this.mv.visitLdcInsn("AgentClass");
this.mv.visitMethodInsn(Opcodes.INVOKESTATIC,"java/lang/Class","forName","(Ljava/lang/String;)Ljava/lang/Class;",false);
this.mv.visitInsn(Opcodes.POP);
this.mv.visitLabel(label4);
this.mv.visitLineNumber(47,label4);
this.mv.visitJumpInsn(Opcodes.GOTO,label6);
this.mv.visitLabel(label5);
this.mv.visitLineNumber(46,label5);
this.mv.visitFrame(Opcodes.F_FULL,1,new Object[]{"java/lang/Throwable"},1,new Object[]{"java/lang/ClassNotFoundException"});
this.mv.visitVarInsn(Opcodes.ASTORE,1);
this.mv.visitLabel(label6);
this.mv.visitLineNumber(49,label6);
this.mv.visitFrame(Opcodes.F_CHOP,1,null,0,null);
this.mv.visitInsn(Opcodes.RETURN);
this.mv.visitLocalVariable("e","Ljava/lang/Throwable;",null,label3,label6,0);
this.mv.visitMaxs(1, 2);
super.visitCode();
}
...
}
}
ОБНОВЛЕНИЕ 3 Вот как я подключаю свой агент во время выполнения:
final VirtualMachine attachedVm = VirtualMachine.attach(String.valueOf(processID));
attachedVm.loadAgent(pathOfAgent, argStr);
attachedVm.detach();