alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

Java example source code file (eventFilter.c)

This example Java source code file (eventFilter.c) is included in the alvinalexander.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Learn more about this Java project at its project page.

Java - Java tags/keywords

agent_error_illegal_argument, fieldfilter, filter, filter_count, filters_array, jdwp_request_modifier, jni_false, jni_true, jnienv, jvmti_error_none, jvmti_func_ptr, locationfilter, node_ei, null

The eventFilter.c Java example source code

/*
 * Copyright (c) 2001, 2013, 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
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.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"
#include "jvmti.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;
        }
    }
}

static jboolean isVersionGte12x() {
    jint version;
    jvmtiError err =
        JVMTI_FUNC_PTR(gdata->jvmti,GetVersionNumber)(gdata->jvmti, &version);

    if (err == JVMTI_ERROR_NONE) {
        jint major, minor;

        major = (version & JVMTI_VERSION_MASK_MAJOR)
                    >> JVMTI_VERSION_SHIFT_MAJOR;
        minor = (version & JVMTI_VERSION_MASK_MINOR)
                    >> JVMTI_VERSION_SHIFT_MINOR;
        return (major > 1 || major == 1 && minor >= 2);
    } else {
        return JNI_FALSE;
    }
}

/* 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;

    static jboolean got_version = JNI_FALSE;
    static jboolean is_version_gte_12x = JNI_FALSE;

    if (!got_version) {
        is_version_gte_12x = isVersionGte12x();
        got_version = JNI_TRUE;
    }

    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;
        if (is_version_gte_12x) {
            /* Use new 1.2.x function, GetLocalInstance */
            error = JVMTI_FUNC_PTR(gdata->jvmti,GetLocalInstance)
                        (gdata->jvmti, thread, fnum, &object);
        } else {
            /* 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 &&
                      sourceName != 0 &&
                      patternStringMatch(sourceName, desiredNamePattern)) {
                          // got a hit - report the event
                          jvmtiDeallocate(sourceName);
                          break;
                  }
                  // We have no match, we have no source file name,
                  // or we got a JVM TI error. Don't report the event.
                  jvmtiDeallocate(sourceName);
                  return JNI_FALSE;
              }
              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 preceding 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;
}

Other Java examples (source code examples)

Here is a short list of links related to this Java eventFilter.c source code file:

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.