< prev index next >

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

Print this page

        

@@ -1,7 +1,7 @@
 /*
- * Copyright (c) 2001, 2013, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2001, 2019, Oracle and/or its affiliates. All rights reserved.
  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  *
  * This code is free software; you can redistribute it and/or modify it
  * under the terms of the GNU General Public License version 2 only, as
  * published by the Free Software Foundation.  Oracle designates this

@@ -51,10 +51,11 @@
     jlocation location;
 } LocationFilter;
 
 typedef struct ThreadFilter {
     jthread thread;
+    jboolean is_fiber; /* true if the filter thread is actually a fiber. */
 } ThreadFilter;
 
 typedef struct CountFilter {
     jint count;
 } CountFilter;

@@ -80,10 +81,11 @@
 
 typedef struct StepFilter {
     jint size;
     jint depth;
     jthread thread;
+    jboolean is_fiber; /* true if the step filter thread is actually a fiber. */
 } StepFilter;
 
 typedef struct MatchFilter {
     char *classPattern;
 } MatchFilter;

@@ -357,38 +359,118 @@
 
     return object;
 }
 
 /*
+ * Return true if this an event that we prefer to deliver on the fiber if it arrived on a carrier thread.
+ */
+static jboolean
+preferDeliverEventOnFiber(EventIndex ei)
+{
+    /* Determine if this is an event that should be delivered on the fiber.*/
+    switch(ei) {
+        case EI_SINGLE_STEP:
+        case EI_BREAKPOINT:
+        case EI_EXCEPTION:
+        case EI_EXCEPTION_CATCH:
+        case EI_THREAD_START:
+        case EI_THREAD_END:
+        case EI_FIELD_ACCESS:
+        case EI_FIELD_MODIFICATION:
+        case EI_MONITOR_CONTENDED_ENTER:
+        case EI_MONITOR_CONTENDED_ENTERED:
+        case EI_FIBER_TERMINATED:
+        case EI_FIBER_SCHEDULED:
+            return JNI_TRUE;
+        /* Not delivering the following events on fibers helps keep down the number of
+         * fibers we need to notify the debugger about. */
+        case EI_CLASS_PREPARE:
+        case EI_FRAME_POP:
+        case EI_GC_FINISH:
+        case EI_CLASS_LOAD:
+        case EI_METHOD_ENTRY:
+        case EI_METHOD_EXIT:
+        case EI_MONITOR_WAIT:
+        case EI_MONITOR_WAITED:
+        case EI_VM_DEATH:
+            return gdata->notifyDebuggerOfAllFibers; /* Only deliver on fiber if notifying of all fibers. */
+        case EI_FIBER_MOUNT:        /* Not passed to event_callback(). */
+        case EI_FIBER_UNMOUNT:      /* Not passed to event_callback(). */
+        case EI_CONTINUATION_RUN:   /* Not passed to event_callback(). */
+        case EI_CONTINUATION_YIELD: /* Not passed to event_callback(). */
+            EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE, "invalid event index");
+            break;
+        default:
+            EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE, "unknown event index");
+            break;
+    }
+    return JNI_FALSE;
+}
+
+static jboolean
+matchesThreadOrFiber(JNIEnv* env,
+                     jthread thread, jthread fiber,
+                     jthread filterThread, jboolean filter_is_fiber,
+                     jboolean *matchesFiber)
+{
+    jboolean matchesThread = JNI_FALSE;
+    *matchesFiber = JNI_FALSE;
+
+    /*
+     * First check if it matches the fiber. If not, then check if it
+     * matches the thread. Only one of matchesFiber and matchesThread 
+     * will be set true, with the fiber check coming first. true is returned
+     * if either matches, false otherwise.
+     */
+    if (filter_is_fiber) {
+        *matchesFiber = isSameObject(env, fiber, filterThread);
+    } else {
+        matchesThread = isSameObject(env, thread, filterThread);
+    }
+    return matchesThread || *matchesFiber;
+}
+
+/*
  * Determine if this event is interesting to this handler.
  * Do so by checking each of the handler's filters.
  * Return false if any of the filters fail,
  * true if the handler wants this event.
  * Anyone modifying this function should check
  * eventFilterRestricted_passesUnloadFilter and
  * eventFilter_predictFiltering as well.
  *
+ * evinfo->matchesFiber will be set if the handler matched based on
+ * the fiber specified in the evinfo.
+ *
  * If shouldDelete is returned true, a count filter has expired
  * and the corresponding node should be deleted.
+ *
+ * If filterOnly is true, then we don't perform any actions that may
+ * change the state of the filter or the debugging state of the thread.
  */
 jboolean
 eventFilterRestricted_passesFilter(JNIEnv *env,
                                    char *classname,
                                    EventInfo *evinfo,
                                    HandlerNode *node,
-                                   jboolean *shouldDelete)
+                                   jboolean *shouldDelete,
+                                   jboolean filterOnly)
 {
     jthread thread;
+    jthread fiber;
     jclass clazz;
     jmethodID method;
     Filter *filter = FILTERS_ARRAY(node);
     int i;
+    jboolean mustDeliverEventOnFiber = JNI_FALSE;
 
     *shouldDelete = JNI_FALSE;
     thread = evinfo->thread;
+    fiber = evinfo->fiber;
     clazz = evinfo->clazz;
     method = evinfo->method;
+    evinfo->matchesFiber = fiber != NULL; /* Assume it matches the fiber. Will be cleared below if not. */
 
     /*
      * Suppress most events if they happen in debug threads
      */
     if ((evinfo->ei != EI_CLASS_PREPARE) &&

@@ -399,13 +481,16 @@
     }
 
     for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
         switch (filter->modifier) {
             case JDWP_REQUEST_MODIFIER(ThreadOnly):
-                if (!isSameObject(env, thread, filter->u.ThreadOnly.thread)) {
+                if (!matchesThreadOrFiber(env, thread, fiber,
+                                          filter->u.ThreadOnly.thread, filter->u.ThreadOnly.is_fiber,
+                                          &evinfo->matchesFiber)) {
                     return JNI_FALSE;
                 }
+                mustDeliverEventOnFiber = evinfo->matchesFiber;
                 break;
 
             case JDWP_REQUEST_MODIFIER(ClassOnly):
                 /* Class filters catch events in the specified
                  * class and any subclass/subinterface.

@@ -474,10 +559,18 @@
                 }
                 break;
             }
             case JDWP_REQUEST_MODIFIER(Count): {
                 JDI_ASSERT(filter->u.Count.count > 0);
+                if (filterOnly) {
+                    /* Don't decrement the counter. */
+                    if (filter->u.Count.count > 1) {
+                        return JNI_FALSE;
+                    } else {
+                        break;
+                    }
+                }
                 if (--filter->u.Count.count > 0) {
                     return JNI_FALSE;
                 }
                 *shouldDelete = JNI_TRUE;
                 break;

@@ -506,17 +599,28 @@
             }
             break;
         }
 
         case JDWP_REQUEST_MODIFIER(Step):
-                if (!isSameObject(env, thread, filter->u.Step.thread)) {
-                    return JNI_FALSE;
-                }
-                if (!stepControl_handleStep(env, thread, clazz, method)) {
+            if (!matchesThreadOrFiber(env, thread, fiber,
+                                      filter->u.Step.thread, filter->u.Step.is_fiber,
+                                      &evinfo->matchesFiber)) {
+                return JNI_FALSE;
+            }
+            mustDeliverEventOnFiber = evinfo->matchesFiber;
+            /*
+             * Don't call handleStep() if filterOnly is true. It's too complicated to see if the step
+             * would be completed without actually changing the state, so we just assume it will be.
+             * No harm can come from this since the fiber is already a known one, and that's the
+             * only reason this "filterOnly" request is being made.
+             */
+            if (!filterOnly) {
+                if (!stepControl_handleStep(env, thread, fiber, evinfo->matchesFiber, clazz, method)) {
                     return JNI_FALSE;
                 }
-                break;
+            }
+            break;
 
           case JDWP_REQUEST_MODIFIER(SourceNameMatch): {
               char* desiredNamePattern = filter->u.SourceNameOnly.sourceNamePattern;
               if (searchAllSourceNames(env, clazz,
                            desiredNamePattern) != 1) {

@@ -544,10 +648,14 @@
         default:
             EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"Invalid filter modifier");
             return JNI_FALSE;
         }
     }
