/*
* Copyright (c) 2001, 2005, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation. Oracle designates this
* particular file as subject to the "Classpath" exception as provided
* by Oracle in the LICENSE file that accompanied this code.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/
/*
* handlers
*
* The default event request handler functions
*/
#include "util.h"
#include "eventHandler.h"
#include "threadControl.h"
#include "eventHelper.h"
#include "classTrack.h"
#include "standardHandlers.h"
static void
handleClassPrepare(JNIEnv *env, EventInfo *evinfo,
HandlerNode *node,
struct bag *eventBag)
{
jthread thread = evinfo->thread;
/* We try hard to avoid class loads/prepares in debugger
* threads, but it is still possible for them to happen (most
* likely for exceptions that are thrown within JNI
* methods). If such an event occurs, we must report it, but
* we cannot suspend the debugger thread.
*
* 1) We report the thread as NULL because we don't want the
* application to get hold of a debugger thread object.
* 2) We try to do the right thing wrt to suspending
* threads without suspending debugger threads. If the
* requested suspend policy is NONE, there's no problem. If
* the requested policy is ALL, we can just suspend all
* application threads without producing any surprising
* results by leaving the debugger thread running. However,
* if the requested policy is EVENT_THREAD, we are forced
* to do something different than requested. The most
* useful behavior is to suspend all application threads
* (just as if the policy was ALL). This allows the
* application to operate on the class before it gets into
* circulation and so it is preferable to the other
* alternative of suspending no threads.
*/
if (threadControl_isDebugThread(thread)) {
evinfo->thread = NULL;
if (node->suspendPolicy == JDWP_SUSPEND_POLICY(EVENT_THREAD)) {
node->suspendPolicy = JDWP_SUSPEND_POLICY(ALL);
}
}
eventHelper_recordEvent(evinfo, node->handlerID,
node->suspendPolicy, eventBag);
}
static void
handleGarbageCollectionFinish(JNIEnv *env, EventInfo *evinfo,
HandlerNode *node,
struct bag *eventBag)
{
JDI_ASSERT_MSG(JNI_FALSE, "Should never call handleGarbageCollectionFinish");
}
static void
handleFrameEvent(JNIEnv *env, EventInfo *evinfo,
HandlerNode *node,
struct bag *eventBag)
{
/*
* The frame id that comes with this event is very transient.
* We can't send the frame to the helper thread because it
* might be useless by the time the helper thread can use it
* (if suspend policy is NONE). So, get the needed info from
* the frame and then use a special command to the helper
* thread.
*/
jmethodID method;
jlocation location;
jvmtiError error;
FrameNumber fnum = 0;
jvalue returnValue;
error = JVMTI_FUNC_PTR(gdata->jvmti,GetFrameLocation)
(gdata->jvmti, evinfo->thread, fnum, &method, &location);
if (error != JVMTI_ERROR_NONE) {
location = -1;
}
returnValue = evinfo->u.method_exit.return_value;
eventHelper_recordFrameEvent(node->handlerID,
node->suspendPolicy,
evinfo->ei,
evinfo->thread,
evinfo->clazz,
evinfo->method,
location,
node->needReturnValue,
returnValue,
eventBag);
}
static void
genericHandler(JNIEnv *env, EventInfo *evinfo,
HandlerNode *node,
struct bag *eventBag)
{
eventHelper_recordEvent(evinfo, node->handlerID, node->suspendPolicy,
eventBag);
}
HandlerFunction
standardHandlers_defaultHandler(EventIndex ei)
{
switch (ei) {
case EI_BREAKPOINT:
case EI_EXCEPTION:
case EI_FIELD_ACCESS:
case EI_FIELD_MODIFICATION:
case EI_SINGLE_STEP:
case EI_THREAD_START:
case EI_THREAD_END:
case EI_VM_DEATH:
case EI_MONITOR_CONTENDED_ENTER:
case EI_MONITOR_CONTENDED_ENTERED:
case EI_MONITOR_WAIT:
case EI_MONITOR_WAITED:
return &genericHandler;
case EI_CLASS_PREPARE:
return &handleClassPrepare;
case EI_GC_FINISH:
return &handleGarbageCollectionFinish;
case EI_METHOD_ENTRY:
case EI_METHOD_EXIT:
return &handleFrameEvent;
default:
/* This NULL will trigger a AGENT_ERROR_INVALID_EVENT_TYPE */
return NULL;
}
}
void
standardHandlers_onConnect(void)
{
jboolean installed;
/* always report VMDeath to a connected debugger */
installed = (eventHandler_createPermanentInternal(
EI_VM_DEATH, genericHandler) != NULL);
if (!installed) {
EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"Unable to install VM Death event handler");
}
}
void
standardHandlers_onDisconnect(void)
{
}