jdk/src/share/back/eventFilter.c
changeset 2 90ce3da70b43
child 828 ad3f54bd6ae8
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/back/eventFilter.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1324 @@
+/*
+ * Copyright 2001-2005 Sun Microsystems, Inc.  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.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+/*
+ * eventFilter
+ *
+ * This module handles event filteration and the enabling/disabling
+ * of the corresponding events. Used for filters on JDI EventRequests
+ * and also internal requests.  Our data is in a private hidden section
+ * of the HandlerNode's data.  See comment for enclosing
+ * module eventHandler.
+ */
+
+#include "util.h"
+#include "eventFilter.h"
+#include "eventFilterRestricted.h"
+#include "eventHandlerRestricted.h"
+#include "stepControl.h"
+#include "threadControl.h"
+#include "SDE.h"
+
+typedef struct ClassFilter {
+    jclass clazz;
+} ClassFilter;
+
+typedef struct LocationFilter {
+    jclass clazz;
+    jmethodID method;
+    jlocation location;
+} LocationFilter;
+
+typedef struct ThreadFilter {
+    jthread thread;
+} ThreadFilter;
+
+typedef struct CountFilter {
+    jint count;
+} CountFilter;
+
+typedef struct ConditionalFilter {
+    jint exprID;
+} ConditionalFilter;
+
+typedef struct FieldFilter {
+    jclass clazz;
+    jfieldID field;
+} FieldFilter;
+
+typedef struct ExceptionFilter {
+    jclass exception;
+    jboolean caught;
+    jboolean uncaught;
+} ExceptionFilter;
+
+typedef struct InstanceFilter {
+    jobject instance;
+} InstanceFilter;
+
+typedef struct StepFilter {
+    jint size;
+    jint depth;
+    jthread thread;
+} StepFilter;
+
+typedef struct MatchFilter {
+    char *classPattern;
+} MatchFilter;
+
+typedef struct SourceNameFilter {
+    char *sourceNamePattern;
+} SourceNameFilter;
+
+typedef struct Filter_ {
+    jbyte modifier;
+    union {
+        struct ClassFilter ClassOnly;
+        struct LocationFilter LocationOnly;
+        struct ThreadFilter ThreadOnly;
+        struct CountFilter Count;
+        struct ConditionalFilter Conditional;
+        struct FieldFilter FieldOnly;
+        struct ExceptionFilter ExceptionOnly;
+        struct InstanceFilter InstanceOnly;
+        struct StepFilter Step;
+        struct MatchFilter ClassMatch;
+        struct MatchFilter ClassExclude;
+        struct SourceNameFilter SourceNameOnly;
+    } u;
+} Filter;
+
+/* The filters array is allocated to the specified filterCount.
+ * Theoretically, some compiler could do range checking on this
+ * array - so, we define it to have a ludicrously large size so
+ * that this range checking won't get upset.
+ *
+ * The actual allocated number of bytes is computed using the
+ * offset of "filters" and so is not effected by this number.
+ */
+#define MAX_FILTERS 10000
+
+typedef struct EventFilters_ {
+    jint filterCount;
+    Filter filters[MAX_FILTERS];
+} EventFilters;
+
+typedef struct EventFilterPrivate_HandlerNode_ {
+    EventHandlerRestricted_HandlerNode   not_for_us;
+    EventFilters                         ef;
+} EventFilterPrivate_HandlerNode;
+
+/**
+ * The following macros extract filter info (EventFilters) from private
+ * data at the end of a HandlerNode
+ */
+#define EVENT_FILTERS(node) (&(((EventFilterPrivate_HandlerNode*)(void*)node)->ef))
+#define FILTER_COUNT(node)  (EVENT_FILTERS(node)->filterCount)
+#define FILTERS_ARRAY(node) (EVENT_FILTERS(node)->filters)
+#define FILTER(node,index)  ((FILTERS_ARRAY(node))[index])
+#define NODE_EI(node)          (node->ei)
+
+/***** filter set-up / destruction *****/
+
+/**
+ * Allocate a HandlerNode.
+ * We do it because eventHandler doesn't know how big to make it.
+ */
+HandlerNode *
+eventFilterRestricted_alloc(jint filterCount)
+{
+    /*LINTED*/
+    size_t size = offsetof(EventFilterPrivate_HandlerNode, ef) +
+                  offsetof(EventFilters, filters) +
+                  (filterCount * (int)sizeof(Filter));
+    HandlerNode *node = jvmtiAllocate((jint)size);
+
+    if (node != NULL) {
+        int i;
+        Filter *filter;
+
+        (void)memset(node, 0, size);
+
+        FILTER_COUNT(node) = filterCount;
+
+        /* Initialize all modifiers
+         */
+        for (i = 0, filter = FILTERS_ARRAY(node);
+                                    i < filterCount;
+                                    i++, filter++) {
+            filter->modifier = JDWP_REQUEST_NONE;
+        }
+    }
+
+    return node;
+}
+
+/**
+ * Free up global refs held by the filter.
+ * free things up at the JNI level if needed.
+ */
+static jvmtiError
+clearFilters(HandlerNode *node)
+{
+    JNIEnv *env = getEnv();
+    jint i;
+    jvmtiError error = JVMTI_ERROR_NONE;
+    Filter *filter = FILTERS_ARRAY(node);
+
+    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
+        switch (filter->modifier) {
+            case JDWP_REQUEST_MODIFIER(ThreadOnly):
+                if ( filter->u.ThreadOnly.thread != NULL ) {
+                    tossGlobalRef(env, &(filter->u.ThreadOnly.thread));
+                }
+                break;
+            case JDWP_REQUEST_MODIFIER(LocationOnly):
+                tossGlobalRef(env, &(filter->u.LocationOnly.clazz));
+                break;
+            case JDWP_REQUEST_MODIFIER(FieldOnly):
+                tossGlobalRef(env, &(filter->u.FieldOnly.clazz));
+                break;
+            case JDWP_REQUEST_MODIFIER(ExceptionOnly):
+                if ( filter->u.ExceptionOnly.exception != NULL ) {
+                    tossGlobalRef(env, &(filter->u.ExceptionOnly.exception));
+                }
+                break;
+            case JDWP_REQUEST_MODIFIER(InstanceOnly):
+                if ( filter->u.InstanceOnly.instance != NULL ) {
+                    tossGlobalRef(env, &(filter->u.InstanceOnly.instance));
+                }
+                break;
+            case JDWP_REQUEST_MODIFIER(ClassOnly):
+                tossGlobalRef(env, &(filter->u.ClassOnly.clazz));
+                break;
+            case JDWP_REQUEST_MODIFIER(ClassMatch):
+                jvmtiDeallocate(filter->u.ClassMatch.classPattern);
+                break;
+            case JDWP_REQUEST_MODIFIER(ClassExclude):
+                jvmtiDeallocate(filter->u.ClassExclude.classPattern);
+                break;
+            case JDWP_REQUEST_MODIFIER(Step): {
+                jthread thread = filter->u.Step.thread;
+                error = stepControl_endStep(thread);
+                if (error == JVMTI_ERROR_NONE) {
+                    tossGlobalRef(env, &(filter->u.Step.thread));
+                }
+                break;
+            }
+        }
+    }
+    if (error == JVMTI_ERROR_NONE) {
+        FILTER_COUNT(node) = 0; /* blast so we don't clear again */
+    }
+
+    return error;
+}
+
+
+/***** filtering *****/
+
+/*
+ * Match a string against a wildcard
+ * string pattern.
+ */
+static jboolean
+patternStringMatch(char *classname, const char *pattern)
+{
+    int pattLen;
+    int compLen;
+    char *start;
+    int offset;
+
+    if ( pattern==NULL || classname==NULL ) {
+        return JNI_FALSE;
+    }
+    pattLen = (int)strlen(pattern);
+
+    if ((pattern[0] != '*') && (pattern[pattLen-1] != '*')) {
+        /* An exact match is required when there is no *: bug 4331522 */
+        return strcmp(pattern, classname) == 0;
+    } else {
+        compLen = pattLen - 1;
+        offset = (int)strlen(classname) - compLen;
+        if (offset < 0) {
+            return JNI_FALSE;
+        } else {
+            if (pattern[0] == '*') {
+                pattern++;
+                start = classname + offset;
+            }  else {
+                start = classname;
+            }
+            return strncmp(pattern, start, compLen) == 0;
+        }
+    }
+}
+
+/* Return the object instance in which the event occurred */
+/* Return NULL if static or if an error occurs */
+static jobject
+eventInstance(EventInfo *evinfo)
+{
+    jobject     object          = NULL;
+    jthread     thread          ;
+    jmethodID   method          ;
+    jint        modifiers       = 0;
+    jvmtiError  error;
+
+    switch (evinfo->ei) {
+        case EI_SINGLE_STEP:
+        case EI_BREAKPOINT:
+        case EI_FRAME_POP:
+        case EI_METHOD_ENTRY:
+        case EI_METHOD_EXIT:
+        case EI_EXCEPTION:
+        case EI_EXCEPTION_CATCH:
+        case EI_MONITOR_CONTENDED_ENTER:
+        case EI_MONITOR_CONTENDED_ENTERED:
+        case EI_MONITOR_WAIT:
+        case EI_MONITOR_WAITED:
+            thread      = evinfo->thread;
+            method      = evinfo->method;
+            break;
+        case EI_FIELD_ACCESS:
+        case EI_FIELD_MODIFICATION:
+            object = evinfo->object;
+            return object;
+        default:
+            return object; /* NULL */
+    }
+
+    error = methodModifiers(method, &modifiers);
+
+    /* fail if error or static (0x8) */
+    if (error == JVMTI_ERROR_NONE && thread!=NULL && (modifiers & 0x8) == 0) {
+        FrameNumber fnum            = 0;
+        /* get slot zero object "this" */
+        error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalObject)
+                    (gdata->jvmti, thread, fnum, 0, &object);
+        if (error != JVMTI_ERROR_NONE)
+            object = NULL;
+    }
+
+    return object;
+}
+
+/*
+ * 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.
+ *
+ * If shouldDelete is returned true, a count filter has expired
+ * and the corresponding node should be deleted.
+ */
+jboolean
+eventFilterRestricted_passesFilter(JNIEnv *env,
+                                   char *classname,
+                                   EventInfo *evinfo,
+                                   HandlerNode *node,
+                                   jboolean *shouldDelete)
+{
+    jthread thread;
+    jclass clazz;
+    jmethodID method;
+    Filter *filter = FILTERS_ARRAY(node);
+    int i;
+
+    *shouldDelete = JNI_FALSE;
+    thread = evinfo->thread;
+    clazz = evinfo->clazz;
+    method = evinfo->method;
+
+    /*
+     * Suppress most events if they happen in debug threads
+     */
+    if ((evinfo->ei != EI_CLASS_PREPARE) &&
+        (evinfo->ei != EI_GC_FINISH) &&
+        (evinfo->ei != EI_CLASS_LOAD) &&
+        threadControl_isDebugThread(thread)) {
+        return JNI_FALSE;
+    }
+
+    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)) {
+                    return JNI_FALSE;
+                }
+                break;
+
+            case JDWP_REQUEST_MODIFIER(ClassOnly):
+                /* Class filters catch events in the specified
+                 * class and any subclass/subinterface.
+                 */
+                if (!JNI_FUNC_PTR(env,IsAssignableFrom)(env, clazz,
+                               filter->u.ClassOnly.clazz)) {
+                    return JNI_FALSE;
+                }
+                break;
+
+            /* This is kinda cheating assumming the event
+             * fields will be in the same locations, but it is
+             * true now.
+             */
+            case JDWP_REQUEST_MODIFIER(LocationOnly):
+                if  (evinfo->method !=
+                          filter->u.LocationOnly.method ||
+                     evinfo->location !=
+                          filter->u.LocationOnly.location ||
+                     !isSameObject(env, clazz, filter->u.LocationOnly.clazz)) {
+                    return JNI_FALSE;
+                }
+                break;
+
+            case JDWP_REQUEST_MODIFIER(FieldOnly):
+                /* Field watchpoints can be triggered from the
+                 * declared class or any subclass/subinterface.
+                 */
+                if ((evinfo->u.field_access.field !=
+                     filter->u.FieldOnly.field) ||
+                    !isSameObject(env, evinfo->u.field_access.field_clazz,
+                               filter->u.FieldOnly.clazz)) {
+                    return JNI_FALSE;
+                }
+                break;
+
+            case JDWP_REQUEST_MODIFIER(ExceptionOnly):
+                /* do we want caught/uncaught exceptions */
+                if (!((evinfo->u.exception.catch_clazz == NULL)?
+                      filter->u.ExceptionOnly.uncaught :
+                      filter->u.ExceptionOnly.caught)) {
+                    return JNI_FALSE;
+                }
+
+                /* do we care about exception class */
+                if (filter->u.ExceptionOnly.exception != NULL) {
+                    jclass exception = evinfo->object;
+
+                    /* do we want this exception class */
+                    if (!JNI_FUNC_PTR(env,IsInstanceOf)(env, exception,
+                            filter->u.ExceptionOnly.exception)) {
+                        return JNI_FALSE;
+                    }
+                }
+                break;
+
+            case JDWP_REQUEST_MODIFIER(InstanceOnly): {
+                jobject eventInst = eventInstance(evinfo);
+                jobject filterInst = filter->u.InstanceOnly.instance;
+                /* if no error and doesn't match, don't pass
+                 * filter
+                 */
+                if (eventInst != NULL &&
+                      !isSameObject(env, eventInst, filterInst)) {
+                    return JNI_FALSE;
+                }
+                break;
+            }
+            case JDWP_REQUEST_MODIFIER(Count): {
+                JDI_ASSERT(filter->u.Count.count > 0);
+                if (--filter->u.Count.count > 0) {
+                    return JNI_FALSE;
+                }
+                *shouldDelete = JNI_TRUE;
+                break;
+            }
+
+            case JDWP_REQUEST_MODIFIER(Conditional):
+/***
+                if (...  filter->u.Conditional.exprID ...) {
+                    return JNI_FALSE;
+                }
+***/
+                break;
+
+        case JDWP_REQUEST_MODIFIER(ClassMatch): {
+            if (!patternStringMatch(classname,
+                       filter->u.ClassMatch.classPattern)) {
+                return JNI_FALSE;
+            }
+            break;
+        }
+
+        case JDWP_REQUEST_MODIFIER(ClassExclude): {
+            if (patternStringMatch(classname,
+                      filter->u.ClassExclude.classPattern)) {
+                return JNI_FALSE;
+            }
+            break;
+        }
+
+        case JDWP_REQUEST_MODIFIER(Step):
+                if (!isSameObject(env, thread, filter->u.Step.thread)) {
+                    return JNI_FALSE;
+                }
+                if (!stepControl_handleStep(env, thread, clazz, method)) {
+                    return JNI_FALSE;
+                }
+                break;
+
+          case JDWP_REQUEST_MODIFIER(SourceNameMatch): {
+              char* desiredNamePattern = filter->u.SourceNameOnly.sourceNamePattern;
+              if (!searchAllSourceNames(env, clazz,
+                           desiredNamePattern) == 1) {
+                  /* The name isn't in the SDE; try the sourceName in the ref
+                   * type
+                   */
+                  char *sourceName = 0;
+                  jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,GetSourceFileName)
+                                            (gdata->jvmti, clazz, &sourceName);
+                  if (error == JVMTI_ERROR_NONE) {
+                      if (sourceName == 0 || !patternStringMatch(sourceName, desiredNamePattern)) {
+                        /* We have no match */
+                        jvmtiDeallocate(sourceName);
+                        return JNI_FALSE;
+                      }
+                  }
+                  jvmtiDeallocate(sourceName);
+              }
+              break;
+          }
+
+        default:
+            EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"Invalid filter modifier");
+            return JNI_FALSE;
+        }
+    }
+    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
+ * of the filters fail, true if the handler wants this event.
+ * Special version of filter for unloads since they don't have an
+ * event structure or a jclass.
+ *
+ * If shouldDelete is returned true, a count filter has expired
+ * and the corresponding node should be deleted.
+ */
+jboolean
+eventFilterRestricted_passesUnloadFilter(JNIEnv *env,
+                                         char *classname,
+                                         HandlerNode *node,
+                                         jboolean *shouldDelete)
+{
+    Filter *filter = FILTERS_ARRAY(node);
+    int i;
+
+    *shouldDelete = JNI_FALSE;
+    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
+        switch (filter->modifier) {
+
+            case JDWP_REQUEST_MODIFIER(Count): {
+                JDI_ASSERT(filter->u.Count.count > 0);
+                if (--filter->u.Count.count > 0) {
+                    return JNI_FALSE;
+                }
+                *shouldDelete = JNI_TRUE;
+                break;
+            }
+
+            case JDWP_REQUEST_MODIFIER(ClassMatch): {
+                if (!patternStringMatch(classname,
+                        filter->u.ClassMatch.classPattern)) {
+                    return JNI_FALSE;
+                }
+                break;
+            }
+
+            case JDWP_REQUEST_MODIFIER(ClassExclude): {
+                if (patternStringMatch(classname,
+                       filter->u.ClassExclude.classPattern)) {
+                    return JNI_FALSE;
+                }
+                break;
+            }
+
+            default:
+                EXIT_ERROR(AGENT_ERROR_ILLEGAL_ARGUMENT,"Invalid filter modifier");
+                return JNI_FALSE;
+        }
+    }
+    return JNI_TRUE;
+}
+
+/**
+ * This function returns true only if it is certain that
+ * all events for the given node in the given stack frame will
+ * be filtered. It is used to optimize stepping. (If this
+ * function returns true the stepping algorithm does not
+ * have to step through every instruction in this stack frame;
+ * instead, it can use more efficient method entry/exit
+ * events.
+ */
+jboolean
+eventFilter_predictFiltering(HandlerNode *node, jclass clazz, char *classname)
+{
+    JNIEnv     *env;
+    jboolean    willBeFiltered;
+    Filter     *filter;
+    jboolean    done;
+    int         count;
+    int         i;
+
+    willBeFiltered = JNI_FALSE;
+    env            = NULL;
+    filter         = FILTERS_ARRAY(node);
+    count          = FILTER_COUNT(node);
+    done           = JNI_FALSE;
+
+    for (i = 0; (i < count) && (!done); ++i, ++filter) {
+        switch (filter->modifier) {
+            case JDWP_REQUEST_MODIFIER(ClassOnly):
+                if ( env==NULL ) {
+                    env = getEnv();
+                }
+                if (!JNI_FUNC_PTR(env,IsAssignableFrom)(env, clazz,
+                                 filter->u.ClassOnly.clazz)) {
+                    willBeFiltered = JNI_TRUE;
+                    done = JNI_TRUE;
+                }
+                break;
+
+            case JDWP_REQUEST_MODIFIER(Count): {
+                /*
+                 * If preceeding filters have determined that events will
+                 * be filtered out, that is fine and we won't get here.
+                 * However, the count must be decremented - even if
+                 * subsequent filters will filter these events.  We
+                 * thus must end now unable to predict
+                 */
+                done = JNI_TRUE;
+                break;
+            }
+
+            case JDWP_REQUEST_MODIFIER(ClassMatch): {
+                if (!patternStringMatch(classname,
+                        filter->u.ClassMatch.classPattern)) {
+                    willBeFiltered = JNI_TRUE;
+                    done = JNI_TRUE;
+                }
+                break;
+            }
+
+            case JDWP_REQUEST_MODIFIER(ClassExclude): {
+                if (patternStringMatch(classname,
+                       filter->u.ClassExclude.classPattern)) {
+                    willBeFiltered = JNI_TRUE;
+                    done = JNI_TRUE;
+                }
+                break;
+            }
+        }
+    }
+
+    return willBeFiltered;
+}
+
+/**
+ * Determine if the given breakpoint node is in the specified class.
+ */
+jboolean
+eventFilterRestricted_isBreakpointInClass(JNIEnv *env, jclass clazz,
+                                          HandlerNode *node)
+{
+    Filter *filter = FILTERS_ARRAY(node);
+    int i;
+
+    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
+        switch (filter->modifier) {
+            case JDWP_REQUEST_MODIFIER(LocationOnly):
+                return isSameObject(env, clazz, filter->u.LocationOnly.clazz);
+        }
+    }
+    return JNI_TRUE; /* should never come here */
+}
+
+/***** filter set-up *****/
+
+jvmtiError
+eventFilter_setConditionalFilter(HandlerNode *node, jint index,
+                                 jint exprID)
+{
+    ConditionalFilter *filter = &FILTER(node, index).u.Conditional;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(Conditional);
+    filter->exprID = exprID;
+    return JVMTI_ERROR_NONE;
+}
+
+jvmtiError
+eventFilter_setCountFilter(HandlerNode *node, jint index,
+                           jint count)
+{
+    CountFilter *filter = &FILTER(node, index).u.Count;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if (count <= 0) {
+        return JDWP_ERROR(INVALID_COUNT);
+    } else {
+        FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(Count);
+        filter->count = count;
+        return JVMTI_ERROR_NONE;
+    }
+}
+
+jvmtiError
+eventFilter_setThreadOnlyFilter(HandlerNode *node, jint index,
+                                jthread thread)
+{
+    JNIEnv *env = getEnv();
+    ThreadFilter *filter = &FILTER(node, index).u.ThreadOnly;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if (NODE_EI(node) == EI_GC_FINISH) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    /* 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;
+}
+
+jvmtiError
+eventFilter_setLocationOnlyFilter(HandlerNode *node, jint index,
+                                  jclass clazz, jmethodID method,
+                                  jlocation location)
+{
+    JNIEnv *env = getEnv();
+    LocationFilter *filter = &FILTER(node, index).u.LocationOnly;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if ((NODE_EI(node) != EI_BREAKPOINT) &&
+        (NODE_EI(node) != EI_FIELD_ACCESS) &&
+        (NODE_EI(node) != EI_FIELD_MODIFICATION) &&
+        (NODE_EI(node) != EI_SINGLE_STEP) &&
+        (NODE_EI(node) != EI_EXCEPTION)) {
+
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    /* Create a class ref that will live beyond */
+    /* the end of this call */
+    saveGlobalRef(env, clazz, &(filter->clazz));
+    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(LocationOnly);
+    filter->method = method;
+    filter->location = location;
+    return JVMTI_ERROR_NONE;
+}
+
+jvmtiError
+eventFilter_setFieldOnlyFilter(HandlerNode *node, jint index,
+                               jclass clazz, jfieldID field)
+{
+    JNIEnv *env = getEnv();
+    FieldFilter *filter = &FILTER(node, index).u.FieldOnly;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if ((NODE_EI(node) != EI_FIELD_ACCESS) &&
+        (NODE_EI(node) != EI_FIELD_MODIFICATION)) {
+
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    /* Create a class ref that will live beyond */
+    /* the end of this call */
+    saveGlobalRef(env, clazz, &(filter->clazz));
+    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(FieldOnly);
+    filter->field = field;
+    return JVMTI_ERROR_NONE;
+}
+
+jvmtiError
+eventFilter_setClassOnlyFilter(HandlerNode *node, jint index,
+                               jclass clazz)
+{
+    JNIEnv *env = getEnv();
+    ClassFilter *filter = &FILTER(node, index).u.ClassOnly;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if (
+        (NODE_EI(node) == EI_GC_FINISH) ||
+        (NODE_EI(node) == EI_THREAD_START) ||
+        (NODE_EI(node) == EI_THREAD_END)) {
+
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    /* Create a class ref that will live beyond */
+    /* the end of this call */
+    saveGlobalRef(env, clazz, &(filter->clazz));
+    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(ClassOnly);
+    return JVMTI_ERROR_NONE;
+}
+
+jvmtiError
+eventFilter_setExceptionOnlyFilter(HandlerNode *node, jint index,
+                                   jclass exceptionClass,
+                                   jboolean caught,
+                                   jboolean uncaught)
+{
+    JNIEnv *env = getEnv();
+    ExceptionFilter *filter = &FILTER(node, index).u.ExceptionOnly;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if (NODE_EI(node) != EI_EXCEPTION) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    filter->exception = NULL;
+    if (exceptionClass != NULL) {
+        /* Create a class ref that will live beyond */
+        /* the end of this call */
+        saveGlobalRef(env, exceptionClass, &(filter->exception));
+    }
+    FILTER(node, index).modifier =
+                       JDWP_REQUEST_MODIFIER(ExceptionOnly);
+    filter->caught = caught;
+    filter->uncaught = uncaught;
+    return JVMTI_ERROR_NONE;
+}
+
+jvmtiError
+eventFilter_setInstanceOnlyFilter(HandlerNode *node, jint index,
+                                  jobject instance)
+{
+    JNIEnv *env = getEnv();
+    InstanceFilter *filter = &FILTER(node, index).u.InstanceOnly;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    filter->instance = NULL;
+    if (instance != NULL) {
+        /* Create an object ref that will live beyond
+         * the end of this call
+         */
+        saveGlobalRef(env, instance, &(filter->instance));
+    }
+    FILTER(node, index).modifier =
+                       JDWP_REQUEST_MODIFIER(InstanceOnly);
+    return JVMTI_ERROR_NONE;
+}
+
+jvmtiError
+eventFilter_setClassMatchFilter(HandlerNode *node, jint index,
+                                char *classPattern)
+{
+    MatchFilter *filter = &FILTER(node, index).u.ClassMatch;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if (
+        (NODE_EI(node) == EI_THREAD_START) ||
+        (NODE_EI(node) == EI_THREAD_END)) {
+
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    FILTER(node, index).modifier =
+                       JDWP_REQUEST_MODIFIER(ClassMatch);
+    filter->classPattern = classPattern;
+    return JVMTI_ERROR_NONE;
+}
+
+jvmtiError
+eventFilter_setClassExcludeFilter(HandlerNode *node, jint index,
+                                  char *classPattern)
+{
+    MatchFilter *filter = &FILTER(node, index).u.ClassExclude;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if (
+        (NODE_EI(node) == EI_THREAD_START) ||
+        (NODE_EI(node) == EI_THREAD_END)) {
+
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    FILTER(node, index).modifier =
+                       JDWP_REQUEST_MODIFIER(ClassExclude);
+    filter->classPattern = classPattern;
+    return JVMTI_ERROR_NONE;
+}
+
+jvmtiError
+eventFilter_setStepFilter(HandlerNode *node, jint index,
+                          jthread thread, jint size, jint depth)
+{
+    jvmtiError error;
+    JNIEnv *env = getEnv();
+    StepFilter *filter = &FILTER(node, index).u.Step;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if (NODE_EI(node) != EI_SINGLE_STEP) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    /* 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) {
+        tossGlobalRef(env, &(filter->thread));
+        return error;
+    }
+    FILTER(node, index).modifier = JDWP_REQUEST_MODIFIER(Step);
+    filter->depth = depth;
+    filter->size = size;
+    return JVMTI_ERROR_NONE;
+}
+
+
+jvmtiError
+eventFilter_setSourceNameMatchFilter(HandlerNode *node,
+                                    jint index,
+                                    char *sourceNamePattern) {
+    SourceNameFilter *filter = &FILTER(node, index).u.SourceNameOnly;
+    if (index >= FILTER_COUNT(node)) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+    if (NODE_EI(node) != EI_CLASS_PREPARE) {
+        return AGENT_ERROR_ILLEGAL_ARGUMENT;
+    }
+
+    FILTER(node, index).modifier =
+                       JDWP_REQUEST_MODIFIER(SourceNameMatch);
+    filter->sourceNamePattern = sourceNamePattern;
+    return JVMTI_ERROR_NONE;
+
+}
+
+/***** JVMTI event enabling / disabling *****/
+
+/**
+ * Return the Filter that is of the specified type (modifier).
+ * Return NULL if not found.
+ */
+static Filter *
+findFilter(HandlerNode *node, jint modifier)
+{
+    int i;
+    Filter *filter;
+    for (i = 0, filter = FILTERS_ARRAY(node);
+                      i <FILTER_COUNT(node);
+                      i++, filter++) {
+        if (filter->modifier == modifier) {
+            return filter;
+        }
+    }
+    return NULL;
+}
+
+/**
+ * Determine if the specified breakpoint node is in the
+ * same location as the LocationFilter passed in arg.
+ *
+ * This is a match function called by a
+ * eventHandlerRestricted_iterator invokation.
+ */
+static jboolean
+matchBreakpoint(JNIEnv *env, HandlerNode *node, void *arg)
+{
+    LocationFilter *goal = (LocationFilter *)arg;
+    Filter *filter = FILTERS_ARRAY(node);
+    int i;
+
+    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
+        switch (filter->modifier) {
+        case JDWP_REQUEST_MODIFIER(LocationOnly): {
+            LocationFilter *trial = &(filter->u.LocationOnly);
+            if (trial->method == goal->method &&
+                trial->location == goal->location &&
+                isSameObject(env, trial->clazz, goal->clazz)) {
+                return JNI_TRUE;
+            }
+        }
+        }
+    }
+    return JNI_FALSE;
+}
+
+/**
+ * Set a breakpoint if this is the first one at this location.
+ */
+static jvmtiError
+setBreakpoint(HandlerNode *node)
+{
+    jvmtiError error = JVMTI_ERROR_NONE;
+    Filter *filter;
+
+    filter = findFilter(node, JDWP_REQUEST_MODIFIER(LocationOnly));
+    if (filter == NULL) {
+        /* bp event with no location filter */
+        error = AGENT_ERROR_INTERNAL;
+    } else {
+        LocationFilter *lf = &(filter->u.LocationOnly);
+
+        /* if this is the first handler for this
+         * location, set bp at JVMTI level
+         */
+        if (!eventHandlerRestricted_iterator(
+                EI_BREAKPOINT, matchBreakpoint, lf)) {
+            LOG_LOC(("SetBreakpoint at location: method=%p,location=%d",
+                        lf->method, (int)lf->location));
+            error = JVMTI_FUNC_PTR(gdata->jvmti,SetBreakpoint)
+                        (gdata->jvmti, lf->method, lf->location);
+        }
+    }
+    return error;
+}
+
+/**
+ * Clear a breakpoint if this is the last one at this location.
+ */
+static jvmtiError
+clearBreakpoint(HandlerNode *node)
+{
+    jvmtiError error = JVMTI_ERROR_NONE;
+    Filter *filter;
+
+    filter = findFilter(node, JDWP_REQUEST_MODIFIER(LocationOnly));
+    if (filter == NULL) {
+        /* bp event with no location filter */
+        error = AGENT_ERROR_INTERNAL;
+    } else {
+        LocationFilter *lf = &(filter->u.LocationOnly);
+
+        /* if this is the last handler for this
+         * location, clear bp at JVMTI level
+         */
+        if (!eventHandlerRestricted_iterator(
+                EI_BREAKPOINT, matchBreakpoint, lf)) {
+            LOG_LOC(("ClearBreakpoint at location: method=%p,location=%d",
+                        lf->method, (int)lf->location));
+            error = JVMTI_FUNC_PTR(gdata->jvmti,ClearBreakpoint)
+                        (gdata->jvmti, lf->method, lf->location);
+        }
+    }
+    return error;
+}
+
+/**
+ * Return true if a breakpoint is set at the specified location.
+ */
+jboolean
+isBreakpointSet(jclass clazz, jmethodID method, jlocation location)
+{
+    LocationFilter lf;
+
+    lf.clazz    = clazz;
+    lf.method   = method;
+    lf.location = location;
+
+    return eventHandlerRestricted_iterator(EI_BREAKPOINT,
+                                           matchBreakpoint, &lf);
+}
+
+/**
+ * Determine if the specified watchpoint node has the
+ * same field as the FieldFilter passed in arg.
+ *
+ * This is a match function called by a
+ * eventHandlerRestricted_iterator invokation.
+ */
+static jboolean
+matchWatchpoint(JNIEnv *env, HandlerNode *node, void *arg)
+{
+    FieldFilter *goal = (FieldFilter *)arg;
+    Filter *filter = FILTERS_ARRAY(node);
+    int i;
+
+    for (i = 0; i < FILTER_COUNT(node); ++i, ++filter) {
+        switch (filter->modifier) {
+        case JDWP_REQUEST_MODIFIER(FieldOnly): {
+            FieldFilter *trial = &(filter->u.FieldOnly);
+            if (trial->field == goal->field &&
+                isSameObject(env, trial->clazz, goal->clazz)) {
+                return JNI_TRUE;
+            }
+        }
+        }
+    }
+    return JNI_FALSE;
+}
+
+/**
+ * Set a watchpoint if this is the first one on this field.
+ */
+static jvmtiError
+setWatchpoint(HandlerNode *node)
+{
+    jvmtiError error = JVMTI_ERROR_NONE;
+    Filter *filter;
+
+    filter = findFilter(node, JDWP_REQUEST_MODIFIER(FieldOnly));
+    if (filter == NULL) {
+        /* event with no field filter */
+        error = AGENT_ERROR_INTERNAL;
+    } else {
+        FieldFilter *ff = &(filter->u.FieldOnly);
+
+        /* if this is the first handler for this
+         * field, set wp at JVMTI level
+         */
+        if (!eventHandlerRestricted_iterator(
+                NODE_EI(node), matchWatchpoint, ff)) {
+            error = (NODE_EI(node) == EI_FIELD_ACCESS) ?
+                JVMTI_FUNC_PTR(gdata->jvmti,SetFieldAccessWatch)
+                        (gdata->jvmti, ff->clazz, ff->field) :
+                JVMTI_FUNC_PTR(gdata->jvmti,SetFieldModificationWatch)
+                        (gdata->jvmti, ff->clazz, ff->field);
+        }
+    }
+    return error;
+}
+
+/**
+ * Clear a watchpoint if this is the last one on this field.
+ */
+static jvmtiError
+clearWatchpoint(HandlerNode *node)
+{
+    jvmtiError error = JVMTI_ERROR_NONE;
+    Filter *filter;
+
+    filter = findFilter(node, JDWP_REQUEST_MODIFIER(FieldOnly));
+    if (filter == NULL) {
+        /* event with no field filter */
+        error = AGENT_ERROR_INTERNAL;
+    } else {
+        FieldFilter *ff = &(filter->u.FieldOnly);
+
+        /* if this is the last handler for this
+         * field, clear wp at JVMTI level
+         */
+        if (!eventHandlerRestricted_iterator(
+                NODE_EI(node), matchWatchpoint, ff)) {
+            error = (NODE_EI(node) == EI_FIELD_ACCESS) ?
+                JVMTI_FUNC_PTR(gdata->jvmti,ClearFieldAccessWatch)
+                        (gdata->jvmti, ff->clazz, ff->field) :
+                JVMTI_FUNC_PTR(gdata->jvmti,ClearFieldModificationWatch)
+                                (gdata->jvmti, ff->clazz, ff->field);
+        }
+    }
+    return error;
+}
+
+/**
+ * Determine the thread this node is filtered on.
+ * NULL if not thread filtered.
+ */
+static jthread
+requestThread(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):
+                return filter->u.ThreadOnly.thread;
+        }
+    }
+
+    return NULL;
+}
+
+/**
+ * Determine if the specified node has a
+ * thread filter with the thread passed in arg.
+ *
+ * This is a match function called by a
+ * eventHandlerRestricted_iterator invokation.
+ */
+static jboolean
+matchThread(JNIEnv *env, HandlerNode *node, void *arg)
+{
+    jthread goalThread = (jthread)arg;
+    jthread reqThread = requestThread(node);
+
+    /* If the event's thread and the passed thread are the same
+     * (or both are NULL), we have a match.
+     */
+    return isSameObject(env, reqThread, goalThread);
+}
+
+/**
+ * Do any enabling of events (including setting breakpoints etc)
+ * needed to get the events requested by this handler node.
+ */
+static jvmtiError
+enableEvents(HandlerNode *node)
+{
+    jvmtiError error = JVMTI_ERROR_NONE;
+
+    switch (NODE_EI(node)) {
+        /* The stepping code directly enables/disables stepping as
+         * necessary
+         */
+        case EI_SINGLE_STEP:
+        /* Internal thread event handlers are always present
+         * (hardwired in the event hook), so we don't change the
+         * notification mode here.
+         */
+        case EI_THREAD_START:
+        case EI_THREAD_END:
+        case EI_VM_INIT:
+        case EI_VM_DEATH:
+        case EI_CLASS_PREPARE:
+        case EI_GC_FINISH:
+            return error;
+
+        case EI_FIELD_ACCESS:
+        case EI_FIELD_MODIFICATION:
+            error = setWatchpoint(node);
+            break;
+
+        case EI_BREAKPOINT:
+            error = setBreakpoint(node);
+            break;
+
+        default:
+            break;
+    }
+
+    /* Don't globally enable if the above failed */
+    if (error == JVMTI_ERROR_NONE) {
+        jthread thread = requestThread(node);
+
+        /* If this is the first request of it's kind on this
+         * thread (or all threads (thread == NULL)) then enable
+         * these events on this thread.
+         */
+        if (!eventHandlerRestricted_iterator(
+                NODE_EI(node), matchThread, thread)) {
+            error = threadControl_setEventMode(JVMTI_ENABLE,
+                                               NODE_EI(node), thread);
+        }
+    }
+    return error;
+}
+
+/**
+ * Do any disabling of events (including clearing breakpoints etc)
+ * needed to no longer get the events requested by this handler node.
+ */
+static jvmtiError
+disableEvents(HandlerNode *node)
+{
+    jvmtiError error = JVMTI_ERROR_NONE;
+    jvmtiError error2 = JVMTI_ERROR_NONE;
+    jthread thread;
+
+
+    switch (NODE_EI(node)) {
+        /* The stepping code directly enables/disables stepping as
+         * necessary
+         */
+        case EI_SINGLE_STEP:
+        /* Internal thread event handlers are always present
+         * (hardwired in the event hook), so we don't change the
+         * notification mode here.
+         */
+        case EI_THREAD_START:
+        case EI_THREAD_END:
+        case EI_VM_INIT:
+        case EI_VM_DEATH:
+        case EI_CLASS_PREPARE:
+        case EI_GC_FINISH:
+            return error;
+
+        case EI_FIELD_ACCESS:
+        case EI_FIELD_MODIFICATION:
+            error = clearWatchpoint(node);
+            break;
+
+        case EI_BREAKPOINT:
+            error = clearBreakpoint(node);
+            break;
+
+        default:
+            break;
+    }
+
+    thread = requestThread(node);
+
+    /* If this is the last request of it's kind on this thread
+     * (or all threads (thread == NULL)) then disable these
+     * events on this thread.
+     *
+     * Disable even if the above caused an error
+     */
+    if (!eventHandlerRestricted_iterator(NODE_EI(node), matchThread, thread)) {
+        error2 = threadControl_setEventMode(JVMTI_DISABLE,
+                                            NODE_EI(node), thread);
+    }
+    return error != JVMTI_ERROR_NONE? error : error2;
+}
+
+
+/***** filter (and event) installation and deinstallation *****/
+
+/**
+ * Make the set of event filters that correspond with this
+ * node active (including enabling the corresponding events).
+ */
+jvmtiError
+eventFilterRestricted_install(HandlerNode *node)
+{
+    return enableEvents(node);
+}
+
+/**
+ * Make the set of event filters that correspond with this
+ * node inactive (including disabling the corresponding events
+ * and freeing resources).
+ */
+jvmtiError
+eventFilterRestricted_deinstall(HandlerNode *node)
+{
+    jvmtiError error1, error2;
+
+    error1 = disableEvents(node);
+    error2 = clearFilters(node);
+
+    return error1 != JVMTI_ERROR_NONE? error1 : error2;
+}