Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8296323

JVMTI can_support_virtual_threads not available for agents loaded into running VM

XMLWordPrintable

    • Icon: Bug Bug
    • Resolution: Fixed
    • Icon: P4 P4
    • 20
    • 20
    • hotspot
    • b25
    • generic
    • generic

      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


            sspitsyn Serguei Spitsyn
            webbuggrp Webbug Group
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated:
              Resolved: