/* * 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)) ? JNI_TRUE : JNI_FALSE; } 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 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; }