--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c Tue Sep 12 19:03:39 2017 +0200
@@ -0,0 +1,1123 @@
+/*
+ * Copyright (c) 1998, 2017, 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.
+ */
+
+#include "util.h"
+#include "outStream.h"
+#include "eventHandler.h"
+#include "threadControl.h"
+#include "invoker.h"
+
+/*
+ * Event helper thread command commandKinds
+ */
+#define COMMAND_REPORT_EVENT_COMPOSITE 1
+#define COMMAND_REPORT_INVOKE_DONE 2
+#define COMMAND_REPORT_VM_INIT 3
+#define COMMAND_SUSPEND_THREAD 4
+
+/*
+ * Event helper thread command singleKinds
+ */
+#define COMMAND_SINGLE_EVENT 11
+#define COMMAND_SINGLE_UNLOAD 12
+#define COMMAND_SINGLE_FRAME_EVENT 13
+
+typedef struct EventCommandSingle {
+ jbyte suspendPolicy; /* NOTE: Must be the first field */
+ jint id;
+ EventInfo info;
+} EventCommandSingle;
+
+typedef struct UnloadCommandSingle {
+ char *classSignature;
+ jint id;
+} UnloadCommandSingle;
+
+typedef struct FrameEventCommandSingle {
+ jbyte suspendPolicy; /* NOTE: Must be the first field */
+ jint id;
+ EventIndex ei;
+ jthread thread;
+ jclass clazz;
+ jmethodID method;
+ jlocation location;
+ char typeKey; /* Not used for method entry events */
+ /* If typeKey is 0, then no return value is needed */
+ jvalue returnValue; /* Not used for method entry events */
+} FrameEventCommandSingle;
+
+typedef struct CommandSingle {
+ jint singleKind;
+ union {
+ EventCommandSingle eventCommand;
+ UnloadCommandSingle unloadCommand;
+ FrameEventCommandSingle frameEventCommand;
+ } u;
+} CommandSingle;
+
+typedef struct ReportInvokeDoneCommand {
+ jthread thread;
+} ReportInvokeDoneCommand;
+
+typedef struct ReportVMInitCommand {
+ jbyte suspendPolicy; /* NOTE: Must be the first field */
+ jthread thread;
+} ReportVMInitCommand;
+
+typedef struct SuspendThreadCommand {
+ jthread thread;
+} SuspendThreadCommand;
+
+typedef struct ReportEventCompositeCommand {
+ jbyte suspendPolicy; /* NOTE: Must be the first field */
+ jint eventCount;
+ CommandSingle singleCommand[1]; /* variable length */
+} ReportEventCompositeCommand;
+
+typedef struct HelperCommand {
+ jint commandKind;
+ jboolean done;
+ jboolean waiting;
+ jbyte sessionID;
+ struct HelperCommand *next;
+ union {
+ /* NOTE: Each of the structs below must have the same first field */
+ ReportEventCompositeCommand reportEventComposite;
+ ReportInvokeDoneCommand reportInvokeDone;
+ ReportVMInitCommand reportVMInit;
+ SuspendThreadCommand suspendThread;
+ } u;
+ /* composite array expand out, put nothing after */
+} HelperCommand;
+
+typedef struct {
+ HelperCommand *head;
+ HelperCommand *tail;
+} CommandQueue;
+
+static CommandQueue commandQueue;
+static jrawMonitorID commandQueueLock;
+static jrawMonitorID commandCompleteLock;
+static jrawMonitorID blockCommandLoopLock;
+static jint maxQueueSize = 50 * 1024; /* TO DO: Make this configurable */
+static jboolean holdEvents;
+static jint currentQueueSize = 0;
+static jint currentSessionID;
+
+static void saveEventInfoRefs(JNIEnv *env, EventInfo *evinfo);
+static void tossEventInfoRefs(JNIEnv *env, EventInfo *evinfo);
+
+static jint
+commandSize(HelperCommand *command)
+{
+ jint size = sizeof(HelperCommand);
+ if (command->commandKind == COMMAND_REPORT_EVENT_COMPOSITE) {
+ /*
+ * One event is accounted for in the Helper Command. If there are
+ * more, add to size here.
+ */
+ /*LINTED*/
+ size += ((int)sizeof(CommandSingle) *
+ (command->u.reportEventComposite.eventCount - 1));
+ }
+ return size;
+}
+
+static void
+freeCommand(HelperCommand *command)
+{
+ if ( command == NULL )
+ return;
+ jvmtiDeallocate(command);
+}
+
+static void
+enqueueCommand(HelperCommand *command,
+ jboolean wait, jboolean reportingVMDeath)
+{
+ static jboolean vmDeathReported = JNI_FALSE;
+ CommandQueue *queue = &commandQueue;
+ jint size = commandSize(command);
+
+ command->done = JNI_FALSE;
+ command->waiting = wait;
+ command->next = NULL;
+
+ debugMonitorEnter(commandQueueLock);
+ while (size + currentQueueSize > maxQueueSize) {
+ debugMonitorWait(commandQueueLock);
+ }
+ log_debugee_location("enqueueCommand(): HelperCommand being processed", NULL, NULL, 0);
+ if (vmDeathReported) {
+ /* send no more events after VMDeath and don't wait */
+ wait = JNI_FALSE;
+ } else {
+ currentQueueSize += size;
+
+ if (queue->head == NULL) {
+ queue->head = command;
+ } else {
+ queue->tail->next = command;
+ }
+ queue->tail = command;
+
+ if (reportingVMDeath) {
+ vmDeathReported = JNI_TRUE;
+ }
+ }
+ debugMonitorNotifyAll(commandQueueLock);
+ debugMonitorExit(commandQueueLock);
+
+ if (wait) {
+ debugMonitorEnter(commandCompleteLock);
+ while (!command->done) {
+ log_debugee_location("enqueueCommand(): HelperCommand wait", NULL, NULL, 0);
+ debugMonitorWait(commandCompleteLock);
+ }
+ freeCommand(command);
+ debugMonitorExit(commandCompleteLock);
+ }
+}
+
+static void
+completeCommand(HelperCommand *command)
+{
+ if (command->waiting) {
+ debugMonitorEnter(commandCompleteLock);
+ command->done = JNI_TRUE;
+ log_debugee_location("completeCommand(): HelperCommand done waiting", NULL, NULL, 0);
+ debugMonitorNotifyAll(commandCompleteLock);
+ debugMonitorExit(commandCompleteLock);
+ } else {
+ freeCommand(command);
+ }
+}
+
+static HelperCommand *
+dequeueCommand(void)
+{
+ HelperCommand *command = NULL;
+ CommandQueue *queue = &commandQueue;
+ jint size;
+
+ debugMonitorEnter(commandQueueLock);
+
+ while (command == NULL) {
+ while (holdEvents || (queue->head == NULL)) {
+ debugMonitorWait(commandQueueLock);
+ }
+
+ JDI_ASSERT(queue->head);
+ command = queue->head;
+ queue->head = command->next;
+ if (queue->tail == command) {
+ queue->tail = NULL;
+ }
+
+ log_debugee_location("dequeueCommand(): command being dequeued", NULL, NULL, 0);
+
+ size = commandSize(command);
+ /*
+ * Immediately close out any commands enqueued from
+ * a dead VM or a previously attached debugger.
+ */
+ if (gdata->vmDead || command->sessionID != currentSessionID) {
+ log_debugee_location("dequeueCommand(): command session removal", NULL, NULL, 0);
+ completeCommand(command);
+ command = NULL;
+ }
+
+ /*
+ * There's room in the queue for more.
+ */
+ currentQueueSize -= size;
+ debugMonitorNotifyAll(commandQueueLock);
+ }
+
+ debugMonitorExit(commandQueueLock);
+
+ return command;
+}
+
+void eventHelper_holdEvents(void)
+{
+ debugMonitorEnter(commandQueueLock);
+ holdEvents = JNI_TRUE;
+ debugMonitorNotifyAll(commandQueueLock);
+ debugMonitorExit(commandQueueLock);
+}
+
+void eventHelper_releaseEvents(void)
+{
+ debugMonitorEnter(commandQueueLock);
+ holdEvents = JNI_FALSE;
+ debugMonitorNotifyAll(commandQueueLock);
+ debugMonitorExit(commandQueueLock);
+}
+
+static void
+writeSingleStepEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo)
+{
+ (void)outStream_writeObjectRef(env, out, evinfo->thread);
+ writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location);
+}
+
+static void
+writeBreakpointEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo)
+{
+ (void)outStream_writeObjectRef(env, out, evinfo->thread);
+ writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location);
+}
+
+static void
+writeFieldAccessEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo)
+{
+ jbyte fieldClassTag;
+
+ fieldClassTag = referenceTypeTag(evinfo->u.field_access.field_clazz);
+
+ (void)outStream_writeObjectRef(env, out, evinfo->thread);
+ writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location);
+ (void)outStream_writeByte(out, fieldClassTag);
+ (void)outStream_writeObjectRef(env, out, evinfo->u.field_access.field_clazz);
+ (void)outStream_writeFieldID(out, evinfo->u.field_access.field);
+ (void)outStream_writeObjectTag(env, out, evinfo->object);
+ (void)outStream_writeObjectRef(env, out, evinfo->object);
+}
+
+static void
+writeFieldModificationEvent(JNIEnv *env, PacketOutputStream *out,
+ EventInfo *evinfo)
+{
+ jbyte fieldClassTag;
+
+ fieldClassTag = referenceTypeTag(evinfo->u.field_modification.field_clazz);
+
+ (void)outStream_writeObjectRef(env, out, evinfo->thread);
+ writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location);
+ (void)outStream_writeByte(out, fieldClassTag);
+ (void)outStream_writeObjectRef(env, out, evinfo->u.field_modification.field_clazz);
+ (void)outStream_writeFieldID(out, evinfo->u.field_modification.field);
+ (void)outStream_writeObjectTag(env, out, evinfo->object);
+ (void)outStream_writeObjectRef(env, out, evinfo->object);
+ (void)outStream_writeValue(env, out, (jbyte)evinfo->u.field_modification.signature_type,
+ evinfo->u.field_modification.new_value);
+}
+
+static void
+writeExceptionEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo)
+{
+ (void)outStream_writeObjectRef(env, out, evinfo->thread);
+ writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location);
+ (void)outStream_writeObjectTag(env, out, evinfo->object);
+ (void)outStream_writeObjectRef(env, out, evinfo->object);
+ writeCodeLocation(out, evinfo->u.exception.catch_clazz,
+ evinfo->u.exception.catch_method, evinfo->u.exception.catch_location);
+}
+
+static void
+writeThreadEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo)
+{
+ (void)outStream_writeObjectRef(env, out, evinfo->thread);
+}
+
+static void
+writeMonitorEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo)
+{
+ jclass klass;
+ (void)outStream_writeObjectRef(env, out, evinfo->thread);
+ (void)outStream_writeObjectTag(env, out, evinfo->object);
+ (void)outStream_writeObjectRef(env, out, evinfo->object);
+ if (evinfo->ei == EI_MONITOR_WAIT || evinfo->ei == EI_MONITOR_WAITED) {
+ /* clazz of evinfo was set to class of monitor object for monitor wait event class filtering.
+ * So get the method class to write location info.
+ * See cbMonitorWait() and cbMonitorWaited() function in eventHandler.c.
+ */
+ klass=getMethodClass(gdata->jvmti, evinfo->method);
+ writeCodeLocation(out, klass, evinfo->method, evinfo->location);
+ if (evinfo->ei == EI_MONITOR_WAIT) {
+ (void)outStream_writeLong(out, evinfo->u.monitor.timeout);
+ } else if (evinfo->ei == EI_MONITOR_WAITED) {
+ (void)outStream_writeBoolean(out, evinfo->u.monitor.timed_out);
+ }
+ /* This runs in a command loop and this thread may not return to java.
+ * So we need to delete the local ref created by jvmti GetMethodDeclaringClass.
+ */
+ JNI_FUNC_PTR(env,DeleteLocalRef)(env, klass);
+ } else {
+ writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location);
+ }
+}
+
+static void
+writeClassEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo)
+{
+ jbyte classTag;
+ jint status;
+ char *signature = NULL;
+ jvmtiError error;
+
+ classTag = referenceTypeTag(evinfo->clazz);
+ error = classSignature(evinfo->clazz, &signature, NULL);
+ if (error != JVMTI_ERROR_NONE) {
+ EXIT_ERROR(error,"signature");
+ }
+ status = classStatus(evinfo->clazz);
+
+ (void)outStream_writeObjectRef(env, out, evinfo->thread);
+ (void)outStream_writeByte(out, classTag);
+ (void)outStream_writeObjectRef(env, out, evinfo->clazz);
+ (void)outStream_writeString(out, signature);
+ (void)outStream_writeInt(out, map2jdwpClassStatus(status));
+ jvmtiDeallocate(signature);
+}
+
+static void
+writeVMDeathEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo)
+{
+}
+
+static void
+handleEventCommandSingle(JNIEnv *env, PacketOutputStream *out,
+ EventCommandSingle *command)
+{
+ EventInfo *evinfo = &command->info;
+
+ (void)outStream_writeByte(out, eventIndex2jdwp(evinfo->ei));
+ (void)outStream_writeInt(out, command->id);
+
+ switch (evinfo->ei) {
+ case EI_SINGLE_STEP:
+ writeSingleStepEvent(env, out, evinfo);
+ break;
+ case EI_BREAKPOINT:
+ writeBreakpointEvent(env, out, evinfo);
+ break;
+ case EI_FIELD_ACCESS:
+ writeFieldAccessEvent(env, out, evinfo);
+ break;
+ case EI_FIELD_MODIFICATION:
+ writeFieldModificationEvent(env, out, evinfo);
+ break;
+ case EI_EXCEPTION:
+ writeExceptionEvent(env, out, evinfo);
+ break;
+ case EI_THREAD_START:
+ case EI_THREAD_END:
+ writeThreadEvent(env, out, evinfo);
+ break;
+ case EI_CLASS_LOAD:
+ case EI_CLASS_PREPARE:
+ writeClassEvent(env, out, evinfo);
+ break;
+ case EI_MONITOR_CONTENDED_ENTER:
+ case EI_MONITOR_CONTENDED_ENTERED:
+ case EI_MONITOR_WAIT:
+ case EI_MONITOR_WAITED:
+ writeMonitorEvent(env, out, evinfo);
+ break;
+ case EI_VM_DEATH:
+ writeVMDeathEvent(env, out, evinfo);
+ break;
+ default:
+ EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"unknown event index");
+ break;
+ }
+ tossEventInfoRefs(env, evinfo);
+}
+
+static void
+handleUnloadCommandSingle(JNIEnv* env, PacketOutputStream *out,
+ UnloadCommandSingle *command)
+{
+ (void)outStream_writeByte(out, JDWP_EVENT(CLASS_UNLOAD));
+ (void)outStream_writeInt(out, command->id);
+ (void)outStream_writeString(out, command->classSignature);
+ jvmtiDeallocate(command->classSignature);
+ command->classSignature = NULL;
+}
+
+static void
+handleFrameEventCommandSingle(JNIEnv* env, PacketOutputStream *out,
+ FrameEventCommandSingle *command)
+{
+ if (command->typeKey) {
+ (void)outStream_writeByte(out, JDWP_EVENT(METHOD_EXIT_WITH_RETURN_VALUE));
+ } else {
+ (void)outStream_writeByte(out, eventIndex2jdwp(command->ei));
+ }
+ (void)outStream_writeInt(out, command->id);
+ (void)outStream_writeObjectRef(env, out, command->thread);
+ writeCodeLocation(out, command->clazz, command->method, command->location);
+ if (command->typeKey) {
+ (void)outStream_writeValue(env, out, command->typeKey, command->returnValue);
+ if (isObjectTag(command->typeKey) &&
+ command->returnValue.l != NULL) {
+ tossGlobalRef(env, &(command->returnValue.l));
+ }
+ }
+ tossGlobalRef(env, &(command->thread));
+ tossGlobalRef(env, &(command->clazz));
+}
+
+static void
+suspendWithInvokeEnabled(jbyte policy, jthread thread)
+{
+ invoker_enableInvokeRequests(thread);
+
+ if (policy == JDWP_SUSPEND_POLICY(ALL)) {
+ (void)threadControl_suspendAll();
+ } else {
+ (void)threadControl_suspendThread(thread, JNI_FALSE);
+ }
+}
+
+static void
+handleReportEventCompositeCommand(JNIEnv *env,
+ ReportEventCompositeCommand *recc)
+{
+ PacketOutputStream out;
+ jint count = recc->eventCount;
+ jint i;
+
+ if (recc->suspendPolicy != JDWP_SUSPEND_POLICY(NONE)) {
+ /* must determine thread to interrupt before writing */
+ /* since writing destroys it */
+ jthread thread = NULL;
+ for (i = 0; i < count; i++) {
+ CommandSingle *single = &(recc->singleCommand[i]);
+ switch (single->singleKind) {
+ case COMMAND_SINGLE_EVENT:
+ thread = single->u.eventCommand.info.thread;
+ break;
+ case COMMAND_SINGLE_FRAME_EVENT:
+ thread = single->u.frameEventCommand.thread;
+ break;
+ }
+ if (thread != NULL) {
+ break;
+ }
+ }
+
+ if (thread == NULL) {
+ (void)threadControl_suspendAll();
+ } else {
+ suspendWithInvokeEnabled(recc->suspendPolicy, thread);
+ }
+ }
+
+ outStream_initCommand(&out, uniqueID(), 0x0,
+ JDWP_COMMAND_SET(Event),
+ JDWP_COMMAND(Event, Composite));
+ (void)outStream_writeByte(&out, recc->suspendPolicy);
+ (void)outStream_writeInt(&out, count);
+
+ for (i = 0; i < count; i++) {
+ CommandSingle *single = &(recc->singleCommand[i]);
+ switch (single->singleKind) {
+ case COMMAND_SINGLE_EVENT:
+ handleEventCommandSingle(env, &out,
+ &single->u.eventCommand);
+ break;
+ case COMMAND_SINGLE_UNLOAD:
+ handleUnloadCommandSingle(env, &out,
+ &single->u.unloadCommand);
+ break;
+ case COMMAND_SINGLE_FRAME_EVENT:
+ handleFrameEventCommandSingle(env, &out,
+ &single->u.frameEventCommand);
+ break;
+ }
+ }
+
+ outStream_sendCommand(&out);
+ outStream_destroy(&out);
+}
+
+static void
+handleReportInvokeDoneCommand(JNIEnv* env, ReportInvokeDoneCommand *command)
+{
+ invoker_completeInvokeRequest(command->thread);
+ tossGlobalRef(env, &(command->thread));
+}
+
+static void
+handleReportVMInitCommand(JNIEnv* env, ReportVMInitCommand *command)
+{
+ PacketOutputStream out;
+
+ if (command->suspendPolicy == JDWP_SUSPEND_POLICY(ALL)) {
+ (void)threadControl_suspendAll();
+ } else if (command->suspendPolicy == JDWP_SUSPEND_POLICY(EVENT_THREAD)) {
+ (void)threadControl_suspendThread(command->thread, JNI_FALSE);
+ }
+
+ outStream_initCommand(&out, uniqueID(), 0x0,
+ JDWP_COMMAND_SET(Event),
+ JDWP_COMMAND(Event, Composite));
+ (void)outStream_writeByte(&out, command->suspendPolicy);
+ (void)outStream_writeInt(&out, 1); /* Always one component */
+ (void)outStream_writeByte(&out, JDWP_EVENT(VM_INIT));
+ (void)outStream_writeInt(&out, 0); /* Not in response to an event req. */
+
+ (void)outStream_writeObjectRef(env, &out, command->thread);
+
+ outStream_sendCommand(&out);
+ outStream_destroy(&out);
+ /* Why aren't we tossing this: tossGlobalRef(env, &(command->thread)); */
+}
+
+static void
+handleSuspendThreadCommand(JNIEnv* env, SuspendThreadCommand *command)
+{
+ /*
+ * For the moment, there's nothing that can be done with the
+ * return code, so we don't check it here.
+ */
+ (void)threadControl_suspendThread(command->thread, JNI_TRUE);
+ tossGlobalRef(env, &(command->thread));
+}
+
+static void
+handleCommand(JNIEnv *env, HelperCommand *command)
+{
+ switch (command->commandKind) {
+ case COMMAND_REPORT_EVENT_COMPOSITE:
+ handleReportEventCompositeCommand(env,
+ &command->u.reportEventComposite);
+ break;
+ case COMMAND_REPORT_INVOKE_DONE:
+ handleReportInvokeDoneCommand(env, &command->u.reportInvokeDone);
+ break;
+ case COMMAND_REPORT_VM_INIT:
+ handleReportVMInitCommand(env, &command->u.reportVMInit);
+ break;
+ case COMMAND_SUSPEND_THREAD:
+ handleSuspendThreadCommand(env, &command->u.suspendThread);
+ break;
+ default:
+ EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"Event Helper Command");
+ break;
+ }
+}
+
+/*
+ * There was an assumption that only one event with a suspend-all
+ * policy could be processed by commandLoop() at one time. It was
+ * assumed that native thread suspension from the first suspend-all
+ * event would prevent the second suspend-all event from making it
+ * into the command queue. For the Classic VM, this was a reasonable
+ * assumption. However, in HotSpot all thread suspension requires a
+ * VM operation and VM operations take time.
+ *
+ * The solution is to add a mechanism to prevent commandLoop() from
+ * processing more than one event with a suspend-all policy. This is
+ * accomplished by forcing commandLoop() to wait for either
+ * ThreadReferenceImpl.c: resume() or VirtualMachineImpl.c: resume()
+ * when an event with a suspend-all policy has been completed.
+ */
+static jboolean blockCommandLoop = JNI_FALSE;
+
+/*
+ * We wait for either ThreadReferenceImpl.c: resume() or
+ * VirtualMachineImpl.c: resume() to be called.
+ */
+static void
+doBlockCommandLoop(void) {
+ debugMonitorEnter(blockCommandLoopLock);
+ while (blockCommandLoop == JNI_TRUE) {
+ debugMonitorWait(blockCommandLoopLock);
+ }
+ debugMonitorExit(blockCommandLoopLock);
+}
+
+/*
+ * If the command that we are about to execute has a suspend-all
+ * policy, then prepare for either ThreadReferenceImpl.c: resume()
+ * or VirtualMachineImpl.c: resume() to be called.
+ */
+static jboolean
+needBlockCommandLoop(HelperCommand *cmd) {
+ if (cmd->commandKind == COMMAND_REPORT_EVENT_COMPOSITE
+ && cmd->u.reportEventComposite.suspendPolicy == JDWP_SUSPEND_POLICY(ALL)) {
+ debugMonitorEnter(blockCommandLoopLock);
+ blockCommandLoop = JNI_TRUE;
+ debugMonitorExit(blockCommandLoopLock);
+
+ return JNI_TRUE;
+ }
+
+ return JNI_FALSE;
+}
+
+/*
+ * Used by either ThreadReferenceImpl.c: resume() or
+ * VirtualMachineImpl.c: resume() to resume commandLoop().
+ */
+void
+unblockCommandLoop(void) {
+ debugMonitorEnter(blockCommandLoopLock);
+ blockCommandLoop = JNI_FALSE;
+ debugMonitorNotifyAll(blockCommandLoopLock);
+ debugMonitorExit(blockCommandLoopLock);
+}
+
+/*
+ * The event helper thread. Dequeues commands and processes them.
+ */
+static void JNICALL
+commandLoop(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
+{
+ LOG_MISC(("Begin command loop thread"));
+
+ while (JNI_TRUE) {
+ HelperCommand *command = dequeueCommand();
+ if (command != NULL) {
+ /*
+ * Setup for a potential doBlockCommand() call before calling
+ * handleCommand() to prevent any races.
+ */
+ jboolean doBlock = needBlockCommandLoop(command);
+ log_debugee_location("commandLoop(): command being handled", NULL, NULL, 0);
+ handleCommand(jni_env, command);
+ completeCommand(command);
+ /* if we just finished a suspend-all cmd, then we block here */
+ if (doBlock) {
+ doBlockCommandLoop();
+ }
+ }
+ }
+ /* This loop never ends, even as connections come and go with server=y */
+}
+
+void
+eventHelper_initialize(jbyte sessionID)
+{
+ jvmtiStartFunction func;
+
+ currentSessionID = sessionID;
+ holdEvents = JNI_FALSE;
+ commandQueue.head = NULL;
+ commandQueue.tail = NULL;
+
+ commandQueueLock = debugMonitorCreate("JDWP Event Helper Queue Monitor");
+ commandCompleteLock = debugMonitorCreate("JDWP Event Helper Completion Monitor");
+ blockCommandLoopLock = debugMonitorCreate("JDWP Event Block CommandLoop Monitor");
+
+ /* Start the event handler thread */
+ func = &commandLoop;
+ (void)spawnNewThread(func, NULL, "JDWP Event Helper Thread");
+}
+
+void
+eventHelper_reset(jbyte newSessionID)
+{
+ debugMonitorEnter(commandQueueLock);
+ currentSessionID = newSessionID;
+ holdEvents = JNI_FALSE;
+ debugMonitorNotifyAll(commandQueueLock);
+ debugMonitorExit(commandQueueLock);
+}
+
+/*
+ * Provide a means for threadControl to ensure that crucial locks are not
+ * held by suspended threads.
+ */
+void
+eventHelper_lock(void)
+{
+ debugMonitorEnter(commandQueueLock);
+ debugMonitorEnter(commandCompleteLock);
+}
+
+void
+eventHelper_unlock(void)
+{
+ debugMonitorExit(commandCompleteLock);
+ debugMonitorExit(commandQueueLock);
+}
+
+/* Change all references to global in the EventInfo struct */
+static void
+saveEventInfoRefs(JNIEnv *env, EventInfo *evinfo)
+{
+ jthread *pthread;
+ jclass *pclazz;
+ jobject *pobject;
+ jthread thread;
+ jclass clazz;
+ jobject object;
+ char sig;
+
+ JNI_FUNC_PTR(env,ExceptionClear)(env);
+
+ if ( evinfo->thread != NULL ) {
+ pthread = &(evinfo->thread);
+ thread = *pthread;
+ *pthread = NULL;
+ saveGlobalRef(env, thread, pthread);
+ }
+ if ( evinfo->clazz != NULL ) {
+ pclazz = &(evinfo->clazz);
+ clazz = *pclazz;
+ *pclazz = NULL;
+ saveGlobalRef(env, clazz, pclazz);
+ }
+ if ( evinfo->object != NULL ) {
+ pobject = &(evinfo->object);
+ object = *pobject;
+ *pobject = NULL;
+ saveGlobalRef(env, object, pobject);
+ }
+
+ switch (evinfo->ei) {
+ case EI_FIELD_MODIFICATION:
+ if ( evinfo->u.field_modification.field_clazz != NULL ) {
+ pclazz = &(evinfo->u.field_modification.field_clazz);
+ clazz = *pclazz;
+ *pclazz = NULL;
+ saveGlobalRef(env, clazz, pclazz);
+ }
+ sig = evinfo->u.field_modification.signature_type;
+ if ((sig == JDWP_TAG(ARRAY)) || (sig == JDWP_TAG(OBJECT))) {
+ if ( evinfo->u.field_modification.new_value.l != NULL ) {
+ pobject = &(evinfo->u.field_modification.new_value.l);
+ object = *pobject;
+ *pobject = NULL;
+ saveGlobalRef(env, object, pobject);
+ }
+ }
+ break;
+ case EI_FIELD_ACCESS:
+ if ( evinfo->u.field_access.field_clazz != NULL ) {
+ pclazz = &(evinfo->u.field_access.field_clazz);
+ clazz = *pclazz;
+ *pclazz = NULL;
+ saveGlobalRef(env, clazz, pclazz);
+ }
+ break;
+ case EI_EXCEPTION:
+ if ( evinfo->u.exception.catch_clazz != NULL ) {
+ pclazz = &(evinfo->u.exception.catch_clazz);
+ clazz = *pclazz;
+ *pclazz = NULL;
+ saveGlobalRef(env, clazz, pclazz);
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (JNI_FUNC_PTR(env,ExceptionOccurred)(env)) {
+ EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"ExceptionOccurred");
+ }
+}
+
+static void
+tossEventInfoRefs(JNIEnv *env, EventInfo *evinfo)
+{
+ char sig;
+ if ( evinfo->thread != NULL ) {
+ tossGlobalRef(env, &(evinfo->thread));
+ }
+ if ( evinfo->clazz != NULL ) {
+ tossGlobalRef(env, &(evinfo->clazz));
+ }
+ if ( evinfo->object != NULL ) {
+ tossGlobalRef(env, &(evinfo->object));
+ }
+ switch (evinfo->ei) {
+ case EI_FIELD_MODIFICATION:
+ if ( evinfo->u.field_modification.field_clazz != NULL ) {
+ tossGlobalRef(env, &(evinfo->u.field_modification.field_clazz));
+ }
+ sig = evinfo->u.field_modification.signature_type;
+ if ((sig == JDWP_TAG(ARRAY)) || (sig == JDWP_TAG(OBJECT))) {
+ if ( evinfo->u.field_modification.new_value.l != NULL ) {
+ tossGlobalRef(env, &(evinfo->u.field_modification.new_value.l));
+ }
+ }
+ break;
+ case EI_FIELD_ACCESS:
+ if ( evinfo->u.field_access.field_clazz != NULL ) {
+ tossGlobalRef(env, &(evinfo->u.field_access.field_clazz));
+ }
+ break;
+ case EI_EXCEPTION:
+ if ( evinfo->u.exception.catch_clazz != NULL ) {
+ tossGlobalRef(env, &(evinfo->u.exception.catch_clazz));
+ }
+ break;
+ default:
+ break;
+ }
+}
+
+struct bag *
+eventHelper_createEventBag(void)
+{
+ return bagCreateBag(sizeof(CommandSingle), 5 /* events */ );
+}
+
+/* Return the combined suspend policy for the event set
+ */
+static jboolean
+enumForCombinedSuspendPolicy(void *cv, void *arg)
+{
+ CommandSingle *command = cv;
+ jbyte thisPolicy;
+ jbyte *policy = arg;
+
+ switch(command->singleKind) {
+ case COMMAND_SINGLE_EVENT:
+ thisPolicy = command->u.eventCommand.suspendPolicy;
+ break;
+ case COMMAND_SINGLE_FRAME_EVENT:
+ thisPolicy = command->u.frameEventCommand.suspendPolicy;
+ break;
+ default:
+ thisPolicy = JDWP_SUSPEND_POLICY(NONE);
+ }
+ /* Expand running policy value if this policy demands it */
+ if (*policy == JDWP_SUSPEND_POLICY(NONE)) {
+ *policy = thisPolicy;
+ } else if (*policy == JDWP_SUSPEND_POLICY(EVENT_THREAD)) {
+ *policy = (thisPolicy == JDWP_SUSPEND_POLICY(ALL))?
+ thisPolicy : *policy;
+ }
+
+ /* Short circuit if we reached maximal suspend policy */
+ if (*policy == JDWP_SUSPEND_POLICY(ALL)) {
+ return JNI_FALSE;
+ } else {
+ return JNI_TRUE;
+ }
+}
+
+/* Determine whether we are reporting VM death
+ */
+static jboolean
+enumForVMDeath(void *cv, void *arg)
+{
+ CommandSingle *command = cv;
+ jboolean *reportingVMDeath = arg;
+
+ if (command->singleKind == COMMAND_SINGLE_EVENT) {
+ if (command->u.eventCommand.info.ei == EI_VM_DEATH) {
+ *reportingVMDeath = JNI_TRUE;
+ return JNI_FALSE;
+ }
+ }
+ return JNI_TRUE;
+}
+
+struct singleTracker {
+ ReportEventCompositeCommand *recc;
+ int index;
+};
+
+static jboolean
+enumForCopyingSingles(void *command, void *tv)
+{
+ struct singleTracker *tracker = (struct singleTracker *)tv;
+ (void)memcpy(&tracker->recc->singleCommand[tracker->index++],
+ command,
+ sizeof(CommandSingle));
+ return JNI_TRUE;
+}
+
+jbyte
+eventHelper_reportEvents(jbyte sessionID, struct bag *eventBag)
+{
+ int size = bagSize(eventBag);
+ jbyte suspendPolicy = JDWP_SUSPEND_POLICY(NONE);
+ jboolean reportingVMDeath = JNI_FALSE;
+ jboolean wait;
+ int command_size;
+
+ HelperCommand *command;
+ ReportEventCompositeCommand *recc;
+ struct singleTracker tracker;
+
+ if (size == 0) {
+ return suspendPolicy;
+ }
+ (void)bagEnumerateOver(eventBag, enumForCombinedSuspendPolicy, &suspendPolicy);
+ (void)bagEnumerateOver(eventBag, enumForVMDeath, &reportingVMDeath);
+
+ /*LINTED*/
+ command_size = (int)(sizeof(HelperCommand) +
+ sizeof(CommandSingle)*(size-1));
+ command = jvmtiAllocate(command_size);
+ (void)memset(command, 0, command_size);
+ command->commandKind = COMMAND_REPORT_EVENT_COMPOSITE;
+ command->sessionID = sessionID;
+ recc = &command->u.reportEventComposite;
+ recc->suspendPolicy = suspendPolicy;
+ recc->eventCount = size;
+ tracker.recc = recc;
+ tracker.index = 0;
+ (void)bagEnumerateOver(eventBag, enumForCopyingSingles, &tracker);
+
+ /*
+ * We must wait if this thread (the event thread) is to be
+ * suspended or if the VM is about to die. (Waiting in the latter
+ * case ensures that we get the event out before the process dies.)
+ */
+ wait = (jboolean)((suspendPolicy != JDWP_SUSPEND_POLICY(NONE)) ||
+ reportingVMDeath);
+ enqueueCommand(command, wait, reportingVMDeath);
+ return suspendPolicy;
+}
+
+void
+eventHelper_recordEvent(EventInfo *evinfo, jint id, jbyte suspendPolicy,
+ struct bag *eventBag)
+{
+ JNIEnv *env = getEnv();
+ CommandSingle *command = bagAdd(eventBag);
+ if (command == NULL) {
+ EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"badAdd(eventBag)");
+ }
+
+ command->singleKind = COMMAND_SINGLE_EVENT;
+ command->u.eventCommand.suspendPolicy = suspendPolicy;
+ command->u.eventCommand.id = id;
+
+ /*
+ * Copy the event into the command so that it can be used
+ * asynchronously by the event helper thread.
+ */
+ (void)memcpy(&command->u.eventCommand.info, evinfo, sizeof(*evinfo));
+ saveEventInfoRefs(env, &command->u.eventCommand.info);
+}
+
+void
+eventHelper_recordClassUnload(jint id, char *signature, struct bag *eventBag)
+{
+ CommandSingle *command = bagAdd(eventBag);
+ if (command == NULL) {
+ EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"bagAdd(eventBag)");
+ }
+ command->singleKind = COMMAND_SINGLE_UNLOAD;
+ command->u.unloadCommand.id = id;
+ command->u.unloadCommand.classSignature = signature;
+}
+
+void
+eventHelper_recordFrameEvent(jint id, jbyte suspendPolicy, EventIndex ei,
+ jthread thread, jclass clazz,
+ jmethodID method, jlocation location,
+ int needReturnValue,
+ jvalue returnValue,
+ struct bag *eventBag)
+{
+ JNIEnv *env = getEnv();
+ FrameEventCommandSingle *frameCommand;
+ CommandSingle *command = bagAdd(eventBag);
+ jvmtiError err = JVMTI_ERROR_NONE;
+ if (command == NULL) {
+ EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"bagAdd(eventBag)");
+ }
+
+ command->singleKind = COMMAND_SINGLE_FRAME_EVENT;
+ frameCommand = &command->u.frameEventCommand;
+ frameCommand->suspendPolicy = suspendPolicy;
+ frameCommand->id = id;
+ frameCommand->ei = ei;
+ saveGlobalRef(env, thread, &(frameCommand->thread));
+ saveGlobalRef(env, clazz, &(frameCommand->clazz));
+ frameCommand->method = method;
+ frameCommand->location = location;
+ if (needReturnValue) {
+ err = methodReturnType(method, &frameCommand->typeKey);
+ JDI_ASSERT(err == JVMTI_ERROR_NONE);
+
+ /*
+ * V or B C D F I J S Z L <classname> ; [ ComponentType
+ */
+ if (isObjectTag(frameCommand->typeKey) &&
+ returnValue.l != NULL) {
+ saveGlobalRef(env, returnValue.l, &(frameCommand->returnValue.l));
+ } else {
+ frameCommand->returnValue = returnValue;
+ }
+ } else {
+ /* This is not a JDWP METHOD_EXIT_WITH_RETURN_VALUE request,
+ * so signal this by setting typeKey = 0 which is not
+ * a legal typekey.
+ */
+ frameCommand->typeKey = 0;
+ }
+}
+
+void
+eventHelper_reportInvokeDone(jbyte sessionID, jthread thread)
+{
+ JNIEnv *env = getEnv();
+ HelperCommand *command = jvmtiAllocate(sizeof(*command));
+ if (command == NULL) {
+ EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"HelperCommand");
+ }
+ (void)memset(command, 0, sizeof(*command));
+ command->commandKind = COMMAND_REPORT_INVOKE_DONE;
+ command->sessionID = sessionID;
+ saveGlobalRef(env, thread, &(command->u.reportInvokeDone.thread));
+ enqueueCommand(command, JNI_TRUE, JNI_FALSE);
+}
+
+/*
+ * This, currently, cannot go through the normal event handling code
+ * because the JVMTI event does not contain a thread.
+ */
+void
+eventHelper_reportVMInit(JNIEnv *env, jbyte sessionID, jthread thread, jbyte suspendPolicy)
+{
+ HelperCommand *command = jvmtiAllocate(sizeof(*command));
+ if (command == NULL) {
+ EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"HelperCommmand");
+ }
+ (void)memset(command, 0, sizeof(*command));
+ command->commandKind = COMMAND_REPORT_VM_INIT;
+ command->sessionID = sessionID;
+ saveGlobalRef(env, thread, &(command->u.reportVMInit.thread));
+ command->u.reportVMInit.suspendPolicy = suspendPolicy;
+ enqueueCommand(command, JNI_TRUE, JNI_FALSE);
+}
+
+void
+eventHelper_suspendThread(jbyte sessionID, jthread thread)
+{
+ JNIEnv *env = getEnv();
+ HelperCommand *command = jvmtiAllocate(sizeof(*command));
+ if (command == NULL) {
+ EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"HelperCommmand");
+ }
+ (void)memset(command, 0, sizeof(*command));
+ command->commandKind = COMMAND_SUSPEND_THREAD;
+ command->sessionID = sessionID;
+ saveGlobalRef(env, thread, &(command->u.suspendThread.thread));
+ enqueueCommand(command, JNI_TRUE, JNI_FALSE);
+}