0N/A/*
2362N/A * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0N/A *
0N/A * This code is free software; you can redistribute it and/or modify it
0N/A * under the terms of the GNU General Public License version 2 only, as
2362N/A * published by the Free Software Foundation. Oracle designates this
0N/A * particular file as subject to the "Classpath" exception as provided
2362N/A * by Oracle in the LICENSE file that accompanied this code.
0N/A *
0N/A * This code is distributed in the hope that it will be useful, but WITHOUT
0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0N/A * version 2 for more details (a copy is included in the LICENSE file that
0N/A * accompanied this code).
0N/A *
0N/A * You should have received a copy of the GNU General Public License version
0N/A * 2 along with this work; if not, write to the Free Software Foundation,
0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0N/A *
2362N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
2362N/A * or visit www.oracle.com if you need additional information or have any
2362N/A * questions.
0N/A */
0N/A
0N/A#include "util.h"
0N/A#include "transport.h"
0N/A#include "debugLoop.h"
0N/A#include "debugDispatch.h"
0N/A#include "standardHandlers.h"
0N/A#include "inStream.h"
0N/A#include "outStream.h"
0N/A#include "threadControl.h"
0N/A
0N/A
0N/Astatic void JNICALL reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg);
359N/Astatic void enqueue(jdwpPacket *p);
0N/Astatic jboolean dequeue(jdwpPacket *p);
0N/Astatic void notifyTransportError(void);
0N/A
0N/Astruct PacketList {
0N/A jdwpPacket packet;
0N/A struct PacketList *next;
359N/A};
0N/A
0N/Astatic volatile struct PacketList *cmdQueue;
0N/Astatic jrawMonitorID cmdQueueLock;
0N/Astatic jrawMonitorID resumeLock;
0N/Astatic jboolean transportError;
0N/A
0N/Astatic jboolean
0N/AlastCommand(jdwpCmdPacket *cmd)
0N/A{
0N/A if ((cmd->cmdSet == JDWP_COMMAND_SET(VirtualMachine)) &&
0N/A ((cmd->cmd == JDWP_COMMAND(VirtualMachine, Dispose)) ||
0N/A (cmd->cmd == JDWP_COMMAND(VirtualMachine, Exit)))) {
0N/A return JNI_TRUE;
0N/A } else {
0N/A return JNI_FALSE;
0N/A }
0N/A}
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);
}