Dissecting the Java Virtual Machine - Debugging Hotspot

Debugging Hotspot

This section only scratches the surface by providing insights on how to debug the Hotspot codebase. Here are several techniques:

   - using JVM flags to dump debugging information;
   - using various existing tools (or writing your own depending on what you want to debug) - the existing tools are in the <OpenJDK_root>/hotspot/src/share/tools directory;
   - debugging a sample Java application (with jdb) that encompasses the JVM feature you want to debug - inspecting the application behaviour will allow to debug the targeted JVM feature itself;
   - using the JVMTI and other seviceability utilities - for inspecting JVM behaviour;
   - building a debug version of the JVM that will unlock additional JVM flags (see "HotSpot Internals: Explore and Debug the VM at the OS Level" in the references);
   - dumping debug information on the standard output - of course, dummiest but iron-proof method :- ))).

Using JVM flags

   To build a debug version of the JVM you may use a separate Hotspot target (the latest JDK image must be already build unless you use an official build from Oracle - it is used for bootstrapping the debug build). This is is an example using 64 bit Windows (with 64 bit Cygwin):

ALT_BOOTDIR=/cygdrive/d/projects/OpenJDK/dev/jdk8_tl/build/windows-x86_64-normal-server-release/jdk LP64=1 STRIP_POLICY=no_strip make debug

   You should make sure that the proper Visual Studio variables are also provided on the PATH if you are building under Windows. For example (change paths accordingly on your Windows system):

export PATH="/cygdrive/d/software/Microsoft Visual Studio 10.0/VC/bin/amd64":$PATH

   Also make sure that the LIB and INCLUDE varibles are set properly (look into the vcvars.exe/vcvars64.exe from the Visual Studio directories on how to set them properly). For example (change paths accordingly on your Windows system):

export LIB=D:\software\Microsoft Visual Studio 10.0\VC\LIB\amd64;D:\software\Microsoft Visual Studio 10.0\VC\ATLMFC\LIB\amd64;C:\Program Files (x86)\Microsoft SDKs\Windows \v7.0A\lib\x64;C:\Program Files\SQLXML 4.0\bin;
export INCLUDE=D:\software\Microsoft Visual Studio 10.0\VC\INCLUDE;D:\software\Microsoft Visual Studio 10.0\VC\ATLMFC\INCLUDE;C:\Program Files (x86)\Microsoft SDKs\Windows\v7.0A\include;

   Finally issue the following to copy the debug JVM (provided as library) on top of the existing Hotspot JVM library (again change paths accordingly - for Linux the JVM library is in the libjvm.so file):

cd <OpenJDK_root>
cp hotspot/build/windows/windows_amd64_compiler2/debug/jvm.dll build/windows-x86_64-normal-server-release/jdk/bin/server/jvm.dll

   To verify that the debug build is working check the version of the JVM using  the Java launcher:

cd build/windows-x86_64-normal-server-release/jdk/bin/./java.exe -version

   You should see output similar to the following:

openjdk version "1.8.0-internal"
OpenJDK Runtime Environment (build 1.8.0-internal-martin_2013_12_23_16_13-b00)
OpenJDK 64-Bit Server VM (build 25.0-b63-internal-debug, mixed mode)

   You can now see all available debug options by issuing:

./java.exe -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -Xprintflags

   or

./java.exe -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsWithComments

   The -XX:+UnlockDiagnosticVMOptions option enables VM diagnostic options.
Note: you may need some time to understand some of the options based on your particular use case.

   Examples (some of the options work only when -XX:+UnlockDiagnosticVMOptions is specified):

./java.exe -XX:+CountBytecodes <program> - prints the number of bytecodes executed by the JVM
./java.exe -XX:+PrintBytecodeHistogram <program> - prints statistics on the number of bytecode instructions executed for each type of instruction
./java.exe -XX:+LogCompilation <program> - can emit a structured XML log of compilation-related activity during a run of the JVM
./java.exe -XX:LogFile=/<path_to_log>/ <program> - prints logging information to a log file
./java.exe -XX:+TraceClassLoading <program> - print identities of loaded classes
./java.exe -XX:+TraceClassUnloading <program> - print identities of unloaded classes
./java.exe -XX:+PrintGCDetails <program> - tracks size of the perm gen
./java.exe -XX:+UseSerialGC <program> - Serial GC (Serial-young Serial-old)
./java.exe -XX:+UseParallelGC <program> - Parallel GC (Parallel-young Serial-old)
./java.exe -XX:+UseParallelOldGC <program> - Parallel Compacting (Parallel-young Parallel-old)
./java.exe -XX:+UseConcMarkSweepGC <program> - Concurrent Mark Sweep GC (Parallel-old CMS-old)
./java.exe -XX:+PrintAssembly <program> - print assembly code for bytecoded and native methods
./java.exe -XX:+PrintOptoAssembly <program> - (C2 only)
./java.exe -XX:+PrintNMethods <program> - print nmethods as they are generated
./java.exe -XX:+PrintNativeNMethods <program> - print native method wrappers as they are generated
./java.exe -XX:+PrintSignatureHandlers <program> - print native method signature handlers
./java.exe -XX:+PrintAdapterHandlers <program> - print adapters (i2c, c2i) as they are generated
./java.exe -XX:+PrintStubCode <program> print stubs: deopt, uncommon trap, exception, safepoint, runtime support
./java.exe -XX:+PrintCompilation <program> - lets you know if any methods are compiled by printing information about compiled methods
./java.exe -XX:+PrintInlining <program> - prints information about inlining decisions
./java.exe -XX:CompileCommand=... - controls compilation policy

   You can print assembly code for each bytecode instruction generated by the template interpreter (decribed earlier in the article) by using the -XX:+PrintInterpreter option - however you will need to install a disassembler plug-in for the JVM (see the article on the PrintAssembly option from the
"Hotspot Internals" wiki - see references).

   For more JVM flags and command line arguments you can look into the following files:

  • <OpenJDK_root>/hotspot/src/share/vm/runtime/globals.hpp - global options;
  • <OpenJDK_root>/hotspot/src/share/vm/gc_implementation/g1/g1_globals.hpp - global options specific to the G1 (garbage-first) server garbage collector;
  • <OpenJDK_root>/hotspot/src/share/vm/runtime/arguments.hpp - global arguments.

