2023-04-04 07:33:21 +00:00

199 lines
7.0 KiB
Java

/*
* Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/**
* @test
* @summary Verifies JVMTI works for agents loaded into running VM
* @requires vm.jvmti
* @requires vm.continuations
* @enablePreview
* @library /test/lib /test/hotspot/jtreg
* @build jdk.test.whitebox.WhiteBox
*
* @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox
* @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -agentlib:ToggleNotifyJvmtiTest ToggleNotifyJvmtiTest
* @run main/othervm/native -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -Xbootclasspath/a:. -Djdk.attach.allowAttachSelf=true ToggleNotifyJvmtiTest attach
*/
import com.sun.tools.attach.VirtualMachine;
import java.util.concurrent.ThreadFactory;
import jdk.test.whitebox.WhiteBox;
// The TestTask mimics some thread activity, but it is important
// to have sleep() calls to provide yielding as some frequency of virtual
// thread mount state transitions is needed for this test scenario.
class TestTask implements Runnable {
private String name;
private volatile boolean threadReady = false;
private volatile boolean shouldFinish = false;
// make thread with specific name
public TestTask(String name) {
this.name = name;
}
// run thread continuously
public void run() {
// run in a loop
threadReady = true;
System.out.println("# Started: " + name);
int i = 0;
int n = 1000;
while (!shouldFinish) {
if (n <= 0) {
n = 1000;
ToggleNotifyJvmtiTest.sleep(1);
}
if (i > n) {
i = 0;
n = n - 1;
}
i = i + 1;
}
}
// ensure thread is ready
public void ensureReady() {
while (!threadReady) {
ToggleNotifyJvmtiTest.sleep(1);
}
}
public void letFinish() {
shouldFinish = true;
}
}
/*
* The testing scenario consists of a number of serialized test cycles.
* Each cycle has initially zero virtual threads and the following steps:
* - disable notifyJvmti events mode
* - start N virtual threads
* - enable notifyJvmti events mode
* - shut the virtual threads down
* The JVMTI agent is loaded at a start-up or at a dynamic attach.
* It collects events:
* - VirtualThreadStart, VirtualThreadEnd, ThreadStart and ThreadEnd
*/
public class ToggleNotifyJvmtiTest {
private static final int VTHREADS_CNT = 20;
private static final String AGENT_LIB = "ToggleNotifyJvmtiTest";
private static final WhiteBox WB = WhiteBox.getWhiteBox();
private static native boolean IsAgentStarted();
private static native int VirtualThreadStartedCount();
private static native int VirtualThreadEndedCount();
private static native int ThreadStartedCount();
private static native int ThreadEndedCount();
static void log(String str) { System.out.println(str); }
static public void sleep(long millis) {
try {
Thread.sleep(millis);
} catch (InterruptedException e) {
throw new RuntimeException("Interruption in TestTask.sleep: \n\t" + e);
}
}
static TestTask[] tasks = new TestTask[VTHREADS_CNT];
static Thread vthreads[] = new Thread[VTHREADS_CNT];
static private void startVirtualThreads() {
log("\n# Java: Starting vthreads");
for (int i = 0; i < VTHREADS_CNT; i++) {
String name = "TestTask" + i;
TestTask task = new TestTask(name);
vthreads[i] = Thread.ofVirtual().name(name).start(task);
tasks[i] = task;
}
}
static private void finishVirtualThreads() {
try {
for (int i = 0; i < VTHREADS_CNT; i++) {
tasks[i].ensureReady();
tasks[i].letFinish();
vthreads[i].join();
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
static private void setVirtualThreadsNotifyJvmtiMode(int iter, boolean enable) {
boolean status = WB.setVirtualThreadsNotifyJvmtiMode(enable);
if (!status) {
throw new RuntimeException("Java: failed to set VirtualThreadsNotifyJvmtiMode: " + enable);
}
log("\n# main: SetNotifyJvmtiEvents: #" + iter + " enable: " + enable);
}
// Accumulative results after each finished test cycle.
static private void printResults() {
log(" VirtualThreadStart events: " + VirtualThreadStartedCount());
log(" VirtualThreadEnd events: " + VirtualThreadEndedCount());
log(" ThreadStart events: " + ThreadStartedCount());
log(" ThreadEnd events: " + ThreadEndedCount());
}
static private void run_test_cycle(int iter) throws Exception {
log("\n# Java: Started test cycle #" + iter);
// Disable notifyJvmti events mode at test cycle start.
// It is unsafe to do so if any virtual threads are executed.
setVirtualThreadsNotifyJvmtiMode(iter, false);
startVirtualThreads();
// We want this somewhere in the middle of virtual threads execution.
setVirtualThreadsNotifyJvmtiMode(iter, true);
finishVirtualThreads();
log("\n# Java: Finished test cycle #" + iter);
printResults();
}
public static void main(String[] args) throws Exception {
log("# main: loading " + AGENT_LIB + " lib");
if (args.length > 0 && args[0].equals("attach")) { // agent loaded into running VM case
String arg = args.length == 2 ? args[1] : "";
VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid()));
vm.loadAgentLibrary(AGENT_LIB, arg);
}
int waitCount = 0;
while (!IsAgentStarted()) {
log("# main: waiting for native agent to start: #" + waitCount++);
sleep(20);
}
// The testing scenario consists of a number of sequential testing cycles.
for (int iter = 0; iter < 10; iter++) {
run_test_cycle(iter);
}
}
}