1 /* 2 * Copyright (c) 2023, 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 * @test id=default 26 * @summary Verifies JVMTI StopThread support for virtual threads. 27 * @requires vm.continuations 28 * @library /test/lib 29 * @run main/othervm/native -agentlib:StopThreadTest StopThreadTest 30 */ 31 32 /* 33 * @test id=no-vmcontinuations 34 * @summary Verifies JVMTI StopThread support for bound virtual threads. 35 * @library /test/lib 36 * @run main/othervm/native -agentlib:StopThreadTest -XX:+UnlockExperimentalVMOptions -XX:-VMContinuations -DboundVThread=true StopThreadTest 37 */ 38 39 /* 40 * @test id=platform 41 * @summary Verifies JVMTI StopThread support for platform threads. 42 * @library /test/lib 43 * @run main/othervm/native -agentlib:StopThreadTest StopThreadTest platform 44 */ 45 46 import jdk.test.lib.Platform; 47 import java.lang.AssertionError; 48 49 /* 50 * The test exercises the JVMTI function: StopThread(jthread). 51 * The test creates a new virtual or platform thread. 52 * Its method run() invokes the following methods: 53 * - method A() that is blocked on a monitor 54 * - method B() that is stopped at a breakpoint 55 * - method C() that forces agent to send AssertionError exception to its own thread 56 * All cases are using JVMTI StopThread to send an AssertionError object. 57 */ 58 public class StopThreadTest { 59 private static final String agentLib = "StopThreadTest"; 60 static final boolean isBoundVThread = Boolean.getBoolean("boundVThread"); 61 static final int JVMTI_ERROR_NONE = 0; 62 static final int JVMTI_ERROR_OPAQUE_FRAME = 32; 63 static final int THREAD_NOT_SUSPENDED = 13; 64 static final int PASSED = 0; 65 static final int FAILED = 2; 66 67 static void log(String str) { System.out.println(str); } 68 69 static native void prepareAgent(Class taskClass, Object exceptionObject); 70 static native void suspendThread(Thread thread); 71 static native void resumeThread(Thread thread); 72 static native void ensureAtBreakpoint(); 73 static native void notifyAtBreakpoint(); 74 static native int stopThread(Thread thread); 75 76 static int status = PASSED; 77 static boolean is_virtual = true; 78 79 static void setFailed(String msg) { 80 log("\nFAILED: " + msg); 81 status = FAILED; 82 } 83 84 static void throwFailed(String msg) { 85 log("\nFAILED: " + msg); 86 throw new RuntimeException("StopThreadTest failed!"); 87 } 88 89 public static void main(String args[]) { 90 is_virtual = !(args.length > 0 && args[0].equals("platform")); 91 run(); 92 if (status == FAILED) { 93 throwFailed("StopThreadTest!"); 94 } 95 log("\nStopThreadTest passed"); 96 } 97 98 public static void run() { 99 TestTask testTask = new TestTask(); 100 Thread testTaskThread = null; 101 AssertionError excObject = new AssertionError(); 102 int retCode; 103 104 prepareAgent(TestTask.class, excObject); 105 106 log("\nMain #A: method A() must be blocked on entering a synchronized statement"); 107 synchronized (TestTask.lock) { 108 if (is_virtual) { 109 testTaskThread = Thread.ofVirtual().name("TestTaskThread").start(testTask); 110 } else { 111 testTaskThread = Thread.ofPlatform().name("TestTaskThread").start(testTask); 112 } 113 TestTask.ensureAtPointA(testTaskThread); 114 115 if (is_virtual) { // this check is for virtual target thread only 116 log("\nMain #A.1: unsuspended"); 117 retCode = stopThread(testTaskThread); 118 if (retCode != THREAD_NOT_SUSPENDED) { 119 throwFailed("Main #A.1: expected THREAD_NOT_SUSPENDED instead of: " + retCode); 120 } else { 121 log("Main #A.1: got expected THREAD_NOT_SUSPENDED"); 122 } 123 } 124 125 log("\nMain #A.2: suspended"); 126 suspendThread(testTaskThread); 127 retCode = stopThread(testTaskThread); 128 int expectedRetCode = (is_virtual && !isBoundVThread && (Platform.isX64() || Platform.isAArch64())) ? JVMTI_ERROR_OPAQUE_FRAME : JVMTI_ERROR_NONE; 129 String expectedRetCodeName = (is_virtual && !isBoundVThread && (Platform.isX64() || Platform.isAArch64())) ? 130 "JVMTI_ERROR_OPAQUE_FRAME" : "JVMTI_ERROR_NONE"; 131 if (retCode != expectedRetCode) { 132 throwFailed("Main #A.2: expected " + expectedRetCodeName + " instead of: " + retCode); 133 } else { 134 log("Main #A.2: got expected " + expectedRetCodeName); 135 } 136 resumeThread(testTaskThread); 137 } 138 log("\nMain #B: method B() must be blocked in a breakpoint event handler"); 139 { 140 ensureAtBreakpoint(); 141 142 if (is_virtual) { // this check is for virtual target thread only 143 log("\nMain #B.1: unsuspended"); 144 retCode = stopThread(testTaskThread); 145 if (retCode != THREAD_NOT_SUSPENDED) { 146 throwFailed("Main #B.1: expected THREAD_NOT_SUSPENDED instead of: " + retCode); 147 } 148 } 149 150 log("\nMain #B.2: suspended"); 151 suspendThread(testTaskThread); 152 retCode = stopThread(testTaskThread); 153 if (retCode != JVMTI_ERROR_NONE) { 154 throwFailed("Main #B.2: expected JVMTI_ERROR_NONE"); 155 } 156 resumeThread(testTaskThread); 157 158 notifyAtBreakpoint(); 159 } 160 161 log("\nMain #C: method C() sends AssertionError object to its own thread"); 162 { 163 // StopThread is called from the test task (own thread) and expected to succeed. 164 // No suspension of the test task thread is required or can be done in this case. 165 TestTask.ensureFinished(); 166 } 167 168 try { 169 testTaskThread.join(); 170 } catch (InterruptedException ex) { 171 throwFailed("Unexpected " + ex); 172 } 173 } 174 175 176 static class TestTask implements Runnable { 177 static Object lock = new Object(); 178 static void log(String str) { System.out.println(str); } 179 180 static volatile boolean atPointA = false; 181 static volatile boolean finished = false; 182 183 static void sleep(long millis) { 184 try { 185 Thread.sleep(millis); 186 } catch (InterruptedException e) { 187 throw new RuntimeException("Interruption in TestTask.sleep: \n\t" + e); 188 } 189 } 190 191 static void ensureAtPointA(Thread vt) { 192 // wait while the thread state is not the expected one 193 while (vt.getState() != Thread.State.BLOCKED) { 194 sleep(1); 195 } 196 } 197 198 // Ensure thread is finished. 199 static void ensureFinished() { 200 while (!finished) { 201 sleep(1); 202 } 203 } 204 205 public void run() { 206 log("TestTask.run: started"); 207 208 boolean seenExceptionFromA = false; 209 try { 210 A(); 211 } catch (AssertionError ex) { 212 log("TestTask.run: caught expected AssertionError from method A()"); 213 seenExceptionFromA = true; 214 } 215 Thread.interrupted(); 216 if (!seenExceptionFromA 217 && !(is_virtual 218 && !isBoundVThread 219 && (Platform.isX64() || (Platform.isAArch64())) )) { 220 StopThreadTest.setFailed("TestTask.run: expected AssertionError from method A()"); 221 } 222 sleep(1); // to cause yield 223 224 boolean seenExceptionFromB = false; 225 try { 226 B(); 227 } catch (AssertionError ex) { 228 log("TestTask.run: caught expected AssertionError from method B()"); 229 seenExceptionFromB = true; 230 } 231 Thread.interrupted(); 232 if (!seenExceptionFromB) { 233 StopThreadTest.setFailed("TestTask.run: expected AssertionError from method B()"); 234 } 235 sleep(1); // to cause yield 236 237 boolean seenExceptionFromC = false; 238 try { 239 C(); 240 } catch (AssertionError ex) { 241 log("TestTask.run: caught expected AssertionError from method C()"); 242 seenExceptionFromC = true; 243 } 244 Thread.interrupted(); 245 if (!seenExceptionFromC) { 246 StopThreadTest.setFailed("TestTask.run: expected AssertionError from method C()"); 247 } 248 finished = true; 249 } 250 251 // Method is blocked on entering a synchronized statement. 252 // StopThread is used to send an AssertionError object two times: 253 // - when not suspended: THREAD_NOT_SUSPENDED is expected 254 // - when suspended: JVMTI_ERROR_NONE is expected 255 static void A() { 256 log("TestTask.A: started"); 257 synchronized (lock) { 258 } 259 log("TestTask.A: finished"); 260 } 261 262 // A breakpoint is set at start of this method. 263 // StopThread is used to send an AssertionError object two times: 264 // - when not suspended: THREAD_NOT_SUSPENDED is expected 265 // - when suspended: expected to succeed 266 static void B() { 267 log("TestTask.B: started"); 268 } 269 270 // This method uses StopThread to send an AssertionError object to 271 // its own thread. It is expected to succeed. 272 static void C() { 273 log("TestTask.C: started"); 274 StopThreadTest.stopThread(Thread.currentThread()); 275 log("TestTask.C: finished"); 276 } 277 } 278 }