1 /* 2 * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 25 package org.graalvm.compiler.hotspot.test; 26 27 import java.io.IOException; 28 import java.util.List; 29 import java.util.concurrent.locks.ReentrantLock; 30 31 import org.graalvm.compiler.test.SubprocessUtil; 32 import org.graalvm.compiler.test.SubprocessUtil.Subprocess; 33 import org.junit.Assume; 34 import org.junit.Before; 35 import org.junit.Test; 36 37 public class ReservedStackAccessTest extends HotSpotGraalCompilerTest { 38 @Before 39 public void check() { 40 Assume.assumeTrue(runtime().getVMConfig().enableStackReservedZoneAddress != 0); 41 } 42 43 public void stackAccessTest() { 44 Assume.assumeTrue(runtime().getVMConfig().enableStackReservedZoneAddress != 0); 45 46 int passed = 0; 47 for (int i = 0; i < 1000; i++) { 48 // Each iteration has to be executed by a new thread. The test 49 // relies on the random size area pushed by the VM at the beginning 50 // of the stack of each Java thread it creates. 51 RunWithSOEContext r = new RunWithSOEContext(new ReentrantLockTest(), 256); 52 Thread thread = new Thread(r); 53 thread.start(); 54 try { 55 thread.join(); 56 assertTrue(r.result.equals("PASSED"), r.result); 57 ++passed; 58 } catch (InterruptedException ex) { 59 } 60 } 61 System.out.println("RESULT: " + (passed == 1000 ? "PASSED" : "FAILED")); 62 } 63 64 public static void main(String[] args) { 65 new ReservedStackAccessTest().stackAccessTest(); 66 } 67 68 @Test 69 public void run() throws IOException, InterruptedException { 70 Assume.assumeTrue(runtime().getVMConfig().enableStackReservedZoneAddress != 0); 71 List<String> vmArgs = SubprocessUtil.withoutDebuggerArguments(SubprocessUtil.getVMCommandLine()); 72 vmArgs.add("-XX:+UseJVMCICompiler"); 73 vmArgs.add("-Dgraal.Inline=false"); 74 vmArgs.add("-XX:CompileCommand=exclude,java/util/concurrent/locks/AbstractOwnableSynchronizer.setExclusiveOwnerThread"); 75 vmArgs.addAll(SubprocessUtil.getPackageOpeningOptions()); 76 77 // Avoid SOE in HotSpotJVMCIRuntime.adjustCompilationLevel 78 vmArgs.add("-Dgraal.CompileGraalWithC1Only=false"); 79 80 Subprocess proc = SubprocessUtil.java(vmArgs, ReservedStackAccessTest.class.getName()); 81 boolean passed = false; 82 for (String line : proc.output) { 83 if (line.equals("RESULT: PASSED")) { 84 passed = true; 85 } 86 } 87 if (!passed) { 88 System.err.println(proc); 89 } 90 assertTrue(passed); 91 } 92 93 static class ReentrantLockTest { 94 95 private ReentrantLock[] lockArray; 96 // Frame sizes vary a lot between interpreted code and compiled code 97 // so the lock array has to be big enough to cover all cases. 98 // If test fails with message "Not conclusive test", try to increase 99 // LOCK_ARRAY_SIZE value 100 private static final int LOCK_ARRAY_SIZE = 8192; 101 private boolean stackOverflowErrorReceived; 102 StackOverflowError soe = null; 103 int index = -1; 104 105 public void initialize() { 106 lockArray = new ReentrantLock[LOCK_ARRAY_SIZE]; 107 for (int i = 0; i < LOCK_ARRAY_SIZE; i++) { 108 lockArray[i] = new ReentrantLock(); 109 } 110 stackOverflowErrorReceived = false; 111 } 112 113 public String getResult() { 114 if (!stackOverflowErrorReceived) { 115 return "ERROR: Not conclusive test: no StackOverflowError received"; 116 } 117 for (int i = 0; i < LOCK_ARRAY_SIZE; i++) { 118 if (lockArray[i].isLocked()) { 119 if (!lockArray[i].isHeldByCurrentThread()) { 120 StringBuilder s = new StringBuilder(); 121 s.append("FAILED: ReentrantLock "); 122 s.append(i); 123 s.append(" looks corrupted"); 124 return s.toString(); 125 } 126 } 127 } 128 return "PASSED"; 129 } 130 131 public void run() { 132 try { 133 lockAndCall(0); 134 } catch (StackOverflowError e) { 135 soe = e; 136 stackOverflowErrorReceived = true; 137 } 138 } 139 140 private void lockAndCall(int i) { 141 index = i; 142 if (i < LOCK_ARRAY_SIZE) { 143 lockArray[i].lock(); 144 lockAndCall(i + 1); 145 } 146 } 147 } 148 149 static class RunWithSOEContext implements Runnable { 150 151 int counter; 152 int deframe; 153 int decounter; 154 int setupSOEFrame; 155 int testStartFrame; 156 ReentrantLockTest test; 157 String result = "FAILED: no result"; 158 159 RunWithSOEContext(ReentrantLockTest test, int deframe) { 160 this.test = test; 161 this.deframe = deframe; 162 } 163 164 @Override 165 public void run() { 166 counter = 0; 167 decounter = deframe; 168 test.initialize(); 169 recursiveCall(); 170 System.out.println("Framework got StackOverflowError at frame = " + counter); 171 System.out.println("Test started execution at frame = " + (counter - deframe)); 172 result = test.getResult(); 173 } 174 175 @SuppressWarnings("unused") 176 void recursiveCall() { 177 // Unused local variables to increase the frame size 178 long l1; 179 long l2; 180 long l3; 181 long l4; 182 long l5; 183 long l6; 184 long l7; 185 long l8; 186 long l9; 187 long l10; 188 long l11; 189 long l12; 190 long l13; 191 long l14; 192 long l15; 193 long l16; 194 long l17; 195 long l18; 196 long l19; 197 long l20; 198 long l21; 199 long l22; 200 long l23; 201 long l24; 202 long l25; 203 long l26; 204 long l27; 205 long l28; 206 long l30; 207 long l31; 208 long l32; 209 long l33; 210 long l34; 211 long l35; 212 long l36; 213 long l37; 214 counter++; 215 try { 216 recursiveCall(); 217 } catch (StackOverflowError e) { 218 } 219 decounter--; 220 if (decounter == 0) { 221 setupSOEFrame = counter; 222 testStartFrame = counter - deframe; 223 test.run(); 224 } 225 } 226 } 227 228 }