setting the classpath for a newly injected class
Context: A new class say Bar, is injected into the JVM at run-time. This class belongs to a package say com.foo. A reference to this class is injected into another class that belongs to the same package. The new class may have a different name each time it is loaded - so this cannot be specified as p开发者_Python百科art of any config file - e.g. cannot be specified in build.xml to be included as part of a jar file.
Issue: At class load time, jvm throws an error - java Result 1. Although I cannot conclusively determine the root cause, it looks like the newly injected class is not being found by class loader. The server was run in verbose mode which shows the list of classes loaded by the JVM and this newly injected class is seen loaded.
Question: Is the newly injected class already in the classpath? If not how to set it?
[Edit] - adding some code to the question.
Code segment - 1: This code segment below is called from the PreMain method - Premain method will be called by JVM agent and will inject the instrumentation reference at run time. Premain method injects 1 new class - Bar - and 1 reference to this new class from a method - returnsABool() - in an existing class - ExistingClass.
public static void premain(String agentArgs, Instrumentation inst) {
// 1. Create and load the new class - Bar
String className = "Bar";
byte [] b = getBytesForNewClass();
//override classDefine (as it is protected) and define the class.
Class clazz = null;
try {
ClassLoader loader = ClassLoader.getSystemClassLoader();
Class cls = Class.forName("java.lang.ClassLoader");
java.lang.reflect.Method method =
cls.getDeclaredMethod("defineClass", new Class[] { String.class, byte[].class, int.class, int.class });
// protected method invocation
method.setAccessible(true);
try {
Object[] args = new Object[] { className, b, new Integer(0), new Integer(b.length)};
clazz = (Class) method.invoke(loader, args);
} finally {
method.setAccessible(false);
}
} catch (Exception e) {
System.err.println(
"AllocationInstrumenter was unable to create new class" + e.getMessage());
e.printStackTrace();
}
// 2. Inject some lines of code into the returnsABool method in ExistingClass class that references Bar
inst.addTransformer(new CustomInstrumenter(), true);
// end of premain method
}
Code sement 2: The method returnsABool() needs to be byte-injected with the commented lines shown below. The code to byte inject this is also called from the PreMain method.
public class ExistingClass{
public static boolean returnsABool() {
// Code within comments is byte-injected, again as part of the pre-main method
/*
String str = Bar.get();
if (str != "someValue") {
return true;
}
*/
return false;
}
}
Byte code injection for ExistingClass - done using asm library
{
MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");
mv.visitLdcInsn("some constant here");
Label l1 = new Label();
mv.visitJumpInsn(Opcodes.IF_ACMPNE, l1);
mv.visitInsn(Opcodes.ICONST_0); Label l2 = new Label();
mv.visitJumpInsn(Opcodes.GOTO, l2);
mv.visitLabel(l1);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(Opcodes.ICONST_1);
mv.visitFrame(Opcodes.F_SAME1, 0, null, 1, new Object[] {Opcodes.INTEGER});
mv.visitInsn(Opcodes.IRETURN);
mv.visitMaxs(2, 0);
mv.visitEnd();
}
I would suspect you have something wrong with your bytecode generation, the following ASM code works for me:
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitMethodInsn(Opcodes.INVOKESTATIC, "com/Bar", "get", "()Ljava/lang/String;");
Label l1 = new Label();
mv.visitLdcInsn("some constant here");
mv.visitJumpInsn(Opcodes.IF_ACMPEQ, l1);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(Opcodes.ICONST_1);
mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER});
mv.visitInsn(Opcodes.IRETURN);
mv.visitLabel(l1);
mv.visitFrame(Opcodes.F_SAME, 0, null, 0, null);
mv.visitInsn(Opcodes.ICONST_0);
mv.visitFrame(Opcodes.F_SAME, 0, null, 1, new Object[] {Opcodes.INTEGER});
mv.visitInsn(Opcodes.IRETURN);
mv.visitMaxs(2, 1);
mv.visitEnd();
Also note that:
- the way you are comparing Strings will most likely lead to issues, you should use
str.equals(str2)
- you are replacing the entire method, instead of injecting your custom code in the beginning (your comments seem to indicate that you want to inject, instead of replace)
精彩评论