ADDITIONAL SYSTEM INFORMATION :
Ubunu 22.10 x86_64
openjdk version "20-ea" 2023-03-21
OpenJDK Runtime Environment (build 20-ea+21-1545)
OpenJDK 64-Bit Server VM (build 20-ea+21-1545, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
The JVMTI ThreadStart is not generated for virtual threads when the agent was attached. When the agent is loaded at start up the events are generated as expected.
The can_support_virtual_threads capability is not available when attaching (with JVMTI_ERROR_NOT_AVAILABLE), so there are also no VirtualThreadStart events.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
With the attached sources and JAVA_HOME set to a JDK 20:
g++ -shared -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC VirtualThreadStartTest.cpp -o libVirtualThreadStartTest.so
$JAVA_HOME/bin/javac --enable-preview --release=20 VirtualThreadStartTest.java
LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -Djdk.attach.allowAttachSelf=true VirtualThreadStartTest attach
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test should see 15 ThreadStart events and exit with 0.
ACTUAL -
The test only sees 5 ThreadStart events and exit with 1.
It works as expected with
LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -agentlib:VirtualThreadStartTest VirtualThreadStartTest
---------- BEGIN SOURCE ----------
-- VirtualThreadStartTest.java ----------------------------------------------------------
import com.sun.tools.attach.VirtualMachine;
public class VirtualThreadStartTest {
private static native int getAndResetStartedThreads();
public static void main(String[] args) throws Exception {
if (args.length == 1 && args[0].equals("attach")) {
VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid()));
vm.loadAgentLibrary("VirtualThreadStartTest");
}
Thread.sleep(2000);
Thread.ofVirtual().start(() -> {}).join(); // start ForkJoinPool worker
getAndResetStartedThreads();
for (int i=0; i<10; i++) {
Thread.ofVirtual().start(() -> {}).join();
}
for (int i=0; i<5; i++) {
Thread.ofPlatform().start(() -> {}).join();
}
int startedThreads = getAndResetStartedThreads();
System.out.println("Thread start event count: " + startedThreads + ", expected: 15");
if (startedThreads != 15) {
System.out.println("Wrong count.");
System.exit(1);
}
}
}
-----------------------------------------------------------------------------------------
-- VirtualThreadStartTest.cpp -----------------------------------------------------------
#include <jvmti.h>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <mutex>
namespace {
jvmtiEnv *jvmti = nullptr;
int startedThreads = 0;
std::mutex startedMutex;
void checkJvmti(int code, const char* message) {
if (code != JVMTI_ERROR_NONE) {
printf("Error %s: %d\n", message, code);
abort();
}
}
void JNICALL testThreadStart(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) {
std::lock_guard<std::mutex> lock(startedMutex);
startedThreads++;
}
}
extern "C" JNIEXPORT jint JNICALL Java_VirtualThreadStartTest_getAndResetStartedThreads(JNIEnv* jni_env, jclass clazz) {
std::lock_guard<std::mutex> lock(startedMutex);
int result = startedThreads;
startedThreads = 0;
return result;
}
extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
if (vm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) {
printf("Could not initialize JVMTI\n");
abort();
}
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.ThreadStart = &testThreadStart;
checkJvmti(jvmti->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks)), "setting callbacks");
checkJvmti(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, nullptr), "enabling events");
return JVMTI_ERROR_NONE;
}
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
return Agent_OnLoad(vm, options, reserved);
}
-----------------------------------------------------------------------------------------
---------- END SOURCE ----------
FREQUENCY : always
Ubunu 22.10 x86_64
openjdk version "20-ea" 2023-03-21
OpenJDK Runtime Environment (build 20-ea+21-1545)
OpenJDK 64-Bit Server VM (build 20-ea+21-1545, mixed mode, sharing)
A DESCRIPTION OF THE PROBLEM :
The JVMTI ThreadStart is not generated for virtual threads when the agent was attached. When the agent is loaded at start up the events are generated as expected.
The can_support_virtual_threads capability is not available when attaching (with JVMTI_ERROR_NOT_AVAILABLE), so there are also no VirtualThreadStart events.
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
With the attached sources and JAVA_HOME set to a JDK 20:
g++ -shared -I$JAVA_HOME/include -I$JAVA_HOME/include/linux -fPIC VirtualThreadStartTest.cpp -o libVirtualThreadStartTest.so
$JAVA_HOME/bin/javac --enable-preview --release=20 VirtualThreadStartTest.java
LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -Djdk.attach.allowAttachSelf=true VirtualThreadStartTest attach
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test should see 15 ThreadStart events and exit with 0.
ACTUAL -
The test only sees 5 ThreadStart events and exit with 1.
It works as expected with
LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -agentlib:VirtualThreadStartTest VirtualThreadStartTest
---------- BEGIN SOURCE ----------
-- VirtualThreadStartTest.java ----------------------------------------------------------
import com.sun.tools.attach.VirtualMachine;
public class VirtualThreadStartTest {
private static native int getAndResetStartedThreads();
public static void main(String[] args) throws Exception {
if (args.length == 1 && args[0].equals("attach")) {
VirtualMachine vm = VirtualMachine.attach(String.valueOf(ProcessHandle.current().pid()));
vm.loadAgentLibrary("VirtualThreadStartTest");
}
Thread.sleep(2000);
Thread.ofVirtual().start(() -> {}).join(); // start ForkJoinPool worker
getAndResetStartedThreads();
for (int i=0; i<10; i++) {
Thread.ofVirtual().start(() -> {}).join();
}
for (int i=0; i<5; i++) {
Thread.ofPlatform().start(() -> {}).join();
}
int startedThreads = getAndResetStartedThreads();
System.out.println("Thread start event count: " + startedThreads + ", expected: 15");
if (startedThreads != 15) {
System.out.println("Wrong count.");
System.exit(1);
}
}
}
-----------------------------------------------------------------------------------------
-- VirtualThreadStartTest.cpp -----------------------------------------------------------
#include <jvmti.h>
#include <cstdlib>
#include <cstring>
#include <vector>
#include <mutex>
namespace {
jvmtiEnv *jvmti = nullptr;
int startedThreads = 0;
std::mutex startedMutex;
void checkJvmti(int code, const char* message) {
if (code != JVMTI_ERROR_NONE) {
printf("Error %s: %d\n", message, code);
abort();
}
}
void JNICALL testThreadStart(jvmtiEnv *jvmti_env, JNIEnv* jni_env, jthread thread) {
std::lock_guard<std::mutex> lock(startedMutex);
startedThreads++;
}
}
extern "C" JNIEXPORT jint JNICALL Java_VirtualThreadStartTest_getAndResetStartedThreads(JNIEnv* jni_env, jclass clazz) {
std::lock_guard<std::mutex> lock(startedMutex);
int result = startedThreads;
startedThreads = 0;
return result;
}
extern "C" JNIEXPORT jint JNICALL Agent_OnLoad(JavaVM *vm, char *options, void *reserved) {
if (vm->GetEnv(reinterpret_cast<void **>(&jvmti), JVMTI_VERSION) != JNI_OK || !jvmti) {
printf("Could not initialize JVMTI\n");
abort();
}
jvmtiEventCallbacks callbacks;
memset(&callbacks, 0, sizeof(callbacks));
callbacks.ThreadStart = &testThreadStart;
checkJvmti(jvmti->SetEventCallbacks(&callbacks, (jint)sizeof(callbacks)), "setting callbacks");
checkJvmti(jvmti->SetEventNotificationMode(JVMTI_ENABLE, JVMTI_EVENT_THREAD_START, nullptr), "enabling events");
return JVMTI_ERROR_NONE;
}
extern "C" JNIEXPORT jint JNICALL Agent_OnAttach(JavaVM *vm, char *options, void *reserved) {
return Agent_OnLoad(vm, options, reserved);
}
-----------------------------------------------------------------------------------------
---------- END SOURCE ----------
FREQUENCY : always
- relates to
-
JDK-8296324 JVMTI GetStackTrace truncates vthread stack trace for agents loaded into running VM
-
- Resolved
-