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