1 /*
   2  * Copyright (c) 1998, 2016, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 #include "util.h"
  27 #include "transport.h"
  28 #include "debugLoop.h"
  29 #include "debugDispatch.h"
  30 #include "standardHandlers.h"
  31 #include "inStream.h"
  32 #include "outStream.h"
  33 #include "threadControl.h"
  34 
  35 
  36 static void JNICALL reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg);
  37 static void enqueue(jdwpPacket *p);
  38 static jboolean dequeue(jdwpPacket *p);
  39 static void notifyTransportError(void);
  40 
  41 struct PacketList {
  42     jdwpPacket packet;
  43     struct PacketList *next;
  44 };
  45 
  46 static volatile struct PacketList *cmdQueue;
  47 static jrawMonitorID cmdQueueLock;
  48 static jrawMonitorID resumeLock;
  49 static jboolean transportError;
  50 
  51 static jboolean
  52 lastCommand(jdwpCmdPacket *cmd)
  53 {
  54     if ((cmd->cmdSet == JDWP_COMMAND_SET(VirtualMachine)) &&
  55         ((cmd->cmd == JDWP_COMMAND(VirtualMachine, Dispose)) ||
  56          (cmd->cmd == JDWP_COMMAND(VirtualMachine, Exit)))) {
  57         return JNI_TRUE;
  58     } else {
  59         return JNI_FALSE;
  60     }
  61 }
  62 
  63 static jboolean
  64 resumeCommand(jdwpCmdPacket *cmd)
  65 {
  66     if ( (cmd->cmdSet == JDWP_COMMAND_SET(VirtualMachine)) &&
  67          (cmd->cmd == JDWP_COMMAND(VirtualMachine, Resume)) ) {
  68         return JNI_TRUE;
  69     } else {
  70         return JNI_FALSE;
  71     }
  72 }
  73 
  74 void
  75 debugLoop_initialize(void)
  76 {
  77     resumeLock = debugMonitorCreate("JDWP Resume Lock");
  78 }
  79 
  80 void
  81 debugLoop_sync(void)
  82 {
  83     debugMonitorEnter(resumeLock);
  84     debugMonitorExit(resumeLock);
  85 }
  86 
  87 /*
  88  * This is where all the work gets done.
  89  */
  90 
  91 void
  92 debugLoop_run(void)
  93 {
  94     jboolean shouldListen;
  95     jdwpPacket p;
  96     jvmtiStartFunction func;
  97 
  98     /* Initialize all statics */
  99     /* We may be starting a new connection after an error */
 100     cmdQueue = NULL;
 101     cmdQueueLock = debugMonitorCreate("JDWP Command Queue Lock");
 102     transportError = JNI_FALSE;
 103 
 104     shouldListen = JNI_TRUE;
 105 
 106     func = &reader;
 107     (void)spawnNewThread(func, NULL, "JDWP Command Reader");
 108 
 109     standardHandlers_onConnect();
 110     threadControl_onConnect();
 111 
 112     /* Okay, start reading cmds! */
 113     while (shouldListen) {
 114         if (!dequeue(&p)) {
 115             break;
 116         }
 117 
 118         if (p.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
 119             /*
 120              * Its a reply packet.
 121              */
 122            continue;
 123         } else {
 124             /*
 125              * Its a cmd packet.
 126              */
 127             jdwpCmdPacket *cmd = &p.type.cmd;
 128             PacketInputStream in;
 129             PacketOutputStream out;
 130             CommandHandler func;
 131 
 132             /* Should reply be sent to sender.
 133              * For error handling, assume yes, since
 134              * only VM/exit does not reply
 135              */
 136             jboolean replyToSender = JNI_TRUE;
 137 
 138             /*
 139              * For VirtualMachine.Resume commands we hold the resumeLock
 140              * while executing and replying to the command. This ensures
 141              * that a Resume after VM_DEATH will be allowed to complete
 142              * before the thread posting the VM_DEATH continues VM
 143              * termination.
 144              */
 145             if (resumeCommand(cmd)) {
 146                 debugMonitorEnter(resumeLock);
 147             }
 148 
 149             /* Initialize the input and output streams */
 150             inStream_init(&in, p);
 151             outStream_initReply(&out, inStream_id(&in));
 152 
 153             LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd));
 154 
 155             func = debugDispatch_getHandler(cmd->cmdSet,cmd->cmd);
 156             if (func == NULL) {
 157                 /* we've never heard of this, so I guess we
 158                  * haven't implemented it.
 159                  * Handle gracefully for future expansion
 160                  * and platform / vendor expansion.
 161                  */
 162                 outStream_setError(&out, JDWP_ERROR(NOT_IMPLEMENTED));
 163             } else if (gdata->vmDead &&
 164              ((cmd->cmdSet) != JDWP_COMMAND_SET(VirtualMachine))) {
 165                 /* Protect the VM from calls while dead.
 166                  * VirtualMachine cmdSet quietly ignores some cmds
 167                  * after VM death, so, it sends it's own errors.
 168                  */
 169                 outStream_setError(&out, JDWP_ERROR(VM_DEAD));
 170             } else {
 171                 /* Call the command handler */
 172                 replyToSender = func(&in, &out);
 173             }
 174 
 175             /* Reply to the sender */
 176             if (replyToSender) {
 177                 if (inStream_error(&in)) {
 178                     outStream_setError(&out, inStream_error(&in));
 179                 }
 180                 outStream_sendReply(&out);
 181             }
 182 
 183             /*
 184              * Release the resumeLock as the reply has been posted.
 185              */
 186             if (resumeCommand(cmd)) {
 187                 debugMonitorExit(resumeLock);
 188             }
 189 
 190             inStream_destroy(&in);
 191             outStream_destroy(&out);
 192 
 193             shouldListen = !lastCommand(cmd);
 194         }
 195     }
 196     threadControl_onDisconnect();
 197     standardHandlers_onDisconnect();
 198 
 199     /*
 200      * Cut off the transport immediately. This has the effect of
 201      * cutting off any events that the eventHelper thread might
 202      * be trying to send.
 203      */
 204     transport_close();
 205     debugMonitorDestroy(cmdQueueLock);
 206 
 207     /* Reset for a new connection to this VM if it's still alive */
 208     if ( ! gdata->vmDead ) {
 209         debugInit_reset(getEnv());
 210     }
 211 }
 212 
 213 /* Command reader */
 214 static void JNICALL
 215 reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
 216 {
 217     jdwpPacket packet;
 218     jdwpCmdPacket *cmd;
 219     jboolean shouldListen = JNI_TRUE;
 220 
 221     LOG_MISC(("Begin reader thread"));
 222 
 223     while (shouldListen) {
 224         jint rc;
 225 
 226         rc = transport_receivePacket(&packet);
 227 
 228         /* I/O error or EOF */
 229         if (rc != 0 || (rc == 0 && packet.type.cmd.len == 0)) {
 230             shouldListen = JNI_FALSE;
 231             notifyTransportError();
 232         } else if (packet.type.cmd.flags != JDWPTRANSPORT_FLAGS_NONE) {
 233             /*
 234              * Close the connection when we get a jdwpCmdPacket with an
 235              * invalid flags field value. This is a protocol violation
 236              * so we drop the connection. Also this could be a web
 237              * browser generating an HTTP request that passes the JDWP
 238              * handshake. HTTP requests requires that everything be in
 239              * the ASCII printable range so a flags value of
 240              * JDWPTRANSPORT_FLAGS_NONE(0) cannot be generated via HTTP.
 241              */
 242             ERROR_MESSAGE(("Received jdwpPacket with flags != 0x%d (actual=0x%x) when a jdwpCmdPacket was expected.",
 243                            JDWPTRANSPORT_FLAGS_NONE, packet.type.cmd.flags));
 244             shouldListen = JNI_FALSE;
 245             notifyTransportError();
 246         } else {
 247             cmd = &packet.type.cmd;
 248 
 249             LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd));
 250 
 251             /*
 252              * FIXME! We need to deal with high priority
 253              * packets and queue flushes!
 254              */
 255             enqueue(&packet);
 256 
 257             shouldListen = !lastCommand(cmd);
 258         }
 259     }
 260     LOG_MISC(("End reader thread"));
 261 }
 262 
 263 /*
 264  * The current system for queueing packets is highly
 265  * inefficient, and should be rewritten! It'd be nice
 266  * to avoid any additional memory allocations.
 267  */
 268 
 269 static void
 270 enqueue(jdwpPacket *packet)
 271 {
 272     struct PacketList *pL;
 273     struct PacketList *walker;
 274 
 275     pL = jvmtiAllocate((jint)sizeof(struct PacketList));
 276     if (pL == NULL) {
 277         EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"packet list");
 278     }
 279 
 280     pL->packet = *packet;
 281     pL->next = NULL;
 282 
 283     debugMonitorEnter(cmdQueueLock);
 284 
 285     if (cmdQueue == NULL) {
 286         cmdQueue = pL;
 287         debugMonitorNotify(cmdQueueLock);
 288     } else {
 289         walker = (struct PacketList *)cmdQueue;
 290         while (walker->next != NULL)
 291             walker = walker->next;
 292 
 293         walker->next = pL;
 294     }
 295 
 296     debugMonitorExit(cmdQueueLock);
 297 }
 298 
 299 static jboolean
 300 dequeue(jdwpPacket *packet) {
 301     struct PacketList *node = NULL;
 302 
 303     debugMonitorEnter(cmdQueueLock);
 304 
 305     while (!transportError && (cmdQueue == NULL)) {
 306         debugMonitorWait(cmdQueueLock);
 307     }
 308 
 309     if (cmdQueue != NULL) {
 310         node = (struct PacketList *)cmdQueue;
 311         cmdQueue = node->next;
 312     }
 313     debugMonitorExit(cmdQueueLock);
 314 
 315     if (node != NULL) {
 316         *packet = node->packet;
 317         jvmtiDeallocate(node);
 318     }
 319     return (node != NULL);
 320 }
 321 
 322 static void
 323 notifyTransportError(void) {
 324     debugMonitorEnter(cmdQueueLock);
 325     transportError = JNI_TRUE;
 326     debugMonitorNotify(cmdQueueLock);
 327     debugMonitorExit(cmdQueueLock);
 328 }