+    /* Update matchesFiber based on whether or not we prefer to deliver this event on the fiber. */
+    if (evinfo->matchesFiber && !mustDeliverEventOnFiber) {
+        evinfo->matchesFiber = preferDeliverEventOnFiber(evinfo->ei);
+    }
     return JNI_TRUE;
 }
 
 /* Determine if this event is interesting to this handler.  Do so
  * by checking each of the handler's filters.  Return false if any

@@ -739,10 +847,13 @@
     }
     if (NODE_EI(node) == EI_GC_FINISH) {
         return AGENT_ERROR_ILLEGAL_ARGUMENT;
     }
 
+    /* The thread we are filtering on might be a fiber. */
+    filter->is_fiber = isFiber(thread);
+
     /* Create a thread ref that will live beyond */
     /* the end of this call */
     saveGlobalRef(env, thread, &(filter->thread));
     FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(ThreadOnly);
     return JVMTI_ERROR_NONE;

@@ -927,10 +1038,13 @@
     }
     if (NODE_EI(node) != EI_SINGLE_STEP) {
         return AGENT_ERROR_ILLEGAL_ARGUMENT;
     }
 
+    /* The thread we are filtering on might be a fiber. */
+    filter->is_fiber = isFiber(thread);
+
     /* Create a thread ref that will live beyond */
     /* the end of this call */
     saveGlobalRef(env, thread, &(filter->thread));
     error = stepControl_beginStep(env, filter->thread, size, depth, node);
     if (error != JVMTI_ERROR_NONE) {

@@ -941,10 +1055,30 @@
     filter->depth = depth;
     filter->size = size;
     return JVMTI_ERROR_NONE;
 }
 
+/*
+ * Finds the step filter in a node, and sets the thread for that filter.
+ * fiber fixme: not used. delete once we know for sure we'll never need it.
+ */
+void
+eventFilter_setStepFilterThread(HandlerNode *node, jthread thread)
+{
+    JNIEnv *env = getEnv();
+    Filter *filter = FILTERS_ARRAY(node);
+    int i;
+    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
+        switch (filter->modifier) {
+          case JDWP_REQUEST_MODIFIER(Step):
+            tossGlobalRef(env, &(filter->u.Step.thread));
+            saveGlobalRef(env, thread, &(filter->u.Step.thread));
+            return;
+        }
+    }
+    JDI_ASSERT(JNI_FALSE); /* We should have found a step filter, but didn't. */
+}
 
 jvmtiError
 eventFilter_setSourceNameMatchFilter(HandlerNode *node,
                                     jint index,
                                     char *sourceNamePattern) {

@@ -1240,10 +1374,16 @@
         case EI_THREAD_END:
         case EI_VM_INIT:
         case EI_VM_DEATH:
         case EI_CLASS_PREPARE:
         case EI_GC_FINISH:
+        case EI_FIBER_SCHEDULED:
+        case EI_FIBER_TERMINATED:
+        case EI_FIBER_MOUNT:
+        case EI_FIBER_UNMOUNT:
+        case EI_CONTINUATION_RUN:
+        case EI_CONTINUATION_YIELD:
             return error;
 
         case EI_FIELD_ACCESS:
         case EI_FIELD_MODIFICATION:
             error = setWatchpoint(node);

@@ -1299,10 +1439,16 @@
         case EI_THREAD_END:
         case EI_VM_INIT:
         case EI_VM_DEATH:
         case EI_CLASS_PREPARE:
         case EI_GC_FINISH:
+        case EI_FIBER_SCHEDULED:
+        case EI_FIBER_TERMINATED:
+        case EI_FIBER_MOUNT:
+        case EI_FIBER_UNMOUNT:
+        case EI_CONTINUATION_RUN:
+        case EI_CONTINUATION_YIELD:
             return error;
 
         case EI_FIELD_ACCESS:
         case EI_FIELD_MODIFICATION:
             error = clearWatchpoint(node);

@@ -1329,10 +1475,95 @@
                                             NODE_EI(node), thread);
     }
     return error != JVMTI_ERROR_NONE? error : error2;
 }
 
