Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to use -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print option with JVM HotSpot

I 'm trying to use -XX:+UnlockDiagnosticVMOptions -XX:CompileCommand=print,*MyClass.myMethod command lines as described in this post.

It seems thats it's available with open-jdk (https://wikis.oracle.com/display/HotSpotInternals/PrintAssembly).

How can I use those options (or similar equivalents) with oracle JDK7 and the JVM HotSpot?

like image 285
alain.janinm Avatar asked Feb 18 '12 12:02

alain.janinm


People also ask

Can JVM crash?

A JVM crash could be caused by a programming error in the JRockit JVM or by errors in third-party library code. Identifying and troubleshooting a JVM crash can help you find a temporary workaround until the problem is solved in the JRockit JVM. This may also help Oracle Support to identify and fix the problem faster.

What does Jcmd do?

The jcmd utility is used to send diagnostic command requests to the JVM, where these requests are useful for controlling Java Flight Recordings, troubleshoot, and diagnose JVM and Java Applications.

What is Jcmd exe?

The jcmd utility is used to send diagnostic command requests to the JVM. It must be used on the same machine on which the JVM is running, and have the same effective user and group identifiers that were used to launch the JVM.

How do I enable Jfr?

The easiest and most intuitive way to use JFR is through the Flight Recorder plug-in that is integrated into Java Mission Control. This plug-in enables access to JFR functionality through an intuitive GUI.


2 Answers

These instructions apply to Linux (Ubuntu 10.04.4 LTS), but should be applicable for your OS. After downloading Oracle JDK 7u3 and appropriately setting your JAVA_HOME and PATH environment variables, execute the following to check available options:

java -XX:+AggressiveOpts -XX:+UnlockDiagnosticVMOptions -XX:+UnlockExperimentalVMOptions -XX:+PrintFlagsFinal -version

You should see the UnlockDiagnosticVMOptions, CompileCommand and PrintAssembly options are available. Using the CompileCommand option will also enable the PrintAssembly option. However, you will need the HotSpot disassembler plugin for PrintAssembly to work; without it, you might see something like the following:

$ java -version
java version "1.7.0_03"
Java(TM) SE Runtime Environment (build 1.7.0_03-b04)
Java HotSpot(TM) Server VM (build 22.1-b02, mixed mode)
$ java -server -XX:+UnlockDiagnosticVMOptions '-XX:CompileCommand=print,*Main.main' Main
CompilerOracle: print *Main.main
Java HotSpot(TM) Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output
Compiled method (c2)      68    1 %           Main::main @ 4 (49 bytes)
 total in heap  [0xb3a97548,0xb3a979ec] = 1188
 relocation     [0xb3a97610,0xb3a97624] = 20
 main code      [0xb3a97640,0xb3a97840] = 512
 stub code      [0xb3a97840,0xb3a97850] = 16
 oops           [0xb3a97850,0xb3a97858] = 8
 scopes data    [0xb3a97858,0xb3a97898] = 64
 scopes pcs     [0xb3a97898,0xb3a979e8] = 336
 dependencies   [0xb3a979e8,0xb3a979ec] = 4
Could not load hsdis-i386.so; library not loadable; PrintAssembly is disabled
OopMapSet contains 1 OopMaps

To get the HotSpot disassembler plugin, you will need to build it. Looking at the OpenJDK 7u2 source, the hsdis plugin readme says:

To use the plugin with a JVM, you need a new version that can load it. If the product mode of your JVM does not accept -XX:+PrintAssembly, you do not have a version that is new enough.

To build this project you [need] a copy of GNU binutils to build against.

In theory this should be buildable on Windows but getting a working GNU build environment on Windows has proven difficult.

We have confirmed above that Oracle JDK 7u3 supports PrintAssembly. I followed the hsdis plugin readme instructions, downloaded GNU binutils 2.22, placed it in the hsdis build/binutils directory and ran make. This eventually produced the following error:

hsdis.c:32:20: error: sysdep.h: No such file or directory

To correct this, I changed hsdis.c using the following patch:

diff -r 6259c6d3bbb7 src/share/tools/hsdis/hsdis.c
--- a/src/share/tools/hsdis/hsdis.c Mon Dec 12 23:08:01 2011 -0800
+++ b/src/share/tools/hsdis/hsdis.c Thu Feb 23 09:26:37 2012 -0500
@@ -29,7 +29,7 @@

 #include "hsdis.h"

-#include <sysdep.h>
+#include <errno.h>
 #include <libiberty.h>
 #include <bfd.h>
 #include <dis-asm.h>

Running make was then successful. Now just copy the hsdis-i386.so plugin in the hsdis build directory to the Oracle JDK 7u3 jre/lib/i386 directory.

Now you can see the disassembled compiled code:

$ java -server -XX:+UnlockDiagnosticVMOptions '-XX:CompileCommand=print,*Main.main' Main
CompilerOracle: print *Main.main
Java HotSpot(TM) Server VM warning: printing of assembly code is enabled; turning on DebugNonSafepoints to gain additional output
Compiled method (c2)      68    1 %           Main::main @ 4 (49 bytes)
 total in heap  [0xb3999548,0xb39999ec] = 1188
 relocation     [0xb3999610,0xb3999624] = 20
 main code      [0xb3999640,0xb3999840] = 512
 stub code      [0xb3999840,0xb3999850] = 16
 oops           [0xb3999850,0xb3999858] = 8
 scopes data    [0xb3999858,0xb3999898] = 64
 scopes pcs     [0xb3999898,0xb39999e8] = 336
 dependencies   [0xb39999e8,0xb39999ec] = 4
Loaded disassembler from [snip]/jdk1.7.0_03/jre/lib/i386/hsdis-i386.so
Decoding compiled method 0xb3999548:
Code:
[Disassembling for mach='i386']
[Entry Point]
[Verified Entry Point]
[Constants]
  # {method} 'main' '([Ljava/lang/String;)V' in 'Main'
  0xb3999640: call   0xb6ff8510         ;   {runtime_call}
  0xb3999645: data32 xchg %ax,%ax
  0xb3999648: mov    %eax,-0x3000(%esp)
  0xb399964f: push   %ebp
  0xb3999650: sub    $0x38,%esp
  0xb3999656: mov    %ecx,%esi
  0xb3999658: mov    0x4(%esi),%ebp
  0xb399965b: mov    0x8(%esi),%edi
  0xb399965e: mov    (%ecx),%esi
  0xb3999660: mov    %ecx,(%esp)
  0xb3999663: call   0xb7078cf0         ;*iload_3
[snip]
  0xb399983e: hlt    
  0xb399983f: hlt    
[Exception Handler]
[Stub Code]
  0xb3999840: jmp    0xb39981e0         ;   {no_reloc}
[Deopt Handler Code]
  0xb3999845: push   $0xb3999845        ;   {section_word}
  0xb399984a: jmp    0xb397e220         ;   {runtime_call}
  0xb399984f: .byte 0x0
OopMapSet contains 1 OopMaps

#0 
OopMap{off=468}

The test class I've used is:

public class Main {
    public static void main(final String[] args) {
        long x = 0;
        for (int i = 0; i < 1000000; i++) {
            x += calculate(i);
        }
        System.out.println("x=" + x);
    }

    private static long calculate(final int i) {
        return (long)i * (long)i;
    }
}
like image 187
Dan Cruz Avatar answered Sep 28 '22 06:09

Dan Cruz


in my case to see the disassembled compiled code:

$ java -XX:CompileThreshold=1 -XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly -XX:CompileCommand="compileonly pac/kage/MyClass myMethod" MyClass

in example above has is cycle: for (int i = 0; i < 1 000 000; i++) {...}, that is why in our case without 1 000 000 iterations we need -XX:CompileThreshold=1 option (by default 10 000 for -server) to see our disassembled compiled code.

like image 36
user49 Avatar answered Sep 28 '22 07:09

user49