< prev index next >

src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c

Print this page
@@ -532,10 +532,69 @@
  }
  
  /* Garbage Collection Happened */
  static unsigned int garbageCollected = 0;
  
+ /*
+  * Run the event through each HandlerNode's filter, and if it passes, call the HandlerNode's
+  * HandlerFunction for the event, and then report all accumulated events to the debugger.
+  */
+ static void
+ filterAndHandleEvent(JNIEnv *env, EventInfo *evinfo, EventIndex ei,
+                      struct bag *eventBag, jbyte eventSessionID)
+ {
+     debugMonitorEnter(handlerLock);
+     {
+         HandlerNode *node;
+         char        *classname;
+ 
+         /* We must keep track of all classes prepared to know what's unloaded */
+         if (evinfo->ei == EI_CLASS_PREPARE) {
+             classTrack_addPreparedClass(env, evinfo->clazz);
+         }
+ 
+         node = getHandlerChain(ei)->first;
+         classname = getClassname(evinfo->clazz);
+ 
+         /* Filter the event over each handler node. */
+         while (node != NULL) {
+             /* Save next so handlers can remove themselves. */
+             HandlerNode *next = NEXT(node);
+             jboolean shouldDelete;
+ 
+             if (eventFilterRestricted_passesFilter(env, classname,
+                                                    evinfo, node,
+                                                    &shouldDelete)) {
+                 HandlerFunction func = HANDLER_FUNCTION(node);
+                 if (func == NULL) {
+                     EXIT_ERROR(AGENT_ERROR_INTERNAL,"handler function NULL");
+                 }
+                 /* Handle the event by calling the event handler. */
+                 (*func)(env, evinfo, node, eventBag);
+             }
+             if (shouldDelete) {
+                 /* We can safely free the node now that we are done using it. */
+                 (void)freeHandler(node);
+             }
+             node = next;
+         }
+         jvmtiDeallocate(classname);
+     }
+     debugMonitorExit(handlerLock);
+ 
+     /*
+      * The events destined for the debugger were accumulated in eventBag. Report all these events.
+      */
+     if (eventBag != NULL) {
+         reportEvents(env, eventSessionID, evinfo->thread, evinfo->ei,
+                      evinfo->clazz, evinfo->method, evinfo->location, eventBag);
+     }
+     // vthread fixme: if we didn't have any events to report, we should allow the vthread
+     // ThreadNode to be released at this point.
+ 
+ }
+ 
  /*
   * The JVMTI generic event callback. Each event is passed to a sequence of
   * handlers in a chain until the chain ends or one handler
   * consumes the event.
   */

@@ -595,10 +654,14 @@
          }
      }
  
      thread = evinfo->thread;
      if (thread != NULL) {
+         if (gdata->vthreadsSupported) {
+             evinfo->is_vthread = isVThread(thread);
+         }
+ 
          /*
           * Record the fact that we're entering an event
           * handler so that thread operations (status, interrupt,
           * stop) can be done correctly and so that thread
           * resources can be allocated.  This must be done before

@@ -632,54 +695,39 @@
               */
              eventBag = NULL;  /* to shut up lint */
          }
      }
  
