How to debug JIT compiled methods in OpenJDK JVM?

In the previous article we explored a couple of strategies to debug the OpenJDK JVM bytecodes interpreter. I will now present a procedure to debug JIT compiled code.

There are some JVM arguments useful to get information about JIT compiled methods:

Note: +PrintAssembly argument can be used to disassemble compiled code but requires a plugin library called hsdis in some JDK releases such as 8. In hotspot/src/share/tools/hsdis directory you will find the plugin code. To build it, open a command line and run:

Once built, copy build/linux-amd64/hsdis-amd64.so library to jre/lib/amd64 and jre/lib/amd64/server directories.

Let’s see an example.

Java code:

Main::main JIT method information:

Main::main JIT method code:

You see there how the Main::exitCode field value is loaded into a register, and a call to a static method -System::exit, presumably- is made.

The JVM allows transitions from interpreted bytecodes to JIT compiled methods and viceversa. For this to be accomplished, methods have adapters which arrange the call frame according to the source and destination ABIs.

Once an adapter to JIT compiled code is executed, the native method starts. If we look at Main::main information above, compiled entry has the starting address. Our goal is to set a breakpoint there.

Contrary to the JVM interpreter -which is generated at the beginning-, JIT methods may be generated at any time. Even more: they can be re-compiled multiple times and by different compilers. As a result, we need to stop every time the method is generated and before its execution.

CompileBroker::invoke_compiler_on_method(CompileTask*) is an interesting point in that regard. This is where methods get compiled either by C1, C2 or any other JIT compiler. In particular, right after the task->code() call, the compiled method is ready:

If we look at the assembly in x86_64, here is the exact point:

We will then set our breakpoint right after the call to CompileTask::code. Note: if rebuilding the JVM is possible, we can add a filter to only break in the method of our interest:

We now have all the information needed about the compiled method:

Method name

 

Compiler level (C1, C2)

 

Method blob size

 

Method’s first instruction

Note: blob size can be used as an overestimate for the number of instructions in the method -in the worst case scenario, each instruction would take 1 byte-.

It’s now possible to set a breakpoint at the beginning of the method:

 

Bonus

Dump the JIT compiled method to a file:

2 Replies to “How to debug JIT compiled methods in OpenJDK JVM?”

  1. _nm->_entry_point doesn’t exist upstream anymore. There’s _nm->_code_begin, but I’m not clear it’s equivalent

Leave a Reply

Your email address will not be published.