Imagine a bacon-wrapped Ferrari. Still not better than our free technical reports.
See all our reports

How-to guide to writing a javaagent

Imagine a situation where you suddenly need to obtain the bytecode of all the loaded classes in a the Java process. For example let’s say you want to debug some sort of instrumentation that happens at runtime and you can’t locate the .class file and inspect that. You need to dig deeper. By the way, if you’re just getting acquainted with the bytecode, here’s a helpful report to get you started: Mastering Java Bytecode.

Alternatively, let’s say you’re doing something along the lines of JITWatch and want to link together the bytecode you produce with the machine code the JIT generates. In any case, you have a problem which involves classes and as we know all too well, problems with classes and classloaders are gonna get interesting and you’re gonna need coffee. Lots of coffee+++!

In this post we’ll look at two approaches to obtaining the bytecode of classes loaded into the JVM and learn a thing or two about javaagents and the HotSpot Debugger, a hidden gem of the JDK.

So let’s outline the problem that we’re tackling in this post.

Problem Description: Obtain the bytecode of all classes loaded into the JVM.

Let’s get started!

Do it yourself with a javaagent

In general, a javaagent is a JVM “plugin”, a specially crafted .jar file, that utilizes the Instrumentation API that the JVM provides. The Instrumentation API was made available in Java 1.5, so for all intents and purposes, the solution we’re going to explore now is quite portable.

To create a successful javaagent we’ll need four things: an agent class, some meta-information to tell the JVM what capabilities to give to our agent class, a way to make the JVM load the .jar with the agent before it starts minding the application’s business and a coffee. Got the coffee already? OK, three things then.

Overview of Javaagents

Above is a slide from my talk: Taming Java Agents, which I most recently presented to the Barcelona JUG meeting a couple of weeks ago.

I have to be honest here, originally, the slide was created by Java Champion, JavaOne Rockstar Speaker, and XRebel product lead Anton Arhipov, so all credit should go to him. However *looks around* he’s not here right now so I’ll claim it as mine!

So without further ado, let’s create a Java class that will be our agent class. The important thing to remember here is that our VM will try to locate our class that we specify in our -javaagent parameter to the VM, and execute its premain method before the main method.

import java.lang.instrument.Instrumentation;

public class Agent {
  public static void premain(String args, Instrumentation instrumentation){
    ClassLogger transformer = new ClassLogger();
    instrumentation.addTransformer(transformer);
  }
}

Notice the Instrumentation parameter that we have access to in the premain method. It is a very powerful API that among other things allows us to register ClassFileTransformers. A registered ClassFileTransformer will intercept the loading of all application classes and will have access to their bytecode.

Incidentally, ClassFileTransformer can also transform the bytecode of the application classes and make the JVM load behave completely differently to the originally intended bytes.

That’s how almost all cool JVM tools work, including JRebel and XRebel, they intrument your classes and inject their functionality right into your app without requiring additional dependencies or even changing your code. By the way, while we’re on the subject, look into those two tools, they are truly fascinating and if you are a Java developer and don’t know about JRebel or XRebel yet, you’ll love them!

However, we digress, let’s get back to logging the bytecode! So, we have now registered a ClassLogger transformer, let’s look how it is implemented.

public class ClassLogger implements ClassFileTransformer {
  @Override
  public byte[] transform(ClassLoader loader,
                          String className,
                          Class<?> classBeingRedefined,
                          ProtectionDomain protectionDomain,
                          byte[] classfileBuffer) throws IllegalClassFormatException {
    try {
      Path path = Paths.get("classes/" + className + ".class");
      Files.write(path, classfileBuffer); 
    } catch (Throwable ignored) { // ignored, don’t do this at home kids
    } finally { return classfileBuffer; }
  }
}

As you can see the code is extremely trivial here. The transform method has access to the application class name and the bytes that correspond to the body of the class. In our case we just dump the bytes into a file.

Sadly, that’s it, the coding time is over, the last part that needs to be done is packaging a jar file and supplying a manifest file that will specify our agent as the Premain-Class.

Here’s a Gradle build file part that would do a trick:

jar {
    archiveName = "${rootProject.name}-${rootProject.version}.jar"
    manifest {
        attributes(
                'Premain-Class': 'Agent',
                'Can-Redefine-Classes': 'true',
                'Can-Retransform-Classes': 'true',
                'Can-Set-Native-Method-Prefix': 'true',
                'Implementation-Title': "ClassLogger",
                'Implementation-Version': rootProject.version
        )
    }
}

All the bits and pieces are ready for you to build your javaagent jar now and all you have to do to enjoy the power of intercepting classloading to log the bytes of the classes is to supply the -javaagent parameter. It’s as simple as this:

java -jar myapp.jar
java -javaagent:/path/to/agent.jar -jar myapp.jar

And when the myapp.jar is done running, under the classes directory you’ll find a bunch of .class files to inspect. I don’t have the source code for the agent at hand, but in this convenient Github repository you can find a complete javaagent project that is a bit more complicated, but follows the same principles. Check it out, it’s pretty cool.

Do it like a pro with a HSDB

Earlier, we looked at how to create a simple javaagent that would intercept the classloading and write the bytes of all loaded classes into files on our file system. That would be my default way of solving the problem of obtaining the bytecode for all loaded classes. In fact javaagents are particularly powerful, in fact the defacto way to tackle many similar problems as well.

But now I want to share another way of checking the bytecode of classes and do other cool things with a tool called HSDB (HotSpot Debugger). I’ve learned about it at the recent SpringIO conference in Barcelona, thanks to Thomas and am excited to share it with you.

HSDB resides in a sa-jdi.jar that you’ll find under the lib directory of your local HotSpot JDK distribution.

To unleash the power of HSDB onto the Java process of your choosing, you’ll have to run it with privileged access and feed it the PID of the Java process that you want to investigate:

shelajev@shrimp  /Library/Java/JavaVirtualMachines/jdk1.8.0_40.jdk/Contents/Home
$ sudo java -cp lib/sa-jdi.jar sun.jvm.hotspot.HSDB

From the main menu, select File => Attach to HotSpot process … and enter the PID of your Java process. By the way, one of the best ways to obtain the PID of a Java process is by using jps, another built-in JDK tool.

Straight after connecting, we can see a list of Threads in the target Java process (I picked Eclipse as my Java process).

HotSpot Debugger Start View

And that is a great thing to look into, if you have some free time, but something more relevant to our problem of getting the class files view would be available through Tools => Class browser.

HotSpot Debugger Class Viewer

You can see the Create .class for all classes link right at the top of this new window. Clicking that will generate all the loaded application class files and we’ve solved the same problem much faster, though maybe in a less satisfying geek way than our javaagent route.

The HotSpot Debugger functionality is of course not limited to generating class files. It’s an incredibly powerful tool, but the rest is a story for another time. At the beginning of this post, we set ourselves on the path of obtaining the class files and we achieved just that. You’re welcome bytecode loving friends!

Conclusion

In this post we’ve looked at two super-mega-powerful entries in the Java developer toolbelt: the javaagent and the HotSpot Debugger. Both of them are very versatile and with the power to access to classes loaded into the JVM, we have only just scratched the surface of what can achieved with them, but hopefully after reading this post you’ll now know of their existence and can investigate things further. Let me know how you got on!

What would be your way of solving the problem at hand? Tell me in the comment section below or find me on Twitter: @shelajev.


Read next: