| /* |
| * Copyright (c) 2018, Oracle and/or its affiliates. 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. |
| */ |
| |
| |
| package org.graalvm.compiler.hotspot.test; |
| |
| import java.io.IOException; |
| import java.util.concurrent.locks.ReentrantLock; |
| import java.util.List; |
| |
| import org.graalvm.compiler.test.SubprocessUtil; |
| import org.graalvm.compiler.test.SubprocessUtil.Subprocess; |
| |
| import org.junit.Assume; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| public class ReservedStackAccessTest extends HotSpotGraalCompilerTest { |
| @Before |
| public void check() { |
| Assume.assumeTrue(runtime().getVMConfig().enableStackReservedZoneAddress != 0); |
| } |
| |
| public void stackAccessTest() { |
| Assume.assumeTrue(runtime().getVMConfig().enableStackReservedZoneAddress != 0); |
| |
| int passed = 0; |
| for (int i = 0; i < 1000; i++) { |
| // Each iteration has to be executed by a new thread. The test |
| // relies on the random size area pushed by the VM at the beginning |
| // of the stack of each Java thread it creates. |
| RunWithSOEContext r = new RunWithSOEContext(new ReentrantLockTest(), 256); |
| Thread thread = new Thread(r); |
| thread.start(); |
| try { |
| thread.join(); |
| assertTrue(r.result.equals("PASSED"), r.result); |
| ++passed; |
| } catch (InterruptedException ex) { |
| } |
| } |
| System.out.println("RESULT: " + (passed == 1000 ? "PASSED" : "FAILED")); |
| } |
| |
| public static void main(String[] args) { |
| new ReservedStackAccessTest().stackAccessTest(); |
| } |
| |
| @Test |
| public void run() throws IOException, InterruptedException { |
| Assume.assumeTrue(runtime().getVMConfig().enableStackReservedZoneAddress != 0); |
| List<String> vmArgs = SubprocessUtil.withoutDebuggerArguments(SubprocessUtil.getVMCommandLine()); |
| vmArgs.add("-XX:+UseJVMCICompiler"); |
| vmArgs.add("-Dgraal.Inline=false"); |
| vmArgs.add("-XX:CompileCommand=exclude,java/util/concurrent/locks/AbstractOwnableSynchronizer.setExclusiveOwnerThread"); |
| Subprocess proc = SubprocessUtil.java(vmArgs, ReservedStackAccessTest.class.getName()); |
| boolean passed = false; |
| for (String line : proc.output) { |
| if (line.equals("RESULT: PASSED")) { |
| passed = true; |
| } |
| } |
| if (!passed) { |
| for (String line : proc.output) { |
| System.err.println("" + line); |
| } |
| } |
| assertTrue(passed); |
| } |
| |
| static class ReentrantLockTest { |
| |
| private ReentrantLock[] lockArray; |
| // Frame sizes vary a lot between interpreted code and compiled code |
| // so the lock array has to be big enough to cover all cases. |
| // If test fails with message "Not conclusive test", try to increase |
| // LOCK_ARRAY_SIZE value |
| private static final int LOCK_ARRAY_SIZE = 8192; |
| private boolean stackOverflowErrorReceived; |
| StackOverflowError soe = null; |
| int index = -1; |
| |
| public void initialize() { |
| lockArray = new ReentrantLock[LOCK_ARRAY_SIZE]; |
| for (int i = 0; i < LOCK_ARRAY_SIZE; i++) { |
| lockArray[i] = new ReentrantLock(); |
| } |
| stackOverflowErrorReceived = false; |
| } |
| |
| public String getResult() { |
| if (!stackOverflowErrorReceived) { |
| return "ERROR: Not conclusive test: no StackOverflowError received"; |
| } |
| for (int i = 0; i < LOCK_ARRAY_SIZE; i++) { |
| if (lockArray[i].isLocked()) { |
| if (!lockArray[i].isHeldByCurrentThread()) { |
| StringBuilder s = new StringBuilder(); |
| s.append("FAILED: ReentrantLock "); |
| s.append(i); |
| s.append(" looks corrupted"); |
| return s.toString(); |
| } |
| } |
| } |
| return "PASSED"; |
| } |
| |
| public void run() { |
| try { |
| lockAndCall(0); |
| } catch (StackOverflowError e) { |
| soe = e; |
| stackOverflowErrorReceived = true; |
| } |
| } |
| |
| private void lockAndCall(int i) { |
| index = i; |
| if (i < LOCK_ARRAY_SIZE) { |
| lockArray[i].lock(); |
| lockAndCall(i + 1); |
| } |
| } |
| } |
| |
| static class RunWithSOEContext implements Runnable { |
| |
| int counter; |
| int deframe; |
| int decounter; |
| int setupSOEFrame; |
| int testStartFrame; |
| ReentrantLockTest test; |
| String result = "FAILED: no result"; |
| |
| RunWithSOEContext(ReentrantLockTest test, int deframe) { |
| this.test = test; |
| this.deframe = deframe; |
| } |
| |
| @Override |
| public void run() { |
| counter = 0; |
| decounter = deframe; |
| test.initialize(); |
| recursiveCall(); |
| System.out.println("Framework got StackOverflowError at frame = " + counter); |
| System.out.println("Test started execution at frame = " + (counter - deframe)); |
| result = test.getResult(); |
| } |
| |
| @SuppressWarnings("unused") |
| void recursiveCall() { |
| // Unused local variables to increase the frame size |
| long l1; |
| long l2; |
| long l3; |
| long l4; |
| long l5; |
| long l6; |
| long l7; |
| long l8; |
| long l9; |
| long l10; |
| long l11; |
| long l12; |
| long l13; |
| long l14; |
| long l15; |
| long l16; |
| long l17; |
| long l18; |
| long l19; |
| long l20; |
| long l21; |
| long l22; |
| long l23; |
| long l24; |
| long l25; |
| long l26; |
| long l27; |
| long l28; |
| long l30; |
| long l31; |
| long l32; |
| long l33; |
| long l34; |
| long l35; |
| long l36; |
| long l37; |
| counter++; |
| try { |
| recursiveCall(); |
| } catch (StackOverflowError e) { |
| } |
| decounter--; |
| if (decounter == 0) { |
| setupSOEFrame = counter; |
| testStartFrame = counter - deframe; |
| test.run(); |
| } |
| } |
| } |
| |
| } |