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 26 * @summary Verifies JVMTI works for agents loaded into running VM 27 * @requires vm.jvmti 28 * @requires vm.continuations 29 * @library /test/lib /test/hotspot/jtreg 30 * @build jdk.test.whitebox.WhiteBox 31 * 32 * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox 33 * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -agentlib:ToggleNotifyJvmtiTest ToggleNotifyJvmtiTest 34 * @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Djdk.attach.allowAttachSelf=true ToggleNotifyJvmtiTest attach 35 */ 36 37 import com.sun.tools.attach.VirtualMachine; 38 import java.util.concurrent.ThreadFactory; 39 import jdk.test.whitebox.WhiteBox; 40 41 // The TestTask mimics some thread activity, but it is important 42 // to have sleep() calls to provide yielding as some frequency of virtual 43 // thread mount state transitions is needed for this test scenario. 44 class TestTask implements Runnable { 45 private String name; 46 private volatile boolean threadReady = false; 47 private volatile boolean shouldFinish = false; 48 49 // make thread with specific name 50 public TestTask(String name) { 51 this.name = name; 52 } 53 54 // run thread continuously 55 public void run() { 56 // run in a loop 57 threadReady = true; 58 System.out.println("# Started: " + name); 59 60 int i = 0; 61 int n = 1000; 62 while (!shouldFinish) { 63 if (n <= 0) { 64 n = 1000; 65 ToggleNotifyJvmtiTest.sleep(1); 66 } 67 if (i > n) { 68 i = 0; 69 n = n - 1; 70 } 71 i = i + 1; 72 } 73 } 74 75 // ensure thread is ready 76 public void ensureReady() { 77 while (!threadReady) { 78 ToggleNotifyJvmtiTest.sleep(1); 79 } 80 } 81 82 public void letFinish() { 83 shouldFinish = true; 84 } 85 } 86 87 /* 88 * The testing scenario consists of a number of serialized test cycles. 89 * Each cycle has initially zero virtual threads and the following steps: 90 * - disable notifyJvmti events mode 91 * - start N virtual threads 92 * - enable notifyJvmti events mode 93 * - shut the virtual threads down 94 * The JVMTI agent is loaded at a start-up or at a dynamic attach. 95 * It collects events: 96 * - VirtualThreadStart, VirtualThreadEnd, ThreadStart and ThreadEnd 97 */ 98 public class ToggleNotifyJvmtiTest { 99 private static final int VTHREADS_CNT = 20; 100 private static final String AGENT_LIB = "ToggleNotifyJvmtiTest"; 101 private static final WhiteBox WB = WhiteBox.getWhiteBox(); 102 103 private static native boolean IsAgentStarted(); 104 private static native int VirtualThreadStartedCount(); 105 private static native int VirtualThreadEndedCount(); 106 private static native int ThreadStartedCount(); 107 private static native int ThreadEndedCount(); 108 109 static void log(String str) { System.out.println(str); } 110 111 static public void sleep(long millis) { 112 try { 113 Thread.sleep(millis); 114 } catch (InterruptedException e) { 115 throw new RuntimeException("Interruption in TestTask.sleep: \n\t" + e); 116 } 117 } 118 119 static TestTask[] tasks = new TestTask[VTHREADS_CNT]; 120 static Thread vthreads[] = new Thread[VTHREADS_CNT]; 121 122 static private void startVirtualThreads() { 123 log("\n# Java: Starting vthreads"); 124 for (int i = 0; i < VTHREADS_CNT; i++) { 125 String name = "TestTask" + i; 126 TestTask task = new TestTask(name); 127 vthreads[i] = Thread.ofVirtual().name(name).start(task); 128 tasks[i] = task; 129 } 130 } 131 132 static private void finishVirtualThreads() { 133 try { 134 for (int i = 0; i < VTHREADS_CNT; i++) { 135 tasks[i].ensureReady(); 136 tasks[i].letFinish(); 137 vthreads[i].join(); 138 } 139 } catch (InterruptedException e) { 140 throw new RuntimeException(e); 141 } 142 } 143 144 static private void setVirtualThreadsNotifyJvmtiMode(int iter, boolean enable) { 145 boolean status = WB.setVirtualThreadsNotifyJvmtiMode(enable); 146 if (!status) { 147 throw new RuntimeException("Java: failed to set VirtualThreadsNotifyJvmtiMode: " + enable); 148 } 149 log("\n# main: SetNotifyJvmtiEvents: #" + iter + " enable: " + enable); 150 } 151 152 // Accumulative results after each finished test cycle. 153 static private void printResults() { 154 log(" VirtualThreadStart events: " + VirtualThreadStartedCount()); 155 log(" VirtualThreadEnd events: " + VirtualThreadEndedCount()); 156 log(" ThreadStart events: " + ThreadStartedCount()); 157 log(" ThreadEnd events: " + ThreadEndedCount()); 158 } 159 160 static private void run_test_cycle(int iter) throws Exception { 161 log("\n# Java: Started test cycle #" + iter); 162 163 // Disable notifyJvmti events mode at test cycle start. 164 // It is unsafe to do so if any virtual threads are executed. 165 setVirtualThreadsNotifyJvmtiMode(iter, false); 166 167 startVirtualThreads(); 168 169 // We want this somewhere in the middle of virtual threads execution. 170 setVirtualThreadsNotifyJvmtiMode(iter, true); 171 172 finishVirtualThreads(); 173 174 log("\n# Java: Finished test cycle #" + iter); 175 printResults(); 176 } 177 178 public static void main(String[] args) throws Exception { 179 log("# main: loading " + AGENT_LIB + " lib"); 180 181 if (args.length > 0 && args[0].equals("attach")) { // agent loaded into running VM case 182 String arg = args.length == 2 ? args[1] : ""; 183 VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid())); 184 vm.loadAgentLibrary(AGENT_LIB, arg); 185 } 186 int waitCount = 0; 187 while (!IsAgentStarted()) { 188 log("# main: waiting for native agent to start: #" + waitCount++); 189 sleep(20); 190 } 191 192 // The testing scenario consists of a number of sequential testing cycles. 193 for (int iter = 0; iter < 10; iter++) { 194 run_test_cycle(iter); 195 } 196 } 197 }