/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* 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.
*/
/*
* 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"
/* Counter of active callbacks and flag for vm_death */
static int active_callbacks = 0;
/* 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 */ \
if (vm_death_callback_active) { \
/* allow VM_DEATH callback to finish */ \
/* Now block because VM is about to die */ \
} else { \
active_callbacks++; \
} \
} \
if ( !bypass ) { \
/* BODY OF CALLBACK CODE */
active_callbacks--; \
if (active_callbacks < 0) { \
EXIT_ERROR(0, "Problems tracking active callbacks"); \
} \
if (vm_death_callback_active) { \
if (active_callbacks == 0) { \
} \
/* allow VM_DEATH callback to finish */ \
/* Now block because VM is about to die */ \
} else { \
} \
} \
} \
} /* 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.
* can access the chains simultaneously while reading (the
* normal activity of an event callback).
*/
typedef struct HandlerChain_ {
/* add lock here */
} HandlerChain;
/*
* This array maps event kinds to handler chains.
* Protected by handlerLock.
*/
/* Given a HandlerNode, these access our private data.
*/
static HandlerChain *
{
}
return &(__handlers[i-EI_min]);
}
static void
{
}
}
static HandlerNode *
{
return node;
}
}
return NULL;
}
static HandlerNode *
{
}
/**
* Deinsert. Safe for non-inserted nodes.
*/
static void
{
return;
}
}
}
}
}
{
}
return JNI_TRUE;
}
}
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) 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
* 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.
*
* 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
{
switch (ei) {
case EI_METHOD_ENTRY:
if (!isMethodNative(method)) {
if (error == JVMTI_ERROR_NONE) {
== JVMTI_ENABLE;
if (!deferring) {
}
}
}
break;
case EI_SINGLE_STEP:
if (!deferring) {
}
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
{
if (ei == EI_BREAKPOINT) {
LOG_MISC(("Co-located breakpoint event found: "
"%s,thread=%p,clazz=%p,method=%p,location=%d",
}
}
return skipping;
}
static void
{
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.
*/
LOG_MISC(("event report being skipped: "
"ei=%s,thread=%p,clazz=%p,method=%p,location=%d",
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 (completedBag == NULL) {
/*
* TO DO: Report, but don't terminate?
*/
return;
} else {
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.
*/
if (invoking) {
}
} while (invoking);
}
}
}
}
/* 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
{
char *classname;
/* TO DO: Report, but don't die
*/
}
/* Signature needs to last, so convert extra copy to
* classname
*/
/* save next so handlers can remove themselves */
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.
*/
eventBag);
}
if (shouldDelete) {
/* We can safely free the node now that we are done
* using it.
*/
(void)freeHandler(node);
}
}
/*
* bag was created locally, destroy it here.
*/
}
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
{
/* 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.
*/
/* 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 ) {
/* We want to compact the hash table of all
* objects sent to the front end by removing objects that have
* been collected.
*/
/* We also need to simulate the class unload events. */
/* Clear garbage collection counter */
garbageCollected = 0;
/* Analyze which class unloads occurred */
if ( unloadedSignatures != NULL ) {
(void *)env);
}
}
/*
* 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.
*/
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.
*/
if (invoking) {
}
} while (invoking);
return; /* Do nothing, event was consumed */
}
} else {
/*
* TO DO: Report, but don't die
*/
}
}
{
char *classname;
/* We must keep track of all classes prepared to know what's unloaded */
}
/* save next so handlers can remove themselves */
&shouldDelete)) {
}
}
if (shouldDelete) {
/* We can safely free the node now that we are done
* using it.
*/
(void)freeHandler(node);
}
}
}
}
/* we are continuing after VMDeathEvent - now we are dead */
}
/*
* If the bag was created locally, destroy it here.
*/
}
/* 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) {
} else {
}
/*
* Release thread resources and perform any delayed operations.
*/
}
}
/* Returns a local ref to the declaring class for an object. */
static jclass
{
return clazz;
}
/* Returns a local ref to the declaring class for a method, or NULL. */
{
return NULL;
}
if ( error != JVMTI_ERROR_NONE ) {
return NULL;
}
return clazz;
}
/* Event callback for JVMTI_EVENT_SINGLE_STEP */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbSingleStep"));
}
/* Event callback for JVMTI_EVENT_BREAKPOINT */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbBreakpoint"));
}
/* Event callback for JVMTI_EVENT_FRAME_POP */
static void JNICALL
{
/* JDWP does not return these events when popped due to an exception. */
if ( wasPoppedByException ) {
return;
}
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbFramePop"));
}
/* Event callback for JVMTI_EVENT_EXCEPTION */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbException"));
}
/* Event callback for JVMTI_EVENT_THREAD_START */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbThreadStart"));
}
/* Event callback for JVMTI_EVENT_THREAD_END */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbThreadEnd"));
}
/* Event callback for JVMTI_EVENT_CLASS_PREPARE */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbClassPrepare"));
}
/* Event callback for JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
static void JNICALL
{
LOG_CB(("cbGarbageCollectionFinish"));
LOG_MISC(("END cbGarbageCollectionFinish"));
}
/* Event callback for JVMTI_EVENT_CLASS_LOAD */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbClassLoad"));
}
/* Event callback for JVMTI_EVENT_FIELD_ACCESS */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbFieldAccess"));
}
/* Event callback for JVMTI_EVENT_FIELD_MODIFICATION */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbFieldModification"));
}
/* Event callback for JVMTI_EVENT_EXCEPTION_CATCH */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbExceptionCatch"));
}
/* Event callback for JVMTI_EVENT_METHOD_ENTRY */
static void JNICALL
{
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbMethodEntry"));
}
/* Event callback for JVMTI_EVENT_METHOD_EXIT */
static void JNICALL
{
/* JDWP does not return these events when popped due to an exception. */
if ( wasPoppedByException ) {
return;
}
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbMethodExit"));
}
/* Event callback for JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
static void JNICALL
{
BEGIN_CALLBACK() {
/* get current location of contended monitor enter */
if (error == JVMTI_ERROR_NONE) {
} else {
}
} END_CALLBACK();
LOG_MISC(("END cbMonitorContendedEnter"));
}
/* Event callback for JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
static void JNICALL
{
BEGIN_CALLBACK() {
/* get current location of contended monitor enter */
if (error == JVMTI_ERROR_NONE) {
} else {
}
} END_CALLBACK();
LOG_MISC(("END cbMonitorContendedEntered"));
}
/* Event callback for JVMTI_EVENT_MONITOR_WAIT */
static void JNICALL
{
BEGIN_CALLBACK() {
/* 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
*/
/* get location of monitor wait() method. */
if (error == JVMTI_ERROR_NONE) {
} else {
}
} END_CALLBACK();
LOG_MISC(("END cbMonitorWait"));
}
/* Event callback for JVMTI_EVENT_MONITOR_WAIT */
static void JNICALL
{
BEGIN_CALLBACK() {
/* 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
*/
/* get location of monitor wait() method */
if (error == JVMTI_ERROR_NONE) {
} else {
}
} END_CALLBACK();
LOG_MISC(("END cbMonitorWaited"));
}
/* Event callback for JVMTI_EVENT_VM_INIT */
static void JNICALL
{
LOG_CB(("cbVMInit"));
BEGIN_CALLBACK() {
} END_CALLBACK();
LOG_MISC(("END cbVMInit"));
}
/* Event callback for JVMTI_EVENT_VM_DEATH */
static void JNICALL
{
LOG_CB(("cbVMDeath"));
/* Clear out ALL callbacks at this time, we don't want any more. */
/* This should prevent any new BEGIN_CALLBACK() calls. */
if (error != JVMTI_ERROR_NONE) {
}
/* 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.
*/
(void)threadControl_resumeAll();
while (active_callbacks > 0) {
/* wait for active CALLBACKs to check in (and block) */
}
/* Only now should we actually process the VM death event */
/* 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.
*/
/*
* 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.
*/
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
/* deinsert the handler node before disableEvents() to make
* sure the event will be disabled when no other event
* handlers are installed.
*/
}
return error;
}
/**
* Delete all the handlers on this chain (do not delete permanent handlers).
* Assumes handlerLock held.
*/
static jvmtiError
{
if ( singleError != JVMTI_ERROR_NONE ) {
error = singleError;
}
}
return error;
}
/**
* Deinsert and free all memory. Safe for non-inserted nodes.
*/
{
return error;
}
/**
* Free all handlers of this kind created by the JDWP client,
* that is, doesn't free handlers internally created by back-end.
*/
{
if (error != JVMTI_ERROR_NONE) {
break;
}
}
}
return error;
}
/***
* Delete all breakpoints on "clazz".
*/
void
{
node)) {
(void)freeHandler(node);
}
}
}
{
} else {
/* already freed */
}
return error;
}
void
{
jint i;
requestIdCounter = 1;
/* This is for BEGIN_CALLBACK/END_CALLBACK handling, make sure this
* is done while none of these callbacks are active.
*/
active_callbacks = 0;
}
/*
* Permanently enabled some events.
*/
EI_VM_INIT, NULL);
if (error != JVMTI_ERROR_NONE) {
}
EI_VM_DEATH, NULL);
if (error != JVMTI_ERROR_NONE) {
}
if (error != JVMTI_ERROR_NONE) {
}
if (error != JVMTI_ERROR_NONE) {
}
if (error != JVMTI_ERROR_NONE) {
}
EI_GC_FINISH, NULL);
if (error != JVMTI_ERROR_NONE) {
}
/* Event callback for JVMTI_EVENT_SINGLE_STEP */
/* Event callback for JVMTI_EVENT_BREAKPOINT */
/* Event callback for JVMTI_EVENT_FRAME_POP */
/* Event callback for JVMTI_EVENT_EXCEPTION */
/* Event callback for JVMTI_EVENT_THREAD_START */
/* Event callback for JVMTI_EVENT_THREAD_END */
/* Event callback for JVMTI_EVENT_CLASS_PREPARE */
/* Event callback for JVMTI_EVENT_CLASS_LOAD */
/* Event callback for JVMTI_EVENT_FIELD_ACCESS */
/* Event callback for JVMTI_EVENT_FIELD_MODIFICATION */
/* Event callback for JVMTI_EVENT_EXCEPTION_CATCH */
/* Event callback for JVMTI_EVENT_METHOD_ENTRY */
/* Event callback for JVMTI_EVENT_METHOD_EXIT */
/* Event callback for JVMTI_EVENT_MONITOR_CONTENDED_ENTER */
/* Event callback for JVMTI_EVENT_MONITOR_CONTENDED_ENTERED */
/* Event callback for JVMTI_EVENT_MONITOR_WAIT */
/* Event callback for JVMTI_EVENT_MONITOR_WAITED */
/* Event callback for JVMTI_EVENT_VM_INIT */
/* Event callback for JVMTI_EVENT_VM_DEATH */
/* Event callback for JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */
if (error != JVMTI_ERROR_NONE) {
}
/* Notify other modules that the event callbacks are in place */
/* Get the event helper thread initialized */
}
void
{
int i;
/* 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.
*/
/* Reset the event helper thread, purging all queued and
* in-process commands.
*/
/* delete all handlers */
(void)freeHandlerChain(getHandlerChain(i));
}
requestIdCounter = 1;
}
void
eventHandler_lock(void)
{
}
void
eventHandler_unlock(void)
{
}
/***** handler creation *****/
{
}
return node;
}
{
return handlerID;
}
static jvmtiError
{
return AGENT_ERROR_INVALID_EVENT_TYPE;
}
if (error == JVMTI_ERROR_NONE) {
}
return error;
}
static HandlerNode *
{
/*
* Start with necessary allocations
*/
return NULL;
}
}
}
/*
* Create the new handler node
*/
if (error != JVMTI_ERROR_NONE) {
(void)eventHandler_free(node);
}
return node;
}
{
}
{
}
{
}
{
return installHandler(node,
JNI_TRUE);
}