Search code examples
javainstrumentationagent

Starting Instrumentation Agent after VM Startup


I was hoping for someone to explain this item since I might be getting this wrong:

I was reading about Java Agent Instrumentation which says that the agent can start after VM startup. So if I want to dynamically replace some class (without brining down the app) is this what I am going to go for using agent-main? Or do I need to do something more here?

I know people might ask "Are you talking about JRebel" - not really because I want to do something simple and JRebel is an overkill.

instrument docs - Java docs for Instrumentation

I understand all the instrumentation overrides, but I am slightly confused how I can hook this agent with -agent argument after the app has started.


Solution

  • First your agent class needs to specify an agentmain method like:

    public class MyAgent {
        public static void agentmain(final String args, final Instrumentation inst) {
            try {
                System.out.println("Agent loaded.");
            } catch (Exception e) {
                // Catch and handle every exception as they would
                // otherwise be ignored in an agentmain method
                e.printStackTrace();
            }
        }
    }
    

    Compile it and pack it inside a jar-file for example. If you choose the jar-variant then it must specify the Agent-Class key in its manifest-file (MANIFEST.MF). It points to the class implementing the agentmain method. It could look like:

    Manifest-Version: 1.0
    Agent-Class: package1.package2.MyAgent
    

    If it is located inside those packages, as an example.


    After that you can load the agent via the VirtualMachine#loadAgent method (documentation). Note that the mechanism used by those classes are part of the Attach library of Java. They decided, as most users don't need it, to not directly add it to the systems path but you can just add it. It is located at

    pathToYourJDKInstallation\jre\bin\attach.dll
    

    And it needs to be somewhere where the system property java.library.path is pointing at. You could for example just copy it to your .../Windows/System32 folder or adjust the property or stuff like that.


    As an example, if you want to inject an agent-jar inside another currently running jar, you could use a method like this:

    public void injectJarIntoJar(final String processIdOfTargetJar,
            final String pathToAgentJar, final String[] argumentsToPass) {
        try {
            final VirtualMachine vm = VirtualMachine.attach(processIdOfTargetJar);
            vm.loadAgent(pathToAgentJar, argumentsToPass.toString());
            vm.detach();
        } catch (AttachNotSupportedException | AgentLoadException
                | AgentInitializationException | IOException e) {
            System.err.println("Unable to inject jar into target jar.");
        }
    }
    

    With the same technique you can inject dll-libraries (if they implement the corresponding agent-methods via the native agent interface) into jars.


    Actually, if that helps you, I have written some small library for that kind of stuff some time ago. See Mem-Eater-Bug, the corresponding class is Injector.java and the whole project has a small Wiki.

    It has an example showing how to use that technique to manipulate a SpaceInvaders game written as Java application.