+/***** debugging *****/
+
+void
+eventFilter_dumpHandlerFilters(HandlerNode *node)
+{
+    int i;
+    Filter *filter = FILTERS_ARRAY(node);
+
+    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
+        switch (filter->modifier) {
+            case JDWP_REQUEST_MODIFIER(ThreadOnly):
+                tty_message("ThreadOnly: thread(%p) is_fiber(%d)",
+                            filter->u.ThreadOnly.thread,
+                            filter->u.ThreadOnly.is_fiber);
+                break;
+            case JDWP_REQUEST_MODIFIER(ClassOnly): {
+                char *class_name;
+                classSignature(filter->u.ClassOnly.clazz, &class_name, NULL);
+                tty_message("ClassOnly: clazz(%s)",
+                            class_name);
+                break;
+            }
+            case JDWP_REQUEST_MODIFIER(LocationOnly): {
+                char *method_name;
+                char *class_name;
+                methodSignature(filter->u.LocationOnly.method, &method_name, NULL, NULL);
+                classSignature(filter->u.LocationOnly.clazz, &class_name, NULL);
+                tty_message("LocationOnly: clazz(%s), method(%s) location(%d)",
+                            class_name,
+                            method_name,
+                            filter->u.LocationOnly.location);
+                break;
+            }
+            case JDWP_REQUEST_MODIFIER(FieldOnly): {
+                char *class_name;
+                classSignature(filter->u.FieldOnly.clazz, &class_name, NULL);
+                tty_message("FieldOnly: clazz(%p), field(%d)",
+                            class_name,
+                            filter->u.FieldOnly.field);
+                break;
+            }
+            case JDWP_REQUEST_MODIFIER(ExceptionOnly):
+                tty_message("ExceptionOnly: clazz(%p), caught(%d) uncaught(%d)",
+                            filter->u.ExceptionOnly.exception,
+                            filter->u.ExceptionOnly.caught,
+                            filter->u.ExceptionOnly.uncaught);
+                break;
+            case JDWP_REQUEST_MODIFIER(InstanceOnly):
+                tty_message("InstanceOnly: instance(%p)",
+                            filter->u.InstanceOnly.instance);
+                break;
+            case JDWP_REQUEST_MODIFIER(Count):
+                tty_message("Count: count(%d)",
+                            filter->u.Count.count);
+                break;
+            case JDWP_REQUEST_MODIFIER(Conditional):
+                tty_message("Conditional: exprID(%d)",
+                            filter->u.Conditional.exprID);
+                break;
+            case JDWP_REQUEST_MODIFIER(ClassMatch):
+                tty_message("ClassMatch: classPattern(%s)",
+                            filter->u.ClassMatch.classPattern);
+                break;
+            case JDWP_REQUEST_MODIFIER(ClassExclude):
+                tty_message("ClassExclude: classPattern(%s)",
+                            filter->u.ClassExclude.classPattern);
+                break;
+            case JDWP_REQUEST_MODIFIER(Step):
+                tty_message("Step: size(%d) depth(%d) thread(%p) is_fiber(%d)",
+                            filter->u.Step.size,
+                            filter->u.Step.depth,
+                            filter->u.Step.thread,
+                            filter->u.Step.is_fiber);
+                break;
+            case JDWP_REQUEST_MODIFIER(SourceNameMatch): 
+                tty_message("SourceNameMatch: sourceNamePattern(%s)",
+                            filter->u.SourceNameOnly.sourceNamePattern);
+                break;
+            default:
+                EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT, "Invalid filter modifier");
+                return;
+        }
+    }
+}
+
 
 /***** filter (and event) installation and deinstallation *****/
 
 /**
  * Make the set of event filters that correspond with this
< prev index next >