WonderMouse
WonderMouse

Reputation: 45

Java Agent Class Loader

I have a problem with how to instruct class loader to use Java Agent jar as a reference for classes. I am using VirtualMachine to attach to specified process with Java Agent, and with most of the cases it works. But in the case of attaching the complex app(e.g. Wildfly) which uses own class loaders, class loader cannot find classes.

This is a short version of java agent:

package mypackage;
public class JavaInstrumentAgent 
{
    public JavaInstrumentAgent()
    {
    }

    public static void premain(String args, Instrumentation instr) {
        instrument(args, instr);
    }

    public static void agentmain(String args, Instrumentation instr) {
        instrument(args, instr);
    }

    private static void instrument(String args, Instrumentation instr) {
        instr.addTransformer(new JavaInstrumentTransformer(), true);
    }
}

a transformer:

package mypackage;
public class JavaInstrumentTransformer implements ClassFileTransformer
{
    public JavaInstrumentTransformer()
    {
        CtClass.debugDump = "F:\\tmp\\ct";
    }

    public byte[] transform(ClassLoader loader, String className, Class<?> classBeingRedefined, ProtectionDomain protectionDomain, byte[] classfileBuffer) throws IllegalClassFormatException {
        byte[] byteCode = null; 
        String cn = className.replaceAll("/", "\\.");
        try {
            ClassPool classPool = ClassPool.getDefault();
            classPool.importPackage("mypackage.AgentManager");
            classPool.insertClassPath(new LoaderClassPath(loader));
            CtClass ctClass = classPool.makeClass(new ByteArrayInputStream(classfileBuffer));
            CtMethod[] methods = ctClass.getDeclaredMethods();
            for (CtMethod method : methods) {
                if (Modifier.isNative( method.getModifiers())) continue ;
                    String params = "\"" + cn + "\",\"" + method.getName() + "\"";
                    method.insertBefore("mypackage.AgentManager.start(" + params + ");");
                
            }
            byteCode = ctClass.toBytecode();
            ctClass.detach();
        } catch (Exception ex) {
            System.err.println(ex.getMessage());
        }
        return byteCode;
    }
}

and a call collector:

package mypackage;
public class AgentManager 
{
    private AgentManager() {
    }

    public static void start(String clazz, String methodName)
    {
        System.out.println(clazz + ":" + methodName);
    }
}

If I attach to a simple application, like this:

public class Test()
{
    public void test()
    {
    }

    public static void main(String[] args)
    {
        new Test().test();
    }
}

I can see in Javassist dump, that transformation is done well and looks like:

import mypackage.AgentManager ;

public class Test()
{
    public void test()
    {
        mypackage.AgentManager.start();
    }

    public static void main(String[] args)
    {
        new Test().test();
    }
}

and I can see runing test method, but that's because a transformer uses default class loader, which loaded also mypacket.AgentManager class. If I try to attach to a Wildfly process to monitor my web services on it, I got transformer compile error:

(Test) Exception in thread "Test" java.lang.NoClassDefFoundError: mypackage/AgentManager

I tried:

classPool.appendClassPath("MyAgent.jar");
intrumentation.appendToSystemClassLoaderSearch("MyAgent.jar");
instrumentation.appendToBootstrapClassLoaderSearch("MyAgent.jar");

but nothing changes. Any help/hint how I could notify attached process class loader to look also in MyAgent.jar would be appreciated.

Upvotes: 0

Views: 283

Answers (0)

Related Questions