/* * 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 */ 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 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 localVars = frame.visibleVariables(); boolean changedLocal = false; boolean caughtOFE = false; for (LocalVariable lv : localVars) { if (lv.name().equals("zero")) { try { frame.setValue(lv, vm().mirrorOf(0)); // triggers deoptimization! changedLocal = true; } catch (OpaqueFrameException e) { caughtOFE = true; } } } boolean isVirtualThread = DebuggeeWrapper.isVirtual(); Asserts.assertTrue(caughtOFE == isVirtualThread); Asserts.assertTrue(changedLocal == !isVirtualThread); // 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"); } } }