--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/back/eventHandler.c Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,1714 @@
+/*
+ * Copyright 1998-2007 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.
+ */
+/*
+ * eventHandler
+ *
+ * This module handles events as they come in directly from JVMTI
+ * and also maps them to JDI events. JDI events are those requested
+ * at the JDI or JDWP level and seen on those levels. Mapping is
+ * one-to-many, a JVMTI event may map to several JDI events, or
+ * to none. Part of that mapping process is filteration, which
+ * eventFilter sub-module handles. A JDI EventRequest corresponds
+ * to a HandlerNode and a JDI filter to the hidden HandlerNode data
+ * used by eventFilter. For example, if at the JDI level the user
+ * executed:
+ *
+ * EventRequestManager erm = vm.eventRequestManager();
+ * BreakpointRequest bp = erm.createBreakpointRequest();
+ * bp.enable();
+ * ClassPrepareRequest req = erm.createClassPrepareRequest();
+ * req.enable();
+ * req = erm.createClassPrepareRequest();
+ * req.addClassFilter("Foo*");
+ * req.enable();
+ *
+ * Three handlers would be created, the first with a LocationOnly
+ * filter and the last with a ClassMatch filter.
+ * When a JVMTI class prepare event for "Foobar"
+ * comes in, the second handler will create one JDI event, the
+ * third handler will compare the class signature, and since
+ * it matchs create a second event. There may also be internal
+ * events as there are in this case, one created by the front-end
+ * and one by the back-end.
+ *
+ * Each event kind has a handler chain, which is a doublely linked
+ * list of handlers for that kind of event.
+ */
+#include "util.h"
+#include "eventHandler.h"
+#include "eventHandlerRestricted.h"
+#include "eventFilter.h"
+#include "eventFilterRestricted.h"
+#include "standardHandlers.h"
+#include "threadControl.h"
+#include "eventHelper.h"
+#include "classTrack.h"
+#include "commonRef.h"
+#include "debugLoop.h"
+
+static HandlerID requestIdCounter;
+static jbyte currentSessionID;
+
+/* Counter of active callbacks and flag for vm_death */
+static int active_callbacks = 0;
+static jboolean vm_death_callback_active = JNI_FALSE;
+static jrawMonitorID callbackLock;
+static jrawMonitorID callbackBlock;
+
+/* Macros to surround callback code (non-VM_DEATH callbacks).
+ * Note that this just keeps a count of the non-VM_DEATH callbacks that
+ * are currently active, it does not prevent these callbacks from
+ * operating in parallel. It's the VM_DEATH callback that will wait
+ * for all these callbacks to finish up, so that it can report the
+ * VM_DEATH in a clean state.
+ * If the VM_DEATH callback is active in the BEGIN macro then this
+ * callback just blocks until released by the VM_DEATH callback.
+ * If the VM_DEATH callback is active in the END macro, then this
+ * callback will notify the VM_DEATH callback if it's the last one,
+ * and then block until released by the VM_DEATH callback.
+ * Why block? These threads are often the threads of the Java program,
+ * not blocking might mean that a return would continue execution of
+ * some java thread in the middle of VM_DEATH, this seems troubled.
+ *
+ * WARNING: No not 'return' or 'goto' out of the BEGIN_CALLBACK/END_CALLBACK
+ * block, this will mess up the count.
+ */
+
+#define BEGIN_CALLBACK() \
+{ /* BEGIN OF CALLBACK */ \
+ jboolean bypass = JNI_TRUE; \
+ debugMonitorEnter(callbackLock); { \
+ if (vm_death_callback_active) { \
+ /* allow VM_DEATH callback to finish */ \
+ debugMonitorExit(callbackLock); \
+ /* Now block because VM is about to die */ \
+ debugMonitorEnter(callbackBlock); \
+ debugMonitorExit(callbackBlock); \
+ } else { \
+ active_callbacks++; \
+ bypass = JNI_FALSE; \
+ debugMonitorExit(callbackLock); \
+ } \
+ } \
+ if ( !bypass ) { \
+ /* BODY OF CALLBACK CODE */
+
+#define END_CALLBACK() /* Part of bypass if body */ \
+ debugMonitorEnter(callbackLock); { \
+ active_callbacks--; \
+ if (active_callbacks < 0) { \
+ EXIT_ERROR(0, "Problems tracking active callbacks"); \
+ } \
+ if (vm_death_callback_active) { \
+ if (active_callbacks == 0) { \
+ debugMonitorNotifyAll(callbackLock); \
+ } \
+ /* allow VM_DEATH callback to finish */ \
+ debugMonitorExit(callbackLock); \
+ /* Now block because VM is about to die */ \
+ debugMonitorEnter(callbackBlock); \
+ debugMonitorExit(callbackBlock); \
+ } else { \
+ debugMonitorExit(callbackLock); \
+ } \
+ } \
+ } \
+} /* END OF CALLBACK */
+
+/*
+ * We are starting with a very simple locking scheme
+ * for event handling. All readers and writers of data in
+ * the handlers[] chain must own this lock for the duration
+ * of its use. If contention becomes a problem, we can:
+ *
+ * 1) create a lock per event type.
+ * 2) move to a readers/writers approach where multiple threads
+ * can access the chains simultaneously while reading (the
+ * normal activity of an event callback).
+ */
+static jrawMonitorID handlerLock;
+
+typedef struct HandlerChain_ {
+ HandlerNode *first;
+ /* add lock here */
+} HandlerChain;
+
+/*
+ * This array maps event kinds to handler chains.
+ * Protected by handlerLock.
+ */
+
+static HandlerChain __handlers[EI_max-EI_min+1];
+
+/* Given a HandlerNode, these access our private data.
+ */
+#define PRIVATE_DATA(node) \
+ (&(((EventHandlerRestricted_HandlerNode*)(void*)(node))->private_ehpd))
+
+#define NEXT(node) (PRIVATE_DATA(node)->private_next)
+#define PREV(node) (PRIVATE_DATA(node)->private_prev)
+#define CHAIN(node) (PRIVATE_DATA(node)->private_chain)
+#define HANDLER_FUNCTION(node) (PRIVATE_DATA(node)->private_handlerFunction)
+
+static jclass getObjectClass(jobject object);
+static jvmtiError freeHandler(HandlerNode *node);
+
+static jvmtiError freeHandlerChain(HandlerChain *chain);
+
+static HandlerChain *
+getHandlerChain(EventIndex i)
+{
+ if ( i < EI_min || i > EI_max ) {
+ EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"bad index for handler");
+ }
+ return &(__handlers[i-EI_min]);
+}
+
+static void
+insert(HandlerChain *chain, HandlerNode *node)
+{
+ HandlerNode *oldHead = chain->first;
+ NEXT(node) = oldHead;
+ PREV(node) = NULL;
+ CHAIN(node) = chain;
+ if (oldHead != NULL) {
+ PREV(oldHead) = node;
+ }
+ chain->first = node;
+}
+
+static HandlerNode *
+findInChain(HandlerChain *chain, HandlerID handlerID)
+{
+ HandlerNode *node = chain->first;
+ while (node != NULL) {
+ if (node->handlerID == handlerID) {
+ return node;
+ }
+ node = NEXT(node);
+ }
+ return NULL;
+}
+
+static HandlerNode *
+find(EventIndex ei, HandlerID handlerID)
+{
+ return findInChain(getHandlerChain(ei), handlerID);
+}
+
+/**
+ * Deinsert. Safe for non-inserted nodes.
+ */
+static void
+deinsert(HandlerNode *node)
+{
+ HandlerChain *chain = CHAIN(node);
+
+ if (chain == NULL) {
+ return;
+ }
+ if (chain->first == node) {
+ chain->first = NEXT(node);
+ }
+ if (NEXT(node) != NULL) {
+ PREV(NEXT(node)) = PREV(node);
+ }
+ if (PREV(node) != NULL) {
+ NEXT(PREV(node)) = NEXT(node);
+ }
+ CHAIN(node) = NULL;
+}
+
+jboolean
+eventHandlerRestricted_iterator(EventIndex ei,
+ IteratorFunction func, void *arg)
+{
+ HandlerChain *chain;
+ HandlerNode *node;
+ JNIEnv *env;
+
+ chain = getHandlerChain(ei);
+ node = chain->first;
+ env = getEnv();
+
+ if ( func == NULL ) {
+ EXIT_ERROR(AGENT_ERROR_INTERNAL,"iterator function NULL");
+ }
+
+ while (node != NULL) {
+ if (((func)(env, node, arg))) {
+ return JNI_TRUE;
+ }
+ node = NEXT(node);
+ }
+ return JNI_FALSE;
+}
+
+/* BREAKPOINT, METHOD_ENTRY and SINGLE_STEP events are covered by
+ * the co-location of events policy. Of these three co-located
+ * events, METHOD_ENTRY is always reported first and BREAKPOINT
+ * is always reported last. Here are the possible combinations and
+ * their order:
+ *
+ * (p1) METHOD_ENTRY, BREAKPOINT (existing)
+ * (p2) METHOD_ENTRY, BREAKPOINT (new)
+ * (p1) METHOD_ENTRY, SINGLE_STEP
+ * (p1) METHOD_ENTRY, SINGLE_STEP, BREAKPOINT (existing)
+ * (p1/p2) METHOD_ENTRY, SINGLE_STEP, BREAKPOINT (new)
+ * (p1) SINGLE_STEP, BREAKPOINT (existing)
+ * (p2) SINGLE_STEP, BREAKPOINT (new)
+ *
+ * BREAKPOINT (existing) indicates a BREAKPOINT that is set before
+ * the other co-located event is posted. BREAKPOINT (new) indicates
+ * a BREAKPOINT that is set after the other co-located event is
+ * posted and before the thread has resumed execution.
+ *
+ * Co-location of events policy used to be implemented via
+ * temporary BREAKPOINTs along with deferring the reporting of
+ * non-BREAKPOINT co-located events, but the temporary BREAKPOINTs
+ * caused performance problems on VMs where setting or clearing
+ * BREAKPOINTs is expensive, e.g., HotSpot.
+ *
+ * The policy is now implemented in two phases. Phase 1: when a
+ * METHOD_ENTRY or SINGLE_STEP event is received, if there is an
+ * existing co-located BREAKPOINT, then the current event is
+ * deferred. When the BREAKPOINT event is processed, the event
+ * bag will contain the deferred METHOD_ENTRY and/or SINGLE_STEP
+ * events along with the BREAKPOINT event. For a METHOD_ENTRY
+ * event where there is not an existing co-located BREAKPOINT,
+ * if SINGLE_STEP events are also enabled for the thread, then
+ * the METHOD_ENTRY event is deferred. When the SINGLE_STEP event
+ * is processed, the event bag will also contain the deferred
+ * METHOD_ENTRY event. This covers each of the combinations
+ * marked with 'p1' above.
+ *
+ * Phase 2: if there is no existing co-located BREAKPOINT, then the
+ * location information for the METHOD_ENTRY or SINGLE_STEP event
+ * is recorded in the ThreadNode. If the next event for the thread
+ * is a co-located BREAKPOINT, then the first BREAKPOINT event will
+ * be skipped since it cannot be delivered in the same event set.
+ * This covers each of the combinations marked with 'p2' above.
+ *
+ * For the combination marked p1/p2, part of the case is handled
+ * during phase 1 and the rest is handled during phase 2.
+ *
+ * The recording of information in the ThreadNode is handled in
+ * this routine. The special handling of the next event for the
+ * thread is handled in skipEventReport().
+ */
+
+static jboolean
+deferEventReport(JNIEnv *env, jthread thread,
+ EventIndex ei, jclass clazz, jmethodID method, jlocation location)
+{
+ jboolean deferring = JNI_FALSE;
+
+ switch (ei) {
+ case EI_METHOD_ENTRY:
+ if (!isMethodNative(method)) {
+ jvmtiError error;
+ jlocation start;
+ jlocation end;
+ error = methodLocation(method, &start, &end);
+ if (error == JVMTI_ERROR_NONE) {
+ deferring = isBreakpointSet(clazz, method, start) ||
+ threadControl_getInstructionStepMode(thread)
+ == JVMTI_ENABLE;
+ if (!deferring) {
+ threadControl_saveCLEInfo(env, thread, ei,
+ clazz, method, start);
+ }
+ }
+ }
+ break;
+ case EI_SINGLE_STEP:
+ deferring = isBreakpointSet(clazz, method, location);
+ if (!deferring) {
+ threadControl_saveCLEInfo(env, thread, ei,
+ clazz, method, location);
+ }
+ break;
+ default:
+ break;
+ }
+ /* TO DO: Once JVMTI supports a way to know if we're
+ * at the end of a method, we should check here for
+ * break and step events which precede a method exit
+ * event.
+ */
+ return deferring;
+}
+
+/* Handle phase 2 of the co-located events policy. See detailed
+ * comments in deferEventReport() above.
+ */
+static jboolean
+skipEventReport(JNIEnv *env, jthread thread, EventIndex ei,
+ jclass clazz, jmethodID method, jlocation location)
+{
+ jboolean skipping = JNI_FALSE;
+
+ if (ei == EI_BREAKPOINT) {
+ if (threadControl_cmpCLEInfo(env, thread, clazz, method, location)) {
+ LOG_MISC(("Co-located breakpoint event found: "
+ "%s,thread=%p,clazz=%p,method=%p,location=%d",
+ eventText(ei), thread, clazz, method, location));
+ skipping = JNI_TRUE;
+ }
+ }
+
+ threadControl_clearCLEInfo(env, thread);
+
+ return skipping;
+}
+
+static void
+reportEvents(JNIEnv *env, jbyte sessionID, jthread thread, EventIndex ei,
+ jclass clazz, jmethodID method, jlocation location,
+ struct bag *eventBag)
+{
+ jbyte suspendPolicy;
+ jboolean invoking;
+
+ if (bagSize(eventBag) < 1) {
+ return;
+ }
+
+ /*
+ * Never report events before initialization completes
+ */
+ if (!debugInit_isInitComplete()) {
+ return;
+ }
+
+ /*
+ * Check to see if we should skip reporting this event due to
+ * co-location of events policy.
+ */
+ if (thread != NULL &&
+ skipEventReport(env, thread, ei, clazz, method, location)) {
+ LOG_MISC(("event report being skipped: "
+ "ei=%s,thread=%p,clazz=%p,method=%p,location=%d",
+ eventText(ei), thread, clazz, method, location));
+ bagDeleteAll(eventBag);
+ return;
+ }
+
+ /* We delay the reporting of some events so that they can be
+ * properly grouped into event sets with upcoming events. If
+ * the reporting is to be deferred, the event commands remain
+ * in the event bag until a subsequent event occurs. Event is
+ * NULL for synthetic events (e.g. unload).
+ */
+ if (thread == NULL
+ || !deferEventReport(env, thread, ei,
+ clazz, method, location)) {
+ struct bag *completedBag = bagDup(eventBag);
+ bagDeleteAll(eventBag);
+ if (completedBag == NULL) {
+ /*
+ * TO DO: Report, but don't terminate?
+ */
+ return;
+ } else {
+ suspendPolicy = eventHelper_reportEvents(sessionID, completedBag);
+ if (thread != NULL && suspendPolicy != JDWP_SUSPEND_POLICY(NONE)) {
+ do {
+ /* The events have been reported and this
+ * thread is about to continue, but it may
+ * have been started up up just to perform a
+ * requested method invocation. If so, we do
+ * the invoke now and then stop again waiting
+ * for another continue. By then another
+ * invoke request can be in place, so there is
+ * a loop around this code.
+ */
+ invoking = invoker_doInvoke(thread);
+ if (invoking) {
+ eventHelper_reportInvokeDone(sessionID, thread);
+ }
+ } while (invoking);
+ }
+ bagDestroyBag(completedBag);
+ }
+ }
+}
+
+/* A bagEnumerateFunction. Create a synthetic class unload event
+ * for every class no longer present. Analogous to event_callback
+ * combined with a handler in a unload specific (no event
+ * structure) kind of way.
+ */
+static jboolean
+synthesizeUnloadEvent(void *signatureVoid, void *envVoid)
+{
+ JNIEnv *env = (JNIEnv *)envVoid;
+ char *signature = *(char **)signatureVoid;
+ char *classname;
+ HandlerNode *node;
+ jbyte eventSessionID = currentSessionID;
+ struct bag *eventBag = eventHelper_createEventBag();
+
+ if (eventBag == NULL) {
+ /* TO DO: Report, but don't die
+ */
+ JDI_ASSERT(eventBag != NULL);
+ }
+
+ /* Signature needs to last, so convert extra copy to
+ * classname
+ */
+ classname = jvmtiAllocate((int)strlen(signature)+1);
+ (void)strcpy(classname, signature);
+ convertSignatureToClassname(classname);
+
+ debugMonitorEnter(handlerLock);
+
+ node = getHandlerChain(EI_GC_FINISH)->first;
+ while (node != NULL) {
+ /* save next so handlers can remove themselves */
+ HandlerNode *next = NEXT(node);
+ jboolean shouldDelete;
+
+ if (eventFilterRestricted_passesUnloadFilter(env, classname,
+ node,
+ &shouldDelete)) {
+ /* There may be multiple handlers, the signature will
+ * be freed when the event helper thread has written
+ * it. So each event needs a separate allocation.
+ */
+ char *durableSignature = jvmtiAllocate((int)strlen(signature)+1);
+ (void)strcpy(durableSignature, signature);
+
+ eventHelper_recordClassUnload(node->handlerID,
+ durableSignature,
+ eventBag);
+ }
+ if (shouldDelete) {
+ /* We can safely free the node now that we are done
+ * using it.
+ */
+ (void)freeHandler(node);
+ }
+ node = next;
+ }
+
+ debugMonitorExit(handlerLock);
+
+ if (eventBag != NULL) {
+ reportEvents(env, eventSessionID, (jthread)NULL, 0,
+ (jclass)NULL, (jmethodID)NULL, 0, eventBag);
+
+ /*
+ * bag was created locally, destroy it here.
+ */
+ bagDestroyBag(eventBag);
+ }
+
+ jvmtiDeallocate(signature);
+ jvmtiDeallocate(classname);
+
+ return JNI_TRUE;
+}
+
+/* Garbage Collection Happened */
+static unsigned int garbageCollected = 0;
+
+/* The JVMTI generic event callback. Each event is passed to a sequence of
+ * handlers in a chain until the chain ends or one handler
+ * consumes the event.
+ */
+static void
+event_callback(JNIEnv *env, EventInfo *evinfo)
+{
+ struct bag *eventBag;
+ jbyte eventSessionID = currentSessionID; /* session could change */
+ jthrowable currentException;
+ jthread thread;
+
+ LOG_MISC(("event_callback(): ei=%s", eventText(evinfo->ei)));
+ log_debugee_location("event_callback()", evinfo->thread, evinfo->method, evinfo->location);
+
+ /* We want to preserve any current exception that might get
+ * wiped out during event handling (e.g. JNI calls). We have
+ * to rely on space for the local reference on the current
+ * frame because doing a PushLocalFrame here might itself
+ * generate an exception.
+ */
+ currentException = JNI_FUNC_PTR(env,ExceptionOccurred)(env);
+ JNI_FUNC_PTR(env,ExceptionClear)(env);
+
+ /* See if a garbage collection finish event happened earlier.
+ *
+ * Note: The "if" is an optimization to avoid entering the lock on every
+ * event; garbageCollected may be zapped before we enter
+ * the lock but then this just becomes one big no-op.
+ */
+ if ( garbageCollected > 0 ) {
+ struct bag *unloadedSignatures = NULL;
+
+ /* We want to compact the hash table of all
+ * objects sent to the front end by removing objects that have
+ * been collected.
+ */
+ commonRef_compact();
+
+ /* We also need to simulate the class unload events. */
+
+ debugMonitorEnter(handlerLock);
+
+ /* Clear garbage collection counter */
+ garbageCollected = 0;
+
+ /* Analyze which class unloads occurred */
+ unloadedSignatures = classTrack_processUnloads(env);
+
+ debugMonitorExit(handlerLock);
+
+ /* Generate the synthetic class unload events and/or just cleanup. */
+ if ( unloadedSignatures != NULL ) {
+ (void)bagEnumerateOver(unloadedSignatures, synthesizeUnloadEvent,
+ (void *)env);
+ bagDestroyBag(unloadedSignatures);
+ }
+ }
+
+ thread = evinfo->thread;
+ if (thread != NULL) {
+ /*
+ * Record the fact that we're entering an event
+ * handler so that thread operations (status, interrupt,
+ * stop) can be done correctly and so that thread
+ * resources can be allocated. This must be done before
+ * grabbing any locks.
+ */
+ eventBag = threadControl_onEventHandlerEntry(eventSessionID,
+ evinfo->ei, thread, currentException);
+ if ( eventBag == NULL ) {
+ jboolean invoking;
+ do {
+ /* The event has been 'handled' and this
+ * thread is about to continue, but it may
+ * have been started up just to perform a
+ * requested method invocation. If so, we do
+ * the invoke now and then stop again waiting
+ * for another continue. By then another
+ * invoke request can be in place, so there is
+ * a loop around this code.
+ */
+ invoking = invoker_doInvoke(thread);
+ if (invoking) {
+ eventHelper_reportInvokeDone(eventSessionID, thread);
+ }
+ } while (invoking);
+ return; /* Do nothing, event was consumed */
+ }
+ } else {
+ eventBag = eventHelper_createEventBag();
+ if (eventBag == NULL) {
+ /*
+ * TO DO: Report, but don't die
+ */
+ eventBag = NULL; /* to shut up lint */
+ }
+ }
+
+ debugMonitorEnter(handlerLock);
+ {
+ HandlerNode *node;
+ char *classname;
+
+ /* We must keep track of all classes prepared to know what's unloaded */
+ if (evinfo->ei == EI_CLASS_PREPARE) {
+ classTrack_addPreparedClass(env, evinfo->clazz);
+ }
+
+ node = getHandlerChain(evinfo->ei)->first;
+ classname = getClassname(evinfo->clazz);
+
+ while (node != NULL) {
+ /* save next so handlers can remove themselves */
+ HandlerNode *next = NEXT(node);
+ jboolean shouldDelete;
+
+ if (eventFilterRestricted_passesFilter(env, classname,
+ evinfo, node,
+ &shouldDelete)) {
+ HandlerFunction func;
+
+ func = HANDLER_FUNCTION(node);
+ if ( func == NULL ) {
+ EXIT_ERROR(AGENT_ERROR_INTERNAL,"handler function NULL");
+ }
+ (*func)(env, evinfo, node, eventBag);
+ }
+ if (shouldDelete) {
+ /* We can safely free the node now that we are done
+ * using it.
+ */
+ (void)freeHandler(node);
+ }
+ node = next;
+ }
+ jvmtiDeallocate(classname);
+ }
+ debugMonitorExit(handlerLock);
+
+ if (eventBag != NULL) {
+ reportEvents(env, eventSessionID, thread, evinfo->ei,
+ evinfo->clazz, evinfo->method, evinfo->location, eventBag);
+ }
+
+ /* we are continuing after VMDeathEvent - now we are dead */
+ if (evinfo->ei == EI_VM_DEATH) {
+ gdata->vmDead = JNI_TRUE;
+ }
+
+ /*
+ * If the bag was created locally, destroy it here.
+ */
+ if (thread == NULL) {
+ bagDestroyBag(eventBag);
+ }
+
+ /* Always restore any exception that was set beforehand. If
+ * there is a pending async exception, StopThread will be
+ * called from threadControl_onEventHandlerExit immediately
+ * below. Depending on VM implementation and state, the async
+ * exception might immediately overwrite the currentException,
+ * or it might be delayed until later. */
+ if (currentException != NULL) {
+ JNI_FUNC_PTR(env,Throw)(env, currentException);
+ } else {
+ JNI_FUNC_PTR(env,ExceptionClear)(env);
+ }
+
+ /*
+ * Release thread resources and perform any delayed operations.
+ */
+ if (thread != NULL) {
+ threadControl_onEventHandlerExit(evinfo->ei, thread, eventBag);
+ }
+}
+
+/* Returns a local ref to the declaring class for an object. */
+static jclass
+getObjectClass(jobject object)
+{
+ jclass clazz;
+ JNIEnv *env = getEnv();
+
+ clazz = JNI_FUNC_PTR(env,GetObjectClass)(env, object);
+
+ return clazz;
+}
+
+/* Returns a local ref to the declaring class for a method, or NULL. */
+jclass
+getMethodClass(jvmtiEnv *jvmti_env, jmethodID method)
+{
+ jclass clazz = NULL;
+ jvmtiError error;
+
+ if ( method == NULL ) {
+ return NULL;
+ }
+ error = methodClass(method, &clazz);
+ if ( error != JVMTI_ERROR_NONE ) {
+ EXIT_ERROR(error,"Can't get jclass for a methodID, invalid?");
+ return NULL;
+ }
+ return clazz;
+}
+
+/* Event callback for JVMTI_EVENT_SINGLE_STEP */
+static void JNICALL
+cbSingleStep(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jmethodID method, jlocation location)
+{
+ EventInfo info;
+
+ LOG_CB(("cbSingleStep: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_SINGLE_STEP;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ info.location = location;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbSingleStep"));
+}
+
+/* Event callback for JVMTI_EVENT_BREAKPOINT */
+static void JNICALL
+cbBreakpoint(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jmethodID method, jlocation location)
+{
+ EventInfo info;
+
+ LOG_CB(("cbBreakpoint: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_BREAKPOINT;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ info.location = location;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbBreakpoint"));
+}
+
+/* Event callback for JVMTI_EVENT_FRAME_POP */
+static void JNICALL
+cbFramePop(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jmethodID method,
+ jboolean wasPoppedByException)
+{
+ EventInfo info;
+
+ /* JDWP does not return these events when popped due to an exception. */
+ if ( wasPoppedByException ) {
+ return;
+ }
+
+ LOG_CB(("cbFramePop: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_FRAME_POP;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbFramePop"));
+}
+
+/* Event callback for JVMTI_EVENT_EXCEPTION */
+static void JNICALL
+cbException(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jmethodID method,
+ jlocation location, jobject exception,
+ jmethodID catch_method, jlocation catch_location)
+{
+ EventInfo info;
+
+ LOG_CB(("cbException: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_EXCEPTION;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ info.location = location;
+ info.object = exception;
+ info.u.exception.catch_clazz = getMethodClass(jvmti_env, catch_method);
+ info.u.exception.catch_method = catch_method;
+ info.u.exception.catch_location = catch_location;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbException"));
+}
+
+/* Event callback for JVMTI_EVENT_THREAD_START */
+static void JNICALL
+cbThreadStart(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread)
+{
+ EventInfo info;
+
+ LOG_CB(("cbThreadStart: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_THREAD_START;
+ info.thread = thread;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbThreadStart"));
+}
+
+/* Event callback for JVMTI_EVENT_THREAD_END */
+static void JNICALL
+cbThreadEnd(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread)
+{
+ EventInfo info;
+
+ LOG_CB(("cbThreadEnd: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_THREAD_END;
+ info.thread = thread;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbThreadEnd"));
+}
+
+/* Event callback for JVMTI_EVENT_CLASS_PREPARE */
+static void JNICALL
+cbClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jclass klass)
+{
+ EventInfo info;
+
+ LOG_CB(("cbClassPrepare: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_CLASS_PREPARE;
+ info.thread = thread;
+ info.clazz = klass;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbClassPrepare"));
+}
+
+/* Event callback for JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
+static void JNICALL
+cbGarbageCollectionFinish(jvmtiEnv *jvmti_env)
+{
+ LOG_CB(("cbGarbageCollectionFinish"));
+ ++garbageCollected;
+ LOG_MISC(("END cbGarbageCollectionFinish"));
+}
+
+/* Event callback for JVMTI_EVENT_CLASS_LOAD */
+static void JNICALL
+cbClassLoad(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jclass klass)
+{
+ EventInfo info;
+
+ LOG_CB(("cbClassLoad: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_CLASS_LOAD;
+ info.thread = thread;
+ info.clazz = klass;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbClassLoad"));
+}
+
+/* Event callback for JVMTI_EVENT_FIELD_ACCESS */
+static void JNICALL
+cbFieldAccess(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jmethodID method,
+ jlocation location, jclass field_klass,
+ jobject object, jfieldID field)
+{
+ EventInfo info;
+
+ LOG_CB(("cbFieldAccess: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_FIELD_ACCESS;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ info.location = location;
+ info.u.field_access.field_clazz = field_klass;
+ info.object = object;
+ info.u.field_access.field = field;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbFieldAccess"));
+}
+
+/* Event callback for JVMTI_EVENT_FIELD_MODIFICATION */
+static void JNICALL
+cbFieldModification(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jmethodID method,
+ jlocation location, jclass field_klass, jobject object, jfieldID field,
+ char signature_type, jvalue new_value)
+{
+ EventInfo info;
+
+ LOG_CB(("cbFieldModification: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_FIELD_MODIFICATION;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ info.location = location;
+ info.u.field_modification.field = field;
+ info.u.field_modification.field_clazz = field_klass;
+ info.object = object;
+ info.u.field_modification.signature_type= signature_type;
+ info.u.field_modification.new_value = new_value;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbFieldModification"));
+}
+
+/* Event callback for JVMTI_EVENT_EXCEPTION_CATCH */
+static void JNICALL
+cbExceptionCatch(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread,
+ jmethodID method, jlocation location, jobject exception)
+{
+ EventInfo info;
+
+ LOG_CB(("cbExceptionCatch: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_EXCEPTION_CATCH;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ info.location = location;
+ info.object = exception;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbExceptionCatch"));
+}
+
+/* Event callback for JVMTI_EVENT_METHOD_ENTRY */
+static void JNICALL
+cbMethodEntry(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jmethodID method)
+{
+ EventInfo info;
+
+ LOG_CB(("cbMethodEntry: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_METHOD_ENTRY;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbMethodEntry"));
+}
+
+/* Event callback for JVMTI_EVENT_METHOD_EXIT */
+static void JNICALL
+cbMethodExit(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jmethodID method,
+ jboolean wasPoppedByException, jvalue return_value)
+{
+ EventInfo info;
+
+ /* JDWP does not return these events when popped due to an exception. */
+ if ( wasPoppedByException ) {
+ return;
+ }
+
+ LOG_CB(("cbMethodExit: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_METHOD_EXIT;
+ info.thread = thread;
+ info.clazz = getMethodClass(jvmti_env, method);
+ info.method = method;
+ info.u.method_exit.return_value = return_value;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbMethodExit"));
+}
+
+/* Event callback for JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
+static void JNICALL
+cbMonitorContendedEnter(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jobject object)
+{
+ EventInfo info;
+ jvmtiError error;
+ jmethodID method;
+ jlocation location;
+
+ LOG_CB(("cbMonitorContendedEnter: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_MONITOR_CONTENDED_ENTER;
+ info.thread = thread;
+ info.object = object;
+ /* get current location of contended monitor enter */
+ error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameLocation)
+ (gdata->jvmti, thread, 0, &method, &location);
+ if (error == JVMTI_ERROR_NONE) {
+ info.location = location;
+ info.method = method;
+ info.clazz = getMethodClass(jvmti_env, method);
+ } else {
+ info.location = -1;
+ }
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbMonitorContendedEnter"));
+}
+
+/* Event callback for JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
+static void JNICALL
+cbMonitorContendedEntered(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jobject object)
+{
+ EventInfo info;
+ jvmtiError error;
+ jmethodID method;
+ jlocation location;
+
+ LOG_CB(("cbMonitorContendedEntered: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_MONITOR_CONTENDED_ENTERED;
+ info.thread = thread;
+ info.object = object;
+ /* get current location of contended monitor enter */
+ error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameLocation)
+ (gdata->jvmti, thread, 0, &method, &location);
+ if (error == JVMTI_ERROR_NONE) {
+ info.location = location;
+ info.method = method;
+ info.clazz = getMethodClass(jvmti_env, method);
+ } else {
+ info.location = -1;
+ }
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbMonitorContendedEntered"));
+}
+
+/* Event callback for JVMTI_EVENT_MONITOR_WAIT */
+static void JNICALL
+cbMonitorWait(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jobject object,
+ jlong timeout)
+{
+ EventInfo info;
+ jvmtiError error;
+ jmethodID method;
+ jlocation location;
+
+ LOG_CB(("cbMonitorWait: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_MONITOR_WAIT;
+ info.thread = thread;
+ info.object = object;
+ /* The info.clazz is used for both class filtering and for location info.
+ * For monitor wait event the class filtering is done for class of monitor
+ * object. So here info.clazz is set to class of monitor object here and it
+ * is reset to class of method before writing location info.
+ * See writeMonitorEvent in eventHelper.c
+ */
+ info.clazz = getObjectClass(object);
+ info.u.monitor.timeout = timeout;
+
+ /* get location of monitor wait() method. */
+ error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameLocation)
+ (gdata->jvmti, thread, 0, &method, &location);
+ if (error == JVMTI_ERROR_NONE) {
+ info.location = location;
+ info.method = method;
+ } else {
+ info.location = -1;
+ }
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbMonitorWait"));
+}
+
+/* Event callback for JVMTI_EVENT_MONITOR_WAIT */
+static void JNICALL
+cbMonitorWaited(jvmtiEnv *jvmti_env, JNIEnv *env,
+ jthread thread, jobject object,
+ jboolean timed_out)
+{
+ EventInfo info;
+ jvmtiError error;
+ jmethodID method;
+ jlocation location;
+
+ LOG_CB(("cbMonitorWaited: thread=%p", thread));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_MONITOR_WAITED;
+ info.thread = thread;
+ info.object = object;
+ /* The info.clazz is used for both class filtering and for location info.
+ * For monitor waited event the class filtering is done for class of monitor
+ * object. So here info.clazz is set to class of monitor object here and it
+ * is reset to class of method before writing location info.
+ * See writeMonitorEvent in eventHelper.c
+ */
+ info.clazz = getObjectClass(object);
+ info.u.monitor.timed_out = timed_out;
+
+ /* get location of monitor wait() method */
+ error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameLocation)
+ (gdata->jvmti, thread, 0, &method, &location);
+ if (error == JVMTI_ERROR_NONE) {
+ info.location = location;
+ info.method = method;
+ } else {
+ info.location = -1;
+ }
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbMonitorWaited"));
+}
+
+/* Event callback for JVMTI_EVENT_VM_INIT */
+static void JNICALL
+cbVMInit(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread)
+{
+ EventInfo info;
+
+ LOG_CB(("cbVMInit"));
+
+ BEGIN_CALLBACK() {
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_VM_INIT;
+ info.thread = thread;
+ event_callback(env, &info);
+ } END_CALLBACK();
+
+ LOG_MISC(("END cbVMInit"));
+}
+
+/* Event callback for JVMTI_EVENT_VM_DEATH */
+static void JNICALL
+cbVMDeath(jvmtiEnv *jvmti_env, JNIEnv *env)
+{
+ jvmtiError error;
+ EventInfo info;
+ LOG_CB(("cbVMDeath"));
+
+ /* Clear out ALL callbacks at this time, we don't want any more. */
+ /* This should prevent any new BEGIN_CALLBACK() calls. */
+ (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks));
+ error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventCallbacks)
+ (gdata->jvmti, &(gdata->callbacks), sizeof(gdata->callbacks));
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't clear event callbacks on vm death");
+ }
+
+ /* Now that no new callbacks will be made, we need to wait for the ones
+ * that are still active to complete.
+ * The BEGIN_CALLBACK/END_CALLBACK macros implement the VM_DEATH
+ * callback protocol. Once the callback table is cleared (above),
+ * we can have callback threads in different stages:
+ * 1) after callback function entry and before BEGIN_CALLBACK
+ * macro; we catch these threads with callbackBlock in the
+ * BEGIN_CALLBACK macro
+ * 2) after BEGIN_CALLBACK macro and before END_CALLBACK macro; we
+ * catch these threads with callbackBlock in the END_CALLBACK
+ * macro
+ * 3) after END_CALLBACK macro; these threads have made it past
+ * callbackBlock and callbackLock and don't count as active
+ *
+ * Since some of the callback threads could be blocked or suspended
+ * we will resume all threads suspended by the debugger for a short
+ * time to flush out all callbacks. Note that the callback threads
+ * will block from returning to the VM in both macros. Some threads
+ * not associated with callbacks, but suspended by the debugger may
+ * continue on, but not for long.
+ * Once the last callback finishes, it will notify this thread and
+ * we fall out of the loop below and actually process the VM_DEATH
+ * event.
+ */
+ debugMonitorEnter(callbackBlock); {
+ debugMonitorEnter(callbackLock); {
+ vm_death_callback_active = JNI_TRUE;
+ (void)threadControl_resumeAll();
+ while (active_callbacks > 0) {
+ /* wait for active CALLBACKs to check in (and block) */
+ debugMonitorWait(callbackLock);
+ }
+ } debugMonitorExit(callbackLock);
+
+ /* Only now should we actually process the VM death event */
+ (void)memset(&info,0,sizeof(info));
+ info.ei = EI_VM_DEATH;
+ event_callback(env, &info);
+
+ /* Here we unblock all the callbacks and let them return to the
+ * VM. It's not clear this is necessary, but leaving threads
+ * blocked doesn't seem like a good idea. They don't have much
+ * life left anyway.
+ */
+ } debugMonitorExit(callbackBlock);
+
+ /*
+ * The VM will die soon after the completion of this callback - we
+ * may need to do a final synchronization with the command loop to
+ * avoid the VM terminating with replying to the final (resume)
+ * command.
+ */
+ debugLoop_sync();
+
+ LOG_MISC(("END cbVMDeath"));
+}
+
+/**
+ * Delete this handler (do not delete permanent handlers):
+ * Deinsert handler from active list,
+ * make it inactive, and free it's memory
+ * Assumes handlerLock held.
+ */
+static jvmtiError
+freeHandler(HandlerNode *node) {
+ jvmtiError error = JVMTI_ERROR_NONE;
+
+ /* deinsert the handler node before disableEvents() to make
+ * sure the event will be disabled when no other event
+ * handlers are installed.
+ */
+ if (node != NULL && (!node->permanent)) {
+ deinsert(node);
+ error = eventFilterRestricted_deinstall(node);
+ jvmtiDeallocate(node);
+ }
+
+ return error;
+}
+
+/**
+ * Delete all the handlers on this chain (do not delete permanent handlers).
+ * Assumes handlerLock held.
+ */
+static jvmtiError
+freeHandlerChain(HandlerChain *chain)
+{
+ HandlerNode *node;
+ jvmtiError error;
+
+ error = JVMTI_ERROR_NONE;
+ node = chain->first;
+ while ( node != NULL ) {
+ HandlerNode *next;
+ jvmtiError singleError;
+
+ next = NEXT(node);
+ singleError = freeHandler(node);
+ if ( singleError != JVMTI_ERROR_NONE ) {
+ error = singleError;
+ }
+ node = next;
+ }
+ return error;
+}
+
+/**
+ * Deinsert and free all memory. Safe for non-inserted nodes.
+ */
+jvmtiError
+eventHandler_free(HandlerNode *node)
+{
+ jvmtiError error;
+
+ debugMonitorEnter(handlerLock);
+
+ error = freeHandler(node);
+
+ debugMonitorExit(handlerLock);
+
+ return error;
+}
+
+/**
+ * Free all handlers of this kind created by the JDWP client,
+ * that is, doesn't free handlers internally created by back-end.
+ */
+jvmtiError
+eventHandler_freeAll(EventIndex ei)
+{
+ jvmtiError error = JVMTI_ERROR_NONE;
+ HandlerNode *node;
+
+ debugMonitorEnter(handlerLock);
+ node = getHandlerChain(ei)->first;
+ while (node != NULL) {
+ HandlerNode *next = NEXT(node); /* allows node removal */
+ if (node->handlerID != 0) { /* don't free internal handlers */
+ error = freeHandler(node);
+ if (error != JVMTI_ERROR_NONE) {
+ break;
+ }
+ }
+ node = next;
+ }
+ debugMonitorExit(handlerLock);
+ return error;
+}
+
+/***
+ * Delete all breakpoints on "clazz".
+ */
+void
+eventHandler_freeClassBreakpoints(jclass clazz)
+{
+ HandlerNode *node;
+ JNIEnv *env = getEnv();
+
+ debugMonitorEnter(handlerLock);
+ node = getHandlerChain(EI_BREAKPOINT)->first;
+ while (node != NULL) {
+ HandlerNode *next = NEXT(node); /* allows node removal */
+ if (eventFilterRestricted_isBreakpointInClass(env, clazz,
+ node)) {
+ (void)freeHandler(node);
+ }
+ node = next;
+ }
+ debugMonitorExit(handlerLock);
+}
+
+jvmtiError
+eventHandler_freeByID(EventIndex ei, HandlerID handlerID)
+{
+ jvmtiError error;
+ HandlerNode *node;
+
+ debugMonitorEnter(handlerLock);
+ node = find(ei, handlerID);
+ if (node != NULL) {
+ error = freeHandler(node);
+ } else {
+ /* already freed */
+ error = JVMTI_ERROR_NONE;
+ }
+ debugMonitorExit(handlerLock);
+ return error;
+}
+
+void
+eventHandler_initialize(jbyte sessionID)
+{
+ jvmtiError error;
+ jint i;
+
+ requestIdCounter = 1;
+ currentSessionID = sessionID;
+
+ /* This is for BEGIN_CALLBACK/END_CALLBACK handling, make sure this
+ * is done while none of these callbacks are active.
+ */
+ active_callbacks = 0;
+ vm_death_callback_active = JNI_FALSE;
+ callbackLock = debugMonitorCreate("JDWP Callback Lock");
+ callbackBlock = debugMonitorCreate("JDWP Callback Block");
+
+ handlerLock = debugMonitorCreate("JDWP Event Handler Lock");
+
+ for (i = EI_min; i <= EI_max; ++i) {
+ getHandlerChain(i)->first = NULL;
+ }
+
+ /*
+ * Permanently enabled some events.
+ */
+ error = threadControl_setEventMode(JVMTI_ENABLE,
+ EI_VM_INIT, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't enable vm init events");
+ }
+ error = threadControl_setEventMode(JVMTI_ENABLE,
+ EI_VM_DEATH, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't enable vm death events");
+ }
+ error = threadControl_setEventMode(JVMTI_ENABLE,
+ EI_THREAD_START, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't enable thread start events");
+ }
+ error = threadControl_setEventMode(JVMTI_ENABLE,
+ EI_THREAD_END, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't enable thread end events");
+ }
+ error = threadControl_setEventMode(JVMTI_ENABLE,
+ EI_CLASS_PREPARE, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't enable class prepare events");
+ }
+ error = threadControl_setEventMode(JVMTI_ENABLE,
+ EI_GC_FINISH, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't enable garbage collection finish events");
+ }
+
+ (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks));
+ /* Event callback for JVMTI_EVENT_SINGLE_STEP */
+ gdata->callbacks.SingleStep = &cbSingleStep;
+ /* Event callback for JVMTI_EVENT_BREAKPOINT */
+ gdata->callbacks.Breakpoint = &cbBreakpoint;
+ /* Event callback for JVMTI_EVENT_FRAME_POP */
+ gdata->callbacks.FramePop = &cbFramePop;
+ /* Event callback for JVMTI_EVENT_EXCEPTION */
+ gdata->callbacks.Exception = &cbException;
+ /* Event callback for JVMTI_EVENT_THREAD_START */
+ gdata->callbacks.ThreadStart = &cbThreadStart;
+ /* Event callback for JVMTI_EVENT_THREAD_END */
+ gdata->callbacks.ThreadEnd = &cbThreadEnd;
+ /* Event callback for JVMTI_EVENT_CLASS_PREPARE */
+ gdata->callbacks.ClassPrepare = &cbClassPrepare;
+ /* Event callback for JVMTI_EVENT_CLASS_LOAD */
+ gdata->callbacks.ClassLoad = &cbClassLoad;
+ /* Event callback for JVMTI_EVENT_FIELD_ACCESS */
+ gdata->callbacks.FieldAccess = &cbFieldAccess;
+ /* Event callback for JVMTI_EVENT_FIELD_MODIFICATION */
+ gdata->callbacks.FieldModification = &cbFieldModification;
+ /* Event callback for JVMTI_EVENT_EXCEPTION_CATCH */
+ gdata->callbacks.ExceptionCatch = &cbExceptionCatch;
+ /* Event callback for JVMTI_EVENT_METHOD_ENTRY */
+ gdata->callbacks.MethodEntry = &cbMethodEntry;
+ /* Event callback for JVMTI_EVENT_METHOD_EXIT */
+ gdata->callbacks.MethodExit = &cbMethodExit;
+ /* Event callback for JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
+ gdata->callbacks.MonitorContendedEnter = &cbMonitorContendedEnter;
+ /* Event callback for JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
+ gdata->callbacks.MonitorContendedEntered = &cbMonitorContendedEntered;
+ /* Event callback for JVMTI_EVENT_MONITOR_WAIT */
+ gdata->callbacks.MonitorWait = &cbMonitorWait;
+ /* Event callback for JVMTI_EVENT_MONITOR_WAITED */
+ gdata->callbacks.MonitorWaited = &cbMonitorWaited;
+ /* Event callback for JVMTI_EVENT_VM_INIT */
+ gdata->callbacks.VMInit = &cbVMInit;
+ /* Event callback for JVMTI_EVENT_VM_DEATH */
+ gdata->callbacks.VMDeath = &cbVMDeath;
+ /* Event callback for JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
+ gdata->callbacks.GarbageCollectionFinish = &cbGarbageCollectionFinish;
+
+ error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventCallbacks)
+ (gdata->jvmti, &(gdata->callbacks), sizeof(gdata->callbacks));
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"Can't set event callbacks");
+ }
+
+ /* Notify other modules that the event callbacks are in place */
+ threadControl_onHook();
+
+ /* Get the event helper thread initialized */
+ eventHelper_initialize(sessionID);
+}
+
+void
+eventHandler_reset(jbyte sessionID)
+{
+ int i;
+
+ debugMonitorEnter(handlerLock);
+
+ /* We must do this first so that if any invokes complete,
+ * there will be no attempt to send them to the front
+ * end. Waiting for threadControl_reset leaves a window where
+ * the invoke completions can sneak through.
+ */
+ threadControl_detachInvokes();
+
+ /* Reset the event helper thread, purging all queued and
+ * in-process commands.
+ */
+ eventHelper_reset(sessionID);
+
+ /* delete all handlers */
+ for (i = EI_min; i <= EI_max; i++) {
+ (void)freeHandlerChain(getHandlerChain(i));
+ }
+
+ requestIdCounter = 1;
+ currentSessionID = sessionID;
+
+ debugMonitorExit(handlerLock);
+}
+
+void
+eventHandler_lock(void)
+{
+ debugMonitorEnter(handlerLock);
+}
+
+void
+eventHandler_unlock(void)
+{
+ debugMonitorExit(handlerLock);
+}
+
+/***** handler creation *****/
+
+HandlerNode *
+eventHandler_alloc(jint filterCount, EventIndex ei, jbyte suspendPolicy)
+{
+ HandlerNode *node = eventFilterRestricted_alloc(filterCount);
+
+ if (node != NULL) {
+ node->ei = ei;
+ node->suspendPolicy = suspendPolicy;
+ node->permanent = JNI_FALSE;
+ }
+
+ return node;
+}
+
+
+HandlerID
+eventHandler_allocHandlerID(void)
+{
+ jint handlerID;
+ debugMonitorEnter(handlerLock);
+ handlerID = ++requestIdCounter;
+ debugMonitorExit(handlerLock);
+ return handlerID;
+}
+
+
+static jvmtiError
+installHandler(HandlerNode *node,
+ HandlerFunction func,
+ jboolean external)
+{
+ jvmtiError error;
+
+ if ( func == NULL ) {
+ return AGENT_ERROR_INVALID_EVENT_TYPE;
+ }
+
+ debugMonitorEnter(handlerLock);
+
+ HANDLER_FUNCTION(node) = func;
+
+ node->handlerID = external? ++requestIdCounter : 0;
+ error = eventFilterRestricted_install(node);
+ if (error == JVMTI_ERROR_NONE) {
+ insert(getHandlerChain(node->ei), node);
+ }
+
+ debugMonitorExit(handlerLock);
+
+ return error;
+}
+
+static HandlerNode *
+createInternal(EventIndex ei, HandlerFunction func,
+ jthread thread, jclass clazz, jmethodID method,
+ jlocation location, jboolean permanent)
+{
+ jint index = 0;
+ jvmtiError error = JVMTI_ERROR_NONE;
+ HandlerNode *node;
+
+ /*
+ * Start with necessary allocations
+ */
+ node = eventHandler_alloc(
+ ((thread == NULL)? 0 : 1) + ((clazz == NULL)? 0 : 1),
+ ei, JDWP_SUSPEND_POLICY(NONE));
+ if (node == NULL) {
+ return NULL;
+ }
+
+ node->permanent = permanent;
+
+ if (thread != NULL) {
+ error = eventFilter_setThreadOnlyFilter(node, index++, thread);
+ }
+
+ if ((error == JVMTI_ERROR_NONE) && (clazz != NULL)) {
+ error = eventFilter_setLocationOnlyFilter(node, index++, clazz,
+ method, location);
+ }
+ /*
+ * Create the new handler node
+ */
+ error = installHandler(node, func, JNI_FALSE);
+
+ if (error != JVMTI_ERROR_NONE) {
+ (void)eventHandler_free(node);
+ node = NULL;
+ }
+ return node;
+}
+
+HandlerNode *
+eventHandler_createPermanentInternal(EventIndex ei, HandlerFunction func)
+{
+ return createInternal(ei, func, NULL,
+ NULL, NULL, (jlocation)NULL, JNI_TRUE);
+}
+
+HandlerNode *
+eventHandler_createInternalThreadOnly(EventIndex ei,
+ HandlerFunction func,
+ jthread thread)
+{
+ return createInternal(ei, func, thread,
+ NULL, NULL, (jlocation)NULL, JNI_FALSE);
+}
+
+HandlerNode *
+eventHandler_createInternalBreakpoint(HandlerFunction func,
+ jthread thread,
+ jclass clazz,
+ jmethodID method,
+ jlocation location)
+{
+ return createInternal(EI_BREAKPOINT, func, thread,
+ clazz, method, location, JNI_FALSE);
+}
+
+jvmtiError
+eventHandler_installExternal(HandlerNode *node)
+{
+ return installHandler(node,
+ standardHandlers_defaultHandler(node->ei),
+ JNI_TRUE);
+}