Using Existing Tools

IdealGraphVisualizer tool

   The "ideal graph" visualizer is a tool developed to help examine the IR (intermediate representation) from the C2 JIT compiler (refered as "ideal graph"). The tool is located under the <OpenJDK_root>/hotspot/src/share/tools/IdealGraphVisualizer directory.

   The JVM support is controlled by the flag -XX:PrintIdealGraphLevel=#
where # is:

   0: no output, the default
   1: dumps graph after parsing, before matching, and final code.
also dumps graph for failed compiles, if available
   2: more detail, including after loop opts
   3: even more detail
   4: prints graph after parsing every bytecode (very slow)

   By default the JVM expects that it will connect to a visualizer on the local host on port 4444. This can be configured using the options -XX:PrintIdealGraphAddress= and -XX:PrintIdealGraphPort=. PrintIdealGraphAddress can actually be a hostname.

   Alternatively the output can be sent to a file using -XX:PrintIdealGraphFile=<filename>. Each compiler thread will get it's own file with unique names being generated by adding a number onto the provided file name.

LogCompilation tool

   The log compilation tool can be used to parse the output of the -XX:+LogCompilation command switch that is used to log the output from the JIT compilation (and it is not very readable). It is located under the  <OpenJDK_root>/hotspot/src/share/tool/LogCompilation directory.

   It's main purpose is to recreate output similar to -XX:+PrintCompilation -XX:+PrintInlining output from a debug JVM. It requires a 1.5 JDK to build and simply typing make should build it. It produces a jar file, logc.jar, that can be run on the hotspot.log from LogCompilation output like this:

java -jar logc.jar hotspot.log

   For more details see the article on the LogCompilation tool in the Hotspot internals wiki.

hsdis tool

   The hsdis tool is a dissasembler used by Hotspot for debugging purposes. For more details see: <OpenJDK_root>hotspot\src\share\tools\hsdis\README.txt

C1visualizer

   The C1 visualizer tool is used to visualize work of the C1 JIT client compiler. For more details read the user guide from the c1visualizer project repository (see references).

jmap

   JMAP prints shared object memory maps or heap memory details of a given process or core file or a remote debug server. You can see Oracle documentation for more details on the jmap utility.

jconsole

   You can use the JConsole JMX client to connect to the default JMX agent running in the JVM to display various statistics on the running JVM instance.
You may need to specify -Dcom.sun.management.jmxremote when starting the application.

Using serviceability utilities

   Yet another option for debugging the Hotspot JVM is to use serviceability utilities that allow observing JVM operations by other Java processes. Here is a list of the various utility implementations throughout the JVM codebase that you can inspect depending on your use case:

The Serviceability Agent(SA):
   hotspot/agent/
   hotspot/src/share/vm/runtime/vmStructs.hpp
   hotspot/src/share/vm/runtime/vmStructs.cpp
   jvmstat performance counters:
   hotspot/src/share/vm/prims/perf.cpp
   hotspot/src/share/runtime/perfMemory.cpp
   hotspot/src/share/runtime/perfData.cpp
   hotspot/src/share/runtime/statSampler.cpp
   hotspot/src/share/vm/services/*Service.cpp
   hotspot/src/os/solaris/vm/perfMemory_solaris.cpp
   hotspot/src/os/linux/vm/perfMemory_linux.cpp
   hotspot/src/os/win32/vm/perfMemory_win32.cpp

The Java Virtual Machine Tool Interface (JVMTI):
   hotspot/src/share/vm/prims/jvmtiGen.java
   hotspot/src/share/vm/prims/jvmtiGen.java
   hotspot/src/share/vm/prims/jvmti.xml

The Monitoring and Management interface:
   hotspot/src/share/vm/services/

Dynamic Attach:
   hotspot/src/share/vm/services/attachListener.*
   hotspot/src/os/linux/vm/attachListener_linux.cpp
   hotspot/src/os/solaris/vm/attachListener_solaris.cpp
   hotspot/src/os/win32/vm/attachListener_win32.cpp

DTrace:
   hotspot/src/os/solaris/dtrace/
   hotspot/build/solaris/makefiles/dtrace.make 

pstack support:
   hotspot/src/os/solaris/dtrace/

   You can read more about the above utilities also from the Hotspot documentation - see references.

Share