Reputation: 31
I want to instrument getMessage() method of Throwable class and output some custom message to System.out or logger. I have written an agent called FirstAgent that is working fine as expected. Now my agent needs to sit along with and complement another vendor's agent. When I attach both agents (my agent and another vendor's agent) to JVM, my instrumented changes in getMessage() of Throwable class are lost. When attached in silo, these two agents are working fine.
I have recreated this problem using FirstAgent and SecondAgent (code attached). Based on the order of the agents, FirstAgent is loaded first and SecondAgent is loaded next. What I noticed is that when FirstAgent is loaded, it loads Throwable class, instruments, and exits. Next, When SecondAgent loads Throwable class, it is not loading the class definition with FirstAgent's instrumented changes. It is loading the original Throwable class (without FirstAgent's instrumented changes), instrumenting with its own changes, and exiting. In the process, Throwable class is losing FirstAgent's instrumented changes.
Am I doing something wrong? Any suggestions?
import java.io.File;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.jar.JarFile;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
public class FirstAgent implements ClassFileTransformer {
public static void premain(String agentArgs, Instrumentation inst) {
try {
String agentJarLocation = new File(
FirstAgent.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getPath();
inst.appendToBootstrapClassLoaderSearch(new JarFile(agentJarLocation));
} catch (Exception e2) {
e2.printStackTrace();
}
try {
Class<?> targetClazz = Class.forName("java.lang.Throwable");
FirstAgent fa = new FirstAgent();
try {
inst.addTransformer(fa, true);
inst.retransformClasses(targetClazz);
} catch (Exception ex) {
throw new RuntimeException("Failed to transform [" + targetClazz.getName() + "]", ex);
} finally {
inst.removeTransformer(fa);
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
try {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
CtMethod[] methods = ctClass.getDeclaredMethods();
for (CtMethod method : methods) {
if (method.getName().contains("getMessage") && Modifier.isPublic(method.getModifiers())) {
method.insertBefore("System.out.println(\"First Custome Message\");");
}
}
byteCode = ctClass.toBytecode();
ctClass.detach();
} catch (Throwable ex) {
ex.printStackTrace();
}
return byteCode;
}
}
import java.io.File;
import java.lang.instrument.ClassFileTransformer;
import java.lang.instrument.IllegalClassFormatException;
import java.lang.instrument.Instrumentation;
import java.security.ProtectionDomain;
import java.util.jar.JarFile;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.Modifier;
public class SecondAgent implements ClassFileTransformer {
public static void premain(String agentArgs, Instrumentation inst) {
try {
String agentJarLocation = new File(
SecondAgent.class.getProtectionDomain().getCodeSource().getLocation().toURI()).getPath();
inst.appendToBootstrapClassLoaderSearch(new JarFile(agentJarLocation));
} catch (Exception e2) {
e2.printStackTrace();
}
try {
Class<?> targetClazz = Class.forName("java.lang.Throwable");
SecondAgent sa = new SecondAgent();
try {
inst.addTransformer(sa, true);
inst.retransformClasses(targetClazz);
} catch (Exception ex) {
throw new RuntimeException("Failed to transform [" + targetClazz.getName() + "]", ex);
} finally {
inst.removeTransformer(sa);
}
} catch (Throwable ex) {
ex.printStackTrace();
}
}
@Override
public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined,
ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
byte[] byteCode = classfileBuffer;
try {
ClassPool classPool = ClassPool.getDefault();
CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
CtMethod[] methods = ctClass.getDeclaredMethods();
for (CtMethod method : methods) {
if (method.getName().contains("getMessage") && Modifier.isPublic(method.getModifiers())) {
method.insertBefore("System.out.println(\"Secocnd Custome Message\");");
}
}
byteCode = ctClass.toBytecode();
ctClass.detach();
} catch (Throwable ex) {
ex.printStackTrace();
}
return byteCode;
}
}
Upvotes: 1
Views: 90