jdk/src/share/back/debugLoop.c
changeset 2 90ce3da70b43
child 5506 202f599c92aa
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/jdk/src/share/back/debugLoop.c	Sat Dec 01 00:00:00 2007 +0000
@@ -0,0 +1,314 @@
+/*
+ * Copyright 1998-2005 Sun Microsystems, Inc.  All Rights Reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation.  Sun designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Sun in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
+ * CA 95054 USA or visit www.sun.com if you need additional information or
+ * have any questions.
+ */
+
+#include "util.h"
+#include "transport.h"
+#include "debugLoop.h"
+#include "debugDispatch.h"
+#include "standardHandlers.h"
+#include "inStream.h"
+#include "outStream.h"
+#include "threadControl.h"
+
+
+static void JNICALL reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg);
+static void enqueue(jdwpPacket *p);
+static jboolean dequeue(jdwpPacket *p);
+static void notifyTransportError(void);
+
+struct PacketList {
+    jdwpPacket packet;
+    struct PacketList *next;
+};
+
+static volatile struct PacketList *cmdQueue;
+static jrawMonitorID cmdQueueLock;
+static jrawMonitorID resumeLock;
+static jboolean transportError;
+
+static jboolean
+lastCommand(jdwpCmdPacket *cmd)
+{
+    if ((cmd->cmdSet == JDWP_COMMAND_SET(VirtualMachine)) &&
+        ((cmd->cmd == JDWP_COMMAND(VirtualMachine, Dispose)) ||
+         (cmd->cmd == JDWP_COMMAND(VirtualMachine, Exit)))) {
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+static jboolean
+resumeCommand(jdwpCmdPacket *cmd)
+{
+    if ( (cmd->cmdSet == JDWP_COMMAND_SET(VirtualMachine)) &&
+         (cmd->cmd == JDWP_COMMAND(VirtualMachine, Resume)) ) {
+        return JNI_TRUE;
+    } else {
+        return JNI_FALSE;
+    }
+}
+
+void
+debugLoop_initialize(void)
+{
+    resumeLock = debugMonitorCreate("JDWP Resume Lock");
+}
+
+void
+debugLoop_sync(void)
+{
+    debugMonitorEnter(resumeLock);
+    debugMonitorExit(resumeLock);
+}
+
+/*
+ * This is where all the work gets done.
+ */
+
+void
+debugLoop_run(void)
+{
+    jboolean shouldListen;
+    jdwpPacket p;
+    jvmtiStartFunction func;
+
+    /* Initialize all statics */
+    /* We may be starting a new connection after an error */
+    cmdQueue = NULL;
+    cmdQueueLock = debugMonitorCreate("JDWP Command Queue Lock");
+    transportError = JNI_FALSE;
+
+    shouldListen = JNI_TRUE;
+
+    func = &reader;
+    (void)spawnNewThread(func, NULL, "JDWP Command Reader");
+
+    standardHandlers_onConnect();
+    threadControl_onConnect();
+
+    /* Okay, start reading cmds! */
+    while (shouldListen) {
+        if (!dequeue(&p)) {
+            break;
+        }
+
+        if (p.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
+            /*
+             * Its a reply packet.
+             */
+           continue;
+        } else {
+            /*
+             * Its a cmd packet.
+             */
+            jdwpCmdPacket *cmd = &p.type.cmd;
+            PacketInputStream in;
+            PacketOutputStream out;
+            CommandHandler func;
+
+            /* Should reply be sent to sender.
+             * For error handling, assume yes, since
+             * only VM/exit does not reply
+             */
+            jboolean replyToSender = JNI_TRUE;
+
+            /*
+             * For VirtualMachine.Resume commands we hold the resumeLock
+             * while executing and replying to the command. This ensures
+             * that a Resume after VM_DEATH will be allowed to complete
+             * before the thread posting the VM_DEATH continues VM
+             * termination.
+             */
+            if (resumeCommand(cmd)) {
+                debugMonitorEnter(resumeLock);
+            }
+
+            /* Initialize the input and output streams */
+            inStream_init(&in, p);
+            outStream_initReply(&out, inStream_id(&in));
+
+            LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd));
+
+            func = debugDispatch_getHandler(cmd->cmdSet,cmd->cmd);
+            if (func == NULL) {
+                /* we've never heard of this, so I guess we
+                 * haven't implemented it.
+                 * Handle gracefully for future expansion
+                 * and platform / vendor expansion.
+                 */
+                outStream_setError(&out, JDWP_ERROR(NOT_IMPLEMENTED));
+            } else if (gdata->vmDead &&
+             ((cmd->cmdSet) != JDWP_COMMAND_SET(VirtualMachine))) {
+                /* Protect the VM from calls while dead.
+                 * VirtualMachine cmdSet quietly ignores some cmds
+                 * after VM death, so, it sends it's own errors.
+                 */
+                outStream_setError(&out, JDWP_ERROR(VM_DEAD));
+            } else {
+                /* Call the command handler */
+                replyToSender = func(&in, &out);
+            }
+
+            /* Reply to the sender */
+            if (replyToSender) {
+                if (inStream_error(&in)) {
+                    outStream_setError(&out, inStream_error(&in));
+                }
+                outStream_sendReply(&out);
+            }
+
+            /*
+             * Release the resumeLock as the reply has been posted.
+             */
+            if (resumeCommand(cmd)) {
+                debugMonitorExit(resumeLock);
+            }
+
+            inStream_destroy(&in);
+            outStream_destroy(&out);
+
+            shouldListen = !lastCommand(cmd);
+        }
+    }
+    threadControl_onDisconnect();
+    standardHandlers_onDisconnect();
+
+    /*
+     * Cut off the transport immediately. This has the effect of
+     * cutting off any events that the eventHelper thread might
+     * be trying to send.
+     */
+    transport_close();
+    debugMonitorDestroy(cmdQueueLock);
+
+    /* Reset for a new connection to this VM if it's still alive */
+    if ( ! gdata->vmDead ) {
+        debugInit_reset(getEnv());
+    }
+}
+
+/* Command reader */
+static void JNICALL
+reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
+{
+    jdwpPacket packet;
+    jdwpCmdPacket *cmd;
+    jboolean shouldListen = JNI_TRUE;
+
+    LOG_MISC(("Begin reader thread"));
+
+    while (shouldListen) {
+        jint rc;
+
+        rc = transport_receivePacket(&packet);
+
+        /* I/O error or EOF */
+        if (rc != 0 || (rc == 0 && packet.type.cmd.len == 0)) {
+            shouldListen = JNI_FALSE;
+            notifyTransportError();
+        } else {
+            cmd = &packet.type.cmd;
+
+            LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd));
+
+            /*
+             * FIXME! We need to deal with high priority
+             * packets and queue flushes!
+             */
+            enqueue(&packet);
+
+            shouldListen = !lastCommand(cmd);
+        }
+    }
+    LOG_MISC(("End reader thread"));
+}
+
+/*
+ * The current system for queueing packets is highly
+ * inefficient, and should be rewritten! It'd be nice
+ * to avoid any additional memory allocations.
+ */
+
+static void
+enqueue(jdwpPacket *packet)
+{
+    struct PacketList *pL;
+    struct PacketList *walker;
+
+    pL = jvmtiAllocate((jint)sizeof(struct PacketList));
+    if (pL == NULL) {
+        EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"packet list");
+    }
+
+    pL->packet = *packet;
+    pL->next = NULL;
+
+    debugMonitorEnter(cmdQueueLock);
+
+    if (cmdQueue == NULL) {
+        cmdQueue = pL;
+        debugMonitorNotify(cmdQueueLock);
+    } else {
+        walker = (struct PacketList *)cmdQueue;
+        while (walker->next != NULL)
+            walker = walker->next;
+
+        walker->next = pL;
+    }
+
+    debugMonitorExit(cmdQueueLock);
+}
+
+static jboolean
+dequeue(jdwpPacket *packet) {
+    struct PacketList *node = NULL;
+
+    debugMonitorEnter(cmdQueueLock);
+
+    while (!transportError && (cmdQueue == NULL)) {
+        debugMonitorWait(cmdQueueLock);
+    }
+
+    if (cmdQueue != NULL) {
+        node = (struct PacketList *)cmdQueue;
+        cmdQueue = node->next;
+    }
+    debugMonitorExit(cmdQueueLock);
+
+    if (node != NULL) {
+        *packet = node->packet;
+        jvmtiDeallocate(node);
+    }
+    return (node != NULL);
+}
+
+static void
+notifyTransportError(void) {
+    debugMonitorEnter(cmdQueueLock);
+    transportError = JNI_TRUE;
+    debugMonitorNotify(cmdQueueLock);
+    debugMonitorExit(cmdQueueLock);
+}