JDK-8299414 : JVMTI FollowReferences should support references from VirtualThread stack
  • Type: Bug
  • Component: hotspot
  • Sub-Component: jvmti
  • Affected Version: 19,20
  • Priority: P4
  • Status: Open
  • Resolution: Unresolved
  • OS: generic
  • CPU: generic
  • Submitted: 2022-12-23
  • Updated: 2023-01-07
The Version table provides details related to the release that this issue/RFE will be addressed.

Unresolved : Release in which this issue/RFE will be addressed.
Resolved: Release in which this issue/RFE has been resolved.
Fixed : Release in which this issue/RFE has been fixed. The release containing this fix may be available for download as an Early Access Release or a General Availability Release.

To download the current JDK release, click here.
JDK 21
21Unresolved
Related Reports
Relates :  
Description
ADDITIONAL SYSTEM INFORMATION :
Ubunu 22.10 x86_64 
openjdk version "20-ea" 2023-03-21
OpenJDK Runtime Environment (build 20-ea+29-2280)
OpenJDK 64-Bit Server VM (build 20-ea+29-2280, mixed mode, sharing)

A DESCRIPTION OF THE PROBLEM :
Objects only referenced by the stack of unmounted VirtualThreads are not visit in any way. Maybe this could be done as a new kind of instance reference of StackChunk or as a new synthetic root. This functionality would be necessary for a JVMTI-based heap dump.

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 VThreadStackRefTest.cpp -o libVThreadStackRefTest.so 

$JAVA_HOME/bin/javac --enable-preview --release=20 VThreadStackRefTest.java

LD_LIBRARY_PATH=. $JAVA_HOME/bin/java --enable-preview -agentlib:VThreadStackRefTest VThreadStackRefTest 

EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
The test should exit with 0.
ACTUAL -
The test shows "did not find expected references: VThreadReferenced: 0, PThreadReferenced: 1" and exits with 1.

---------- BEGIN SOURCE ----------
-- VThreadStackRefTest.java ----------------------------------------------------------- 
import java.util.concurrent.CountDownLatch;

public class VThreadStackRefTest {
    private static native int[] getReferenceCount(Class<?>... classes);

    public static void main(String[] args) throws InterruptedException {
        CountDownLatch dumpedLatch = new CountDownLatch(1);
        Thread vthread = Thread.ofVirtual().start(() -> {
            Object referenced = new VThreadReferenced();
            System.out.println(referenced.getClass());
            await(dumpedLatch);
            System.out.println(referenced.getClass());
        });
        Thread pthread = Thread.ofPlatform().start(() -> {
            Object referenced = new PThreadReferenced();
            System.out.println(referenced.getClass());
            await(dumpedLatch);
            System.out.println(referenced.getClass());
        });
        Thread.sleep(2000); // wait for reference and unmount
        int[] count = getReferenceCount(VThreadReferenced.class, PThreadReferenced.class);
        dumpedLatch.countDown();
        vthread.join();
        pthread.join();
        if (count[0] != 1 || count[1] != 1) {
            System.err.println("did not find expected references: VThreadReferenced: " + count[0] + ", PThreadReferenced: " + count[1]);
            System.exit(1);
        }
    }

    private static void await(CountDownLatch dumpedLatch) {
        try {
            dumpedLatch.await();
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
    }

    public static class VThreadReferenced {
    }
    public static class PThreadReferenced {
    }
}
--------------------------------------------------------------------------------------
-- VThreadStackRefTest.cpp -----------------------------------------------------------
#include <jvmti.h>
#include <cstdlib>
#include <cstring>

namespace {
    jvmtiEnv *jvmti = nullptr;

    void checkJvmti(int code, const char* message) {
        if (code != JVMTI_ERROR_NONE) {
            printf("Error %s: %d\n", message, code);
            abort();
        }
    }

    const int TAG_START = 100;
}

jint JNICALL testJvmtiHeapReferenceCallback(jvmtiHeapReferenceKind reference_kind, const jvmtiHeapReferenceInfo* reference_info,
    jlong class_tag, jlong referrer_class_tag, jlong size, jlong* tag_ptr, jlong* referrer_tag_ptr, jint length, void* user_data) {
    if (class_tag >= TAG_START) {
        ((jint*)user_data)[class_tag - TAG_START]++;
    }
    return JVMTI_VISIT_OBJECTS;
}

extern "C" JNIEXPORT jintArray JNICALL Java_VThreadStackRefTest_getReferenceCount(JNIEnv* env, jclass clazz, jobjectArray classes) {
    int classesCount = env->GetArrayLength(classes);
    for (int i=0; i<classesCount; i++) {
        jvmti->SetTag(env->GetObjectArrayElement(classes, i), TAG_START + i);
    }
    jint* counter = new jint[classesCount];
    jvmtiHeapCallbacks heapCallBacks;
    memset(&heapCallBacks, 0, sizeof(jvmtiHeapCallbacks));
    heapCallBacks.heap_reference_callback = testJvmtiHeapReferenceCallback;
    checkJvmti(jvmti->FollowReferences(0, nullptr, nullptr, &heapCallBacks, counter), "follow references");
    jintArray result = env->NewIntArray(classesCount);
    env->SetIntArrayRegion(result, 0, classesCount, counter);
    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();
    }
    jvmtiCapabilities capabilities;
    memset(&capabilities, 0, sizeof(capabilities));
    capabilities.can_tag_objects = 1;
    checkJvmti(jvmti->AddCapabilities(&capabilities), "adding capabilities");
    return JVMTI_ERROR_NONE;
}

--------------------------------------------------------------------------------------

---------- END SOURCE ----------

FREQUENCY : always



Comments
Targeted to 21 for now to keep it under radar.
05-01-2023

This is currently missing from the JVMTI support so FollowReference does not currently report stack local references from unmounted virtual threads.
29-12-2022