8212928: Assertion too strict in compiledVFrame::update_deferred_value on SPARC
authorkvn
Thu, 08 Nov 2018 09:04:00 -0800
changeset 52457 6954394aa33a
parent 52456 90ff0e286a5e
child 52458 66a0e6b3ec1a
8212928: Assertion too strict in compiledVFrame::update_deferred_value on SPARC Reviewed-by: kvn Contributed-by: richard.reingruber@sap.com
src/hotspot/share/runtime/vframe_hp.cpp
test/jdk/com/sun/jdi/SetLocalWhileThreadInNative.java
test/jdk/com/sun/jdi/compilerDirectives.json
--- a/src/hotspot/share/runtime/vframe_hp.cpp	Thu Nov 08 16:16:57 2018 +0000
+++ b/src/hotspot/share/runtime/vframe_hp.cpp	Thu Nov 08 09:04:00 2018 -0800
@@ -102,7 +102,8 @@
 }
 
 void compiledVFrame::update_deferred_value(BasicType type, int index, jvalue value) {
-  assert(fr().is_deoptimized_frame(), "frame must be scheduled for deoptimization");
+  assert(fr().is_deoptimized_frame() || thread()->must_deopt_id() == fr().id(),
+         "frame must be scheduled for deoptimization");
   GrowableArray<jvmtiDeferredLocalVariableSet*>* deferred = thread()->deferred_locals();
   jvmtiDeferredLocalVariableSet* locals = NULL;
   if (deferred != NULL ) {
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/SetLocalWhileThreadInNative.java	Thu Nov 08 09:04:00 2018 -0800
@@ -0,0 +1,191 @@
+/*
+ * Copyright (c) 2018 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @bug 8212928
+ * @summary With NeedsDeoptSuspend == true the assertion in compiledVFrame::update_deferred_value() fails, because the frame is not deoptimized.
+ * @author Richard Reingruber richard DOT reingruber AT sap DOT com
+ *
+ * @library /test/lib
+ *
+ * @run build TestScaffold VMConnection TargetListener TargetAdapter
+ * @run main jdk.test.lib.FileInstaller compilerDirectives.json compilerDirectives.json
+ * @run compile -g SetLocalWhileThreadInNative.java
+ * @run driver SetLocalWhileThreadInNative -Xbatch -XX:-TieredCompilation -XX:CICompilerCount=1 -XX:+UnlockDiagnosticVMOptions -XX:CompilerDirectivesFile=compilerDirectives.json -XX:+PrintCompilation -XX:+PrintInlining
+ */
+
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.List;
+
+import com.sun.jdi.*;
+import com.sun.jdi.event.*;
+
+import jdk.test.lib.Asserts;
+
+/********** target program **********/
+
+class SetLocalWhileThreadInNativeTarget {
+
+    public static final String name = SetLocalWhileThreadInNativeTarget.class.getName();
+    public static FileInputStream fis;
+    public static int count;
+    public static int bytesRead;
+
+
+    // Let D (debugger) be a JVMTI agent that updates a local of a compiled frame F owned by
+    // thread T. In this test F corresponds to dontinline_testMethod.
+    //
+    // Issue: The VM thread executes an VM op on behalf of D to set the local in F. Doing so it
+    // requests the deoptimization of F and then calls compiledVFrame::update_deferred_value(),
+    // where frame::is_deoptimized_frame() returns false causing an assertion failure on SPARC where
+    // NeedsDeoptSuspend is true.
+    //
+    // Analysis: The deoptimization of F is requested, while T is in the native method
+    // java.io.FileInputStream::read0() and F is the direct caller of read0(). This is a special
+    // case with NeedsDeoptSuspend in frame::deoptimize(). Effectively the deoptimization is not
+    // done synchronously, instead T deoptimizes F at a later point upon return from the native
+    // method.
+    public static int dontinline_testMethod() {
+        int zero = 0;
+        int val = 0;
+        try {
+            val = fis.read(); // Will be inlined. Calls native method java.io.FileInputStream::read0()
+            count++;
+        } catch (IOException e) { /* ignored */ }
+        return val + zero;
+    }
+
+    public static void main(String[] args) {
+        System.out.println(name + " is up and running.");
+        fis = new FileInputStream(FileDescriptor.in);
+        bytesRead=0;
+        while (true) {
+            int val = dontinline_testMethod();
+            if (val == SetLocalWhileThreadInNative.STOP) {
+                System.out.println("Debuggee: received STOP message");
+                System.exit(0);
+            }
+            bytesRead++;
+            if ((bytesRead & ((1L << 14)-1)) == 0) {
+                System.out.println("Called test method " + bytesRead + " times");
+            }
+        }
+    }
+}
+
+ /********** test program **********/
+
+public class SetLocalWhileThreadInNative extends TestScaffold {
+    public static final int MESSAGE_COUNT = 10000;
+    public static final String MESSAGE    = "0123456789";
+    public static final int MESSAGE_SIZE  = MESSAGE.length();
+    public static final int TOTAL_BYTES   = MESSAGE_COUNT * MESSAGE_SIZE;
+    public static final int STOP = 255;
+
+    ReferenceType mainClass;
+    ThreadReference mainThread;
+
+    SetLocalWhileThreadInNative (String args[]) {
+        super(args);
+    }
+
+    public static void main(String[] args)
+        throws Exception
+    {
+        new SetLocalWhileThreadInNative (args).startTests();
+    }
+
+    /********** test core **********/
+
+    protected void runTests()
+        throws Exception
+    {
+        String targetProgName = SetLocalWhileThreadInNativeTarget.class.getName();
+        String testName = getClass().getSimpleName();
+
+        // Start debuggee and obtain reference to main thread an main class
+        BreakpointEvent bpe = startToMain(targetProgName);
+        mainClass = bpe.location().declaringType();
+        mainThread = bpe.thread();
+
+        // Resume debuggee send some bytes
+        vm().resume();
+        OutputStream os = vm().process().getOutputStream();
+        byte[] ba = MESSAGE.getBytes();
+        for (int i = 0; i < MESSAGE_COUNT; i++) {
+            os.write(ba);
+        }
+        os.flush();
+
+        // Wait for the debugee to read all the bytes.
+        int bytesRead = 0;
+        Field bytesReadField = mainClass.fieldByName("bytesRead");
+        do {
+            bytesRead = ((PrimitiveValue)mainClass.getValue(bytesReadField)).intValue();
+            System.out.println("debugee has read " + bytesRead + " of " + TOTAL_BYTES);
+            Thread.sleep(500);
+        } while (bytesRead < TOTAL_BYTES);
+
+        // By now  dontinline_testMethod() will be compiled. The debugee will be blocked in java.io.FileInputStream::read0().
+        // Now set local variable in dontinline_testMethod().
+        vm().suspend();
+        System.out.println("Debuggee Stack:");
+        List<StackFrame> stack_frames = mainThread.frames();
+        int i = 0;
+        for (StackFrame ff : stack_frames) {
+            System.out.println("frame[" + i++ +"]: " + ff.location().method());
+        }
+        StackFrame frame = mainThread.frame(2);
+        Asserts.assertEQ(frame.location().method().toString(), "SetLocalWhileThreadInNativeTarget.dontinline_testMethod()");
+        List<LocalVariable> localVars = frame.visibleVariables();
+        boolean changedLocal = false;
+        for (LocalVariable lv : localVars) {
+            if (lv.name().equals("zero")) {
+                frame.setValue(lv, vm().mirrorOf(0)); // triggers deoptimization!
+                changedLocal = true;
+            }
+        }
+        Asserts.assertTrue(changedLocal);
+
+        // signal stop
+        os.write(STOP);
+        os.flush();
+
+
+        // resume the target listening for events
+        listenUntilVMDisconnect();
+
+
+        // deal with results of test if anything has called failure("foo")
+        // testFailed will be true
+        if (!testFailed) {
+            println(testName + ": passed");
+        } else {
+            throw new Exception(testName + ": failed");
+        }
+    }
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/test/jdk/com/sun/jdi/compilerDirectives.json	Thu Nov 08 09:04:00 2018 -0800
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2018 SAP SE. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+[   
+   {   
+         match: "*.*",
+ 
+         c1: {
+           // control inlining of method
+           // + force inline, - dont inline
+           inline : "-*.dontinline_*",
+         },
+
+         c2: {
+           // control inlining of method
+           // + force inline, - dont inline
+           inline : "-*.dontinline_*",
+         }
+   },
+]