Java - Inject java agent in to running jvm

11,084

Solution 1

Dynamic agents need to declare an agentmain(String, Instrumentation) method which is executed upon attachment within the target VM. You can use the tools.jar dependency which is (until Java 9) only included in a JDK but not a JRE. You can however bundle your agent program with a JDK and attach to JVMs from there.

The biggest pitfall is that the API differs for different VMs; you can however use a library like byte-buddy-agent which contains different implementations for different VMs. An attachment can be done using:

ByteBuddyAgent.attach("my.jar", "my-pid");

This attaches the agent contained in my.jar onto the Java process with id my-id.

Solution 2

Agents can be injected with HotSpot Attach API.
Run the following snippet with $JAVA_HOME/lib/tools.jar on the class path.

    VirtualMachine vm = VirtualMachine.attach(PID);
    try {
        vm.loadAgent(agentJar);
    } finally {
        vm.detach();
    }

Alternatively you may do this with my command-line jattach utility:

$ jattach PID load instrument false /path/to/agent.jar

Note that in order to support dynamic attach, your Java agent should have agentmain method and Agent-Class property in MANIFEST.MF.

Solution 3

As far as I understand from the comment, you are interested in something that can inspect remote JVM from within another Java process. If it is the case, then you need a Serviceability Agent rather than Java Agent.

Serviceability Agent API allows you to attach to another JVM process, to read its memory, to reconstruct VM structures and to inspect remote objects in reflection-like manner.

Here is a sample tool to list all classes loaded by a remote JVM:

import sun.jvm.hotspot.runtime.VM;
import sun.jvm.hotspot.tools.Tool;

public class ListRemoteClasses extends Tool {

    public static void main(String[] args) {
        new ListRemoteClasses().execute(args);
    }

    @Override
    public void run() {
        VM.getVM().getSystemDictionary().classesDo(klass -> {
            String className = klass.getName().asString().replace('/', '.');
            System.out.println(className);
        });
    }
}

How to run it:

java -cp $JAVA_HOME/lib/sa-jdi.jar:. ListRemoteClasses PID
Share:
11,084
Stoud
Author by

Stoud

Updated on July 29, 2022

Comments

  • Stoud
    Stoud almost 2 years

    Basically, I am trying to write something that lists every class loaded by the JVM. What I wrote works, but it only works for the jvm it is running on. I crafted a java agent to dynamically inject into another JVM, but then realized I don't actually know how to inject it. How do I actually send this agent into another JVM? Is it possible?

  • Rafael Winterhalter
    Rafael Winterhalter over 7 years
    Yes. The OS process id.
  • Stoud
    Stoud over 7 years
    I attempted to do this, and under testing it appears my agentmain method is never being called.
  • Stoud
    Stoud over 7 years
    On further inspection, the agentmain method is only being called if I attach to the JVM it is running on.
  • Rafael Winterhalter
    Rafael Winterhalter over 7 years
    It is called by the target vm.
  • Stoud
    Stoud over 7 years
    Basically what I am trying to do is get an instance of the Instrumentation. If the target vm calls agentmain, does that make getting an instance of a different JVM's instrumentation impossible?
  • Stoud
    Stoud over 7 years
    I am aware of this. The problem is that the jar is already running and I need to get a javaagent to connect with it.
  • Rafael Winterhalter
    Rafael Winterhalter over 7 years
    That is impossible! You could however proxy the interface and run some form of receiver on the other VM that executes the commands.
  • Rafael Winterhalter
    Rafael Winterhalter almost 7 years
    Did you use a JDK?
  • ha9u63ar
    ha9u63ar over 6 years
    I have a small problem with the agents. I am assuming that it's the most standard practice to pack your instrumentation agent separately from your target JVM. When my agentmain() is called I tried to find a JVM class by calling Class.forName("pkg.name") but it is always returning NoClassDefFoundError. I have posted the question here (stackoverflow.com/questions/46523055/…. I am slightly confused why the agent, even when attached to the VM, cannot refer to its classes.
  • ha9u63ar
    ha9u63ar over 6 years
    @RafaelWinterhalter does this handle webapp classloading delegation? When you go inside the agent, classloading is not the same as your webapplication classloading....So you have a delegation mechanism working in there?
  • xiemeilong
    xiemeilong about 6 years
    @RafaelWinterhalter How can I create 'File' of my agent jar?
  • Rafael Winterhalter
    Rafael Winterhalter about 6 years
    You have to create a jar, just like for any Java program.
  • deFreitas
    deFreitas almost 4 years
    Alternativelly you can use jvm-attach which is a wrapper for jattach to make instrumentation programatically, no tools.jar or JDK needed