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 }
--- EOF ---