JDK-6572160 : Instrumentation.getObjectSize triggers JVM crash in JPLISAssert in shutdown
  • Type: Bug
  • Component: core-svc
  • Sub-Component: tools
  • Affected Version: 6
  • Priority: P3
  • Status: Closed
  • Resolution: Fixed
  • OS: linux
  • CPU: x86
  • Submitted: 2007-06-21
  • Updated: 2014-04-14
  • Resolved: 2011-03-08
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 6 JDK 7
6u81Fixed 7 b26Fixed
Description
FULL PRODUCT VERSION :
java version "1.6.0_01"
Java(TM) SE Runtime Environment (build 1.6.0_01-b06)
Java HotSpot(TM) Client VM (build 1.6.0_01-b06, mixed mode, sharing)


ADDITIONAL OS VERSION INFORMATION :
Linux d 2.6.18.5-XXX-mixed64-32 #1 SMP XXX  x86_64 GNU/Linux
System is a modified ubuntu dapper.

EXTRA RELEVANT SYSTEM CONFIGURATION :
NPTL 2.3.6 from glibc 2.3.6.

A DESCRIPTION OF THE PROBLEM :
Calling Instrumentation.getObjectSize() while the JVM is attempting to exit causes the JVM to crash on an internal assertion instead of exiting gracefully.

STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Build the attached source into a jar and run with a command line like:
./javac-1.6.0_01 TriggerJPLISAssert.java && jar -cfm triggerjplisassert.jar manifest.mf TriggerJPLISAssert.class TriggerJPLISAssert\$RoundAndRound.class && ./java-1.6.0_01 -server -javaagent:triggerjplisassert.jar -jar triggerjplisassert.jar


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
Nothing should be output other than the "exiting" message.  The daemon threads should be quietly shut down as they are if one comments out the getObjectSize() line.
ACTUAL -
A (variable) number of JPLISAssert failure messages are printed after the "exiting" message is printed by the main thread.  Different runs produce a different number of messages.

ERROR MESSAGES/STACK TRACES THAT OCCUR :
Sample output:

exiting
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE" at ../../../src/share/instrument/JPLISAgent.c line: 1296
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE" at ../../../src/share/instrument/JPLISAgent.c line: 1296
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE" at ../../../src/share/instrument/JPLISAgent.c line: 1296

no other stderr/stdout/core/hs_err_pid output.

REPRODUCIBILITY :
This bug can be reproduced always.

---------- BEGIN SOURCE ----------
manifest.mf file:
Premain-Class: TriggerJPLISAssert
Main-Class: TriggerJPLISAssert

TriggerJPLISAssert.java file:
import java.lang.instrument.Instrumentation;

public class TriggerJPLISAssert {
  private static Instrumentation instrumentation;

  private TriggerJPLISAssert() { }

  public static void premain(String agentArgs, Instrumentation inst) {
    instrumentation = inst;
  }

  public static void main(String args[]) throws InterruptedException {
    RoundAndRound[] threads = new RoundAndRound[10];
    for (int i=0; i<threads.length; ++i) {
      threads[i] = new RoundAndRound(instrumentation);
      threads[i].start();
    }
    Thread.sleep(1); // let all threads get going in their loops
    System.out.println("exiting");
  }

  private static class RoundAndRound extends Thread {
    private final Instrumentation inst;
    private final Object anObject;
    public RoundAndRound(Instrumentation inst) {
      this.inst = inst;
      this.anObject = new Object();
      setDaemon(true);
    }
    public void run() {
      long sum = 0;
      while (true) {
        sum += inst.getObjectSize(anObject);
      }
    }
  }
}

---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
Adding a shutdown hook with Runtime.getRuntime().addShutdownHook() which sets an application-specific "stop calling getObjectSize()" flag seems to prevent the crashes and lets my application exit gracefully.

This is an unsatisfactory long-term workaround because it requires adding synchronization (around the shutdown-indicating flag) for each invocation of getObjectSize(), or else living with a slight race condition by making the flag volatile and hoping shutdown doesn't proceed beyond the point that triggers the assert between the check of the flag and the call to getObjectSize().
I've copied the test from the description and added it to the
INSTRUMENT_REGRESSION test suite as StressGetObjectSizeTest.java.
Using the test I can reproduce the failure:

java version "1.7.0-ea"
Java(TM) SE Runtime Environment (build 1.7.0-ea-b24)
Java HotSpot(TM) Client VM (build 12.0-b01, mixed mode, sharing)

STATUS:Passed.
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE"
at ../../../src/share/instrument/JPLISAgent.c line: 1328
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE"
at ../../../src/share/instrument/JPLISAgent.c line: 1328
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE"
at ../../../src/share/instrument/JPLISAgent.c line: 1328
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE"
at ../../../src/share/instrument/JPLISAgent.c line: 1328
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE"
at ../../../src/share/instrument/JPLISAgent.c line: 1328
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE"
at ../../../src/share/instrument/JPLISAgent.c line: 1328
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE"
at ../../../src/share/instrument/JPLISAgent.c line: 1328
*** java.lang.instrument ASSERTION FAILED ***: "jvmtierror == JVMTI_ERROR_NONE"
at ../../../src/share/instrument/JPLISAgent.c line: 1328
result: Passed. Execution successful

Obviously JavaTest/JTREG thinks the test has passed so I'll
have to tweak it a bit to show up as a failure.
The following new test will fail without the fix for this
bug in a promoted JDK:

    java/lang/instrument/StressGetObjectSizeTest.sh

Comments
SUGGESTED FIX Please see the attached 6572160-webrev-cr0 file for the proposed fix.
21-02-2008

EVALUATION I've confirmed that we're getting JVMTI_ERROR_WRONG_PHASE because of the late calls to JVM/TI GetObjectSize(). I don't believe that we can add a new exception to the JLI APIs at this point so I think we're going to have to figure out a way to insulate the Java agent from the JVMTI_ERROR_WRONG_PHASE returns. I'm currently thinking of something like a zero (0) return from JLI getObjectSize() when the underlying JVM/TI call returns JVMTI_ERROR_WRONG_PHASE.
20-02-2008

EVALUATION This one needs investigation but is likely to be that the JPLIS agent is getting a phase error during shutdown. If so, then it may require a specification update to allow Instrumentation methods throw an exception (probably IllegalStateException) for this case.
21-06-2007