-     debugMonitorEnter(handlerLock);
-     {
-         HandlerNode *node;
-         char        *classname;
- 
-         /* We must keep track of all classes prepared to know what's unloaded */
-         if (evinfo->ei == EI_CLASS_PREPARE) {
-             classTrack_addPreparedClass(env, evinfo->clazz);
-         }
- 
-         node = getHandlerChain(evinfo->ei)->first;
-         classname = getClassname(evinfo->clazz);
- 
-         while (node != NULL) {
-             /* save next so handlers can remove themselves */
-             HandlerNode *next = NEXT(node);
-             jboolean shouldDelete;
- 
-             if (eventFilterRestricted_passesFilter(env, classname,
-                                                    evinfo, node,
-                                                    &shouldDelete)) {
-                 HandlerFunction func;
+     /* We want the vthread start/end events to mimic thread start/end events */
+     /* vthread fixme: We can remove this now because cbVThreadStart/End set to THREAD_START/END. */
+     if (ei == EI_VIRTUAL_THREAD_START) {
+         ei = EI_THREAD_START;
+         JDI_ASSERT(JNI_FALSE);
+     }
+     if (ei == EI_VIRTUAL_THREAD_END) {
+         ei = EI_THREAD_END;
+         JDI_ASSERT(JNI_FALSE);
+     }
  
-                 func = HANDLER_FUNCTION(node);
-                 if ( func == NULL ) {
-                     EXIT_ERROR(AGENT_ERROR_INTERNAL,"handler function NULL");
-                 }
-                 (*func)(env, evinfo, node, eventBag);
-             }
-             if (shouldDelete) {
-                 /* We can safely free the node now that we are done
-                  * using it.
-                  */
-                 (void)freeHandler(node);
-             }
-             node = next;
+     if (gdata->vthreadsSupported && gdata->trackAllVThreads) {
+         /* Add the vthread if we haven't added it before. */
+         if (evinfo->is_vthread && !threadControl_isKnownVThread(thread)) {
+             // vthread fixme: we can actually get rid of the threadControl_addVThread call now since
+             // the call to threadControl_onEventHandlerEntry above should always end up adding
+             // the vthread. Leave in place with an assert for now just to make sure.
+             JDI_ASSERT(threadControl_isKnownVThread(thread));
+             threadControl_addVThread(thread);
          }
-         jvmtiDeallocate(classname);
      }
-     debugMonitorExit(handlerLock);
  
-     if (eventBag != NULL) {
-         reportEvents(env, eventSessionID, thread, evinfo->ei,
-                 evinfo->clazz, evinfo->method, evinfo->location, eventBag);
+     if (ei == EI_THREAD_END && evinfo->is_vthread && !gdata->notifyVThreads) {
+         /*
+          * Skip this event since we are not notifying the debugger of vthread START/END
+          * events. Note special handling of EI_THREAD_START is in cbVthreadStart().
+          */
+     } else {
+         filterAndHandleEvent(env, evinfo, ei, eventBag, eventSessionID);
      }
  
      /* we are continuing after VMDeathEvent - now we are dead */
      if (evinfo->ei == EI_VM_DEATH) {
          gdata->vmDead = JNI_TRUE;

@@ -1298,10 +1346,82 @@
      debugLoop_sync();
  
      LOG_MISC(("END cbVMDeath"));
  }
  
+ /* Event callback for JVMTI_EVENT_VIRTUAL_THREAD_START */
+ static void JNICALL
+ cbVThreadStart(jvmtiEnv *jvmti_env, JNIEnv *env, jthread vthread)
+ {
+     EventInfo info;
+ 
+     LOG_CB(("cbVThreadStart: vthread=%p", vthread));
+     /*tty_message("cbVThreadStart: vthread=%p", vthread);*/
+     JDI_ASSERT(gdata->vthreadsSupported);
+ 
+     /*
+      * Now would be a good time to cache the ThreadGroup for vthreads (carrier threads)
+      * if we haven't already.
+      */
+     if (gdata->vthreadThreadGroup == NULL) {
+         jvmtiThreadInfo info;
+         jvmtiError error;
+ 
+         (void)memset(&info, 0, sizeof(info));
+         error = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadInfo)
+             (gdata->jvmti, vthread, &info);
+ 
+         if (error != JVMTI_ERROR_NONE) {
+             EXIT_ERROR(error, "could not get vthread ThreadGroup");
+         } else {
+             debugMonitorEnter(callbackBlock); /* vthread fixme: any monitor will do, but there probably is a better choice. */
+             /* Make sure there wasn't a race before setting. saveGlobalRef() will assert if the
+              * target is not NULL. */
+             if (gdata->vthreadThreadGroup == NULL) {
+                 saveGlobalRef(env, info.thread_group, &gdata->vthreadThreadGroup);
+             }
+             debugMonitorExit(callbackBlock);
+         }
+     }
+ 
+     /* Ignore VIRTUAL_THREAD_START events unless we are notifying the debugger of all vthreads or
+      * are tracking all vthreads. Note similar code for VIRTUAL_THREAD_END is in event_callback(). */
+     if (!gdata->notifyVThreads && !gdata->trackAllVThreads) {
+         return;
+     }
+ 
+     BEGIN_CALLBACK() {
+         (void)memset(&info,0,sizeof(info));
+         info.ei         = EI_THREAD_START;
+         info.thread     = vthread;
+         event_callback(env, &info);
+     } END_CALLBACK();
+ 
+     LOG_MISC(("END cbVThreadStart"));
+ }
+ 
+ /* Event callback for JVMTI_EVENT_VIRTUAL_THREAD_END */
+ static void JNICALL
+ cbVThreadEnd(jvmtiEnv *jvmti_env, JNIEnv *env, jthread vthread)
+ {
+ 
+     EventInfo info;
+ 
+     LOG_CB(("cbVThreadEnd: vthread=%p", vthread));
+     /*tty_message("cbVThreadEnd: vthread=%p", vthread);*/
+     JDI_ASSERT(gdata->vthreadsSupported);
+ 
+     BEGIN_CALLBACK() {
+         (void)memset(&info,0,sizeof(info));
+         info.ei         = EI_THREAD_END;
+         info.thread     = vthread;
+         event_callback(env, &info);
+     } END_CALLBACK();
+ 
+     LOG_MISC(("END cbVThreadEnd"));
+ }
+ 
  /**
   * Delete this handler (do not delete permanent handlers):
   * Deinsert handler from active list,
   * make it inactive, and free it's memory
   * Assumes handlerLock held.

@@ -1486,10 +1606,23 @@
      error = threadControl_setEventMode(JVMTI_ENABLE,
                                         EI_GC_FINISH, NULL);
      if (error != JVMTI_ERROR_NONE) {
          EXIT_ERROR(error,"Can't enable garbage collection finish events");
      }
+     /* Only enable vthread events if vthread support is enabled. */
+     if (gdata->vthreadsSupported) {
+         error = threadControl_setEventMode(JVMTI_ENABLE,
+                                            EI_VIRTUAL_THREAD_START, NULL);
+         if (error != JVMTI_ERROR_NONE) {
+             EXIT_ERROR(error,"Can't enable vthread start events");
+         }
+         error = threadControl_setEventMode(JVMTI_ENABLE,
+                                            EI_VIRTUAL_THREAD_END, NULL);
+         if (error != JVMTI_ERROR_NONE) {
+             EXIT_ERROR(error,"Can't enable vthread end events");
+         }
+     }
  
      (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks));
      /* Event callback for JVMTI_EVENT_SINGLE_STEP */
      gdata->callbacks.SingleStep                 = &cbSingleStep;
      /* Event callback for JVMTI_EVENT_BREAKPOINT */

@@ -1528,10 +1661,14 @@
      gdata->callbacks.VMInit                     = &cbVMInit;
      /* Event callback for JVMTI_EVENT_VM_DEATH */
      gdata->callbacks.VMDeath                    = &cbVMDeath;
      /* Event callback for JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
      gdata->callbacks.GarbageCollectionFinish    = &cbGarbageCollectionFinish;
+     /* Event callback for JVMTI_EVENT_VIRTUAL_THREAD_START */
+     gdata->callbacks.VirtualThreadStart         = &cbVThreadStart;
+     /* Event callback for JVMTI_EVENT_VIRTUAL_THREAD_END */
+     gdata->callbacks.VirtualThreadEnd           = &cbVThreadEnd;
  
      error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventCallbacks)
                  (gdata->jvmti, &(gdata->callbacks), sizeof(gdata->callbacks));
      if (error != JVMTI_ERROR_NONE) {
          EXIT_ERROR(error,"Can't set event callbacks");
< prev index next >