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 :
JVMTI GetStackTrace returns a truncated trace for the current thread (with nullptr for the jthread parameter) when the agent was attached. When the agent is loaded at start up the JVMTI trace matches Thread.getStackTrace.
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 VirtualStackTraceTest.cpp -o libVirtualStackTraceTest.so
$JAVA_HOME/bin/javac --enable-preview --release=20 VirtualStackTraceTest.java
LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -Djdk.attach.allowAttachSelf=true VirtualStackTraceTest attach
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test should show two identical stack traces and exit with 0.
ACTUAL -
The test shows a truncated stack trace, prints "Stack traces do not match" and exits with 1.
When started with
LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -agentlib:VirtualStackTraceTest VirtualStackTraceTest
it works as expected.
---------- BEGIN SOURCE ----------
-- VirtualStackTraceTest.java -----------------------------------------------------------
import com.sun.tools.attach.VirtualMachine;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.LockSupport;
public class VirtualStackTraceTest {
public static native String[] getStackTrace();
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("VirtualStackTraceTest");
}
Thread.sleep(2000);
Thread.ofVirtual().name("virtual-test").start(VirtualStackTraceTest::testVirtual).join();
}
private static void testVirtual() {
work();
}
private static void work() {
LockSupport.parkNanos(1000);
inner();
}
private static void inner() {
checkCurrentThread();
}
private static void checkCurrentThread() {
System.out.println("Stack trace for " + Thread.currentThread() + ": ");
var jvmtiStackTrace = List.of(getStackTrace());
var javaStackTrace = Arrays.stream(Thread.currentThread().getStackTrace()).map(StackTraceElement::getMethodName).toList();
System.out.println("JVM TI: " + jvmtiStackTrace);
System.out.println("Java : " + javaStackTrace);
if (!Objects.equals(jvmtiStackTrace, javaStackTrace)) {
System.out.println("Stack traces do not match");
System.exit(1);
}
}
}
-----------------------------------------------------------------------------------------
-- VirtualStackTraceTest.cpp ------------------------------------------------------------
#include <jvmti.h>
#include <cstdlib>
#include <cstring>
#include <vector>
namespace {
jvmtiEnv *jvmti = nullptr;
void checkJvmti(int code, const char* message) {
if (code != JVMTI_ERROR_NONE) {
printf("Error %s: %d\n", message, code);
abort();
}
}
}
extern "C" JNIEXPORT jobjectArray JNICALL Java_VirtualStackTraceTest_getStackTrace(JNIEnv* jni_env, jclass clazz) {
jvmtiFrameInfo frameInfo[50];
jint count;
checkJvmti(jvmti->GetStackTrace(nullptr, 0, 50, frameInfo, &count), "GetStackTrace");
std::vector<jobject> visibleFrames;
for (int frameIndex = 0; frameIndex < count; frameIndex++) {
jclass declaringClass = nullptr;
checkJvmti(jvmti->GetMethodDeclaringClass(frameInfo[frameIndex].method, &declaringClass), "GetMethodDeclaringClass");
if (declaringClass) {
char *clasSignature = nullptr, *methodName = nullptr;
checkJvmti(jvmti->GetClassSignature(declaringClass, &clasSignature, nullptr), "GetClassSignature");
checkJvmti(jvmti->GetMethodName(frameInfo[frameIndex].method, &methodName, nullptr, nullptr), "GetMethodName");
if (clasSignature && methodName) {
if (!strchr(clasSignature, '.')) {
visibleFrames.push_back(jni_env->NewStringUTF(methodName));
}
jvmti->Deallocate(reinterpret_cast<unsigned char*>(methodName));
jvmti->Deallocate(reinterpret_cast<unsigned char*>(clasSignature));
}
}
}
jobjectArray methodStrings = jni_env->NewObjectArray(visibleFrames.size(), jni_env->FindClass("java/lang/String"), nullptr);
if (methodStrings) {
for (int frameIndex = 0; frameIndex < visibleFrames.size(); frameIndex++) {
jni_env->SetObjectArrayElement(methodStrings, frameIndex, visibleFrames[frameIndex]);
}
}
return methodStrings;
}
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();
}
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 :
JVMTI GetStackTrace returns a truncated trace for the current thread (with nullptr for the jthread parameter) when the agent was attached. When the agent is loaded at start up the JVMTI trace matches Thread.getStackTrace.
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 VirtualStackTraceTest.cpp -o libVirtualStackTraceTest.so
$JAVA_HOME/bin/javac --enable-preview --release=20 VirtualStackTraceTest.java
LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -Djdk.attach.allowAttachSelf=true VirtualStackTraceTest attach
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test should show two identical stack traces and exit with 0.
ACTUAL -
The test shows a truncated stack trace, prints "Stack traces do not match" and exits with 1.
When started with
LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -agentlib:VirtualStackTraceTest VirtualStackTraceTest
it works as expected.
---------- BEGIN SOURCE ----------
-- VirtualStackTraceTest.java -----------------------------------------------------------
import com.sun.tools.attach.VirtualMachine;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.locks.LockSupport;
public class VirtualStackTraceTest {
public static native String[] getStackTrace();
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("VirtualStackTraceTest");
}
Thread.sleep(2000);
Thread.ofVirtual().name("virtual-test").start(VirtualStackTraceTest::testVirtual).join();
}
private static void testVirtual() {
work();
}
private static void work() {
LockSupport.parkNanos(1000);
inner();
}
private static void inner() {
checkCurrentThread();
}
private static void checkCurrentThread() {
System.out.println("Stack trace for " + Thread.currentThread() + ": ");
var jvmtiStackTrace = List.of(getStackTrace());
var javaStackTrace = Arrays.stream(Thread.currentThread().getStackTrace()).map(StackTraceElement::getMethodName).toList();
System.out.println("JVM TI: " + jvmtiStackTrace);
System.out.println("Java : " + javaStackTrace);
if (!Objects.equals(jvmtiStackTrace, javaStackTrace)) {
System.out.println("Stack traces do not match");
System.exit(1);
}
}
}
-----------------------------------------------------------------------------------------
-- VirtualStackTraceTest.cpp ------------------------------------------------------------
#include <jvmti.h>
#include <cstdlib>
#include <cstring>
#include <vector>
namespace {
jvmtiEnv *jvmti = nullptr;
void checkJvmti(int code, const char* message) {
if (code != JVMTI_ERROR_NONE) {
printf("Error %s: %d\n", message, code);
abort();
}
}
}
extern "C" JNIEXPORT jobjectArray JNICALL Java_VirtualStackTraceTest_getStackTrace(JNIEnv* jni_env, jclass clazz) {
jvmtiFrameInfo frameInfo[50];
jint count;
checkJvmti(jvmti->GetStackTrace(nullptr, 0, 50, frameInfo, &count), "GetStackTrace");
std::vector<jobject> visibleFrames;
for (int frameIndex = 0; frameIndex < count; frameIndex++) {
jclass declaringClass = nullptr;
checkJvmti(jvmti->GetMethodDeclaringClass(frameInfo[frameIndex].method, &declaringClass), "GetMethodDeclaringClass");
if (declaringClass) {
char *clasSignature = nullptr, *methodName = nullptr;
checkJvmti(jvmti->GetClassSignature(declaringClass, &clasSignature, nullptr), "GetClassSignature");
checkJvmti(jvmti->GetMethodName(frameInfo[frameIndex].method, &methodName, nullptr, nullptr), "GetMethodName");
if (clasSignature && methodName) {
if (!strchr(clasSignature, '.')) {
visibleFrames.push_back(jni_env->NewStringUTF(methodName));
}
jvmti->Deallocate(reinterpret_cast<unsigned char*>(methodName));
jvmti->Deallocate(reinterpret_cast<unsigned char*>(clasSignature));
}
}
}
jobjectArray methodStrings = jni_env->NewObjectArray(visibleFrames.size(), jni_env->FindClass("java/lang/String"), nullptr);
if (methodStrings) {
for (int frameIndex = 0; frameIndex < visibleFrames.size(); frameIndex++) {
jni_env->SetObjectArrayElement(methodStrings, frameIndex, visibleFrames[frameIndex]);
}
}
return methodStrings;
}
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();
}
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-8296323 JVMTI can_support_virtual_threads not available for agents loaded into running VM
-
- Resolved
-
-
JDK-8297286 runtime/vthread tests crashing after JDK-8296324
-
- Resolved
-
-
JDK-8298347 Several jvmti related jtreg tests fail with fastdebug build after JDK-8296324
-
- Closed
-