--- /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;
+}