Home | History | Annotate | Download | only in back
      1 /*
      2  * Copyright (c) 1998, 2017, 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 // ANDROID-CHANGED: Needed for DDM_onDisconnect
     36 #include "DDMImpl.h"
     37 // ANDROID-CHANGED: Needed for vmDebug_onDisconnect, vmDebug_notifyDebuggerActivityStart &
     38 // vmDebug_notifyDebuggerActivityEnd.
     39 #include "vmDebug.h"
     40 
     41 
     42 static void JNICALL reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg);
     43 static void enqueue(jdwpPacket *p);
     44 static jboolean dequeue(jdwpPacket *p);
     45 static void notifyTransportError(void);
     46 
     47 struct PacketList {
     48     jdwpPacket packet;
     49     struct PacketList *next;
     50 };
     51 
     52 static volatile struct PacketList *cmdQueue;
     53 static jrawMonitorID cmdQueueLock;
     54 static jrawMonitorID vmDeathLock;
     55 static jboolean transportError;
     56 
     57 static jboolean
     58 lastCommand(jdwpCmdPacket *cmd)
     59 {
     60     if ((cmd->cmdSet == JDWP_COMMAND_SET(VirtualMachine)) &&
     61         ((cmd->cmd == JDWP_COMMAND(VirtualMachine, Dispose)) ||
     62          (cmd->cmd == JDWP_COMMAND(VirtualMachine, Exit)))) {
     63         return JNI_TRUE;
     64     } else {
     65         return JNI_FALSE;
     66     }
     67 }
     68 
     69 void
     70 debugLoop_initialize(void)
     71 {
     72     vmDeathLock = debugMonitorCreate("JDWP VM_DEATH Lock");
     73 }
     74 
     75 void
     76 debugLoop_sync(void)
     77 {
     78     debugMonitorEnter(vmDeathLock);
     79     debugMonitorExit(vmDeathLock);
     80 }
     81 
     82 /*
     83  * This is where all the work gets done.
     84  */
     85 
     86 void
     87 debugLoop_run(void)
     88 {
     89     jboolean shouldListen;
     90     jdwpPacket p;
     91     jvmtiStartFunction func;
     92 
     93     /* Initialize all statics */
     94     /* We may be starting a new connection after an error */
     95     cmdQueue = NULL;
     96     cmdQueueLock = debugMonitorCreate("JDWP Command Queue Lock");
     97     transportError = JNI_FALSE;
     98 
     99     shouldListen = JNI_TRUE;
    100 
    101     func = &reader;
    102     (void)spawnNewThread(func, NULL, "JDWP Command Reader");
    103 
    104     standardHandlers_onConnect();
    105     threadControl_onConnect();
    106 
    107     /* Okay, start reading cmds! */
    108     while (shouldListen) {
    109         if (!dequeue(&p)) {
    110             break;
    111         }
    112 
    113         if (p.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) {
    114             /*
    115              * Its a reply packet.
    116              */
    117            continue;
    118         } else {
    119             /*
    120              * Its a cmd packet.
    121              */
    122             jdwpCmdPacket *cmd = &p.type.cmd;
    123             PacketInputStream in;
    124             PacketOutputStream out;
    125             CommandHandler func;
    126 
    127             /* Should reply be sent to sender.
    128              * For error handling, assume yes, since
    129              * only VM/exit does not reply
    130              */
    131             jboolean replyToSender = JNI_TRUE;
    132 
    133             /*
    134              * For all commands we hold the vmDeathLock
    135              * while executing and replying to the command. This ensures
    136              * that a command after VM_DEATH will be allowed to complete
    137              * before the thread posting the VM_DEATH continues VM
    138              * termination.
    139              */
    140             debugMonitorEnter(vmDeathLock);
    141 
    142             // ANDROID-CHANGED: Tell vmDebug we have started doing some debugger activity. We only
    143             // do this if the cmdSet is not DDMS for historical reasons.
    144             jboolean is_ddms = (cmd->cmdSet == JDWP_COMMAND_SET(DDM));
    145             if (!is_ddms) {
    146                 vmDebug_notifyDebuggerActivityStart();
    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             // ANDROID-CHANGED: Tell vmDebug we are done with the current debugger activity.
    176             if (!is_ddms) {
    177                 vmDebug_notifyDebuggerActivityEnd();
    178             }
    179 
    180             /* Reply to the sender */
    181             if (replyToSender) {
    182                 if (inStream_error(&in)) {
    183                     outStream_setError(&out, inStream_error(&in));
    184                 }
    185                 outStream_sendReply(&out);
    186             }
    187 
    188             /*
    189              * Release the vmDeathLock as the reply has been posted.
    190              */
    191             debugMonitorExit(vmDeathLock);
    192 
    193             inStream_destroy(&in);
    194             outStream_destroy(&out);
    195 
    196             shouldListen = !lastCommand(cmd);
    197         }
    198     }
    199     threadControl_onDisconnect();
    200     standardHandlers_onDisconnect();
    201 
    202     /*
    203      * Cut off the transport immediately. This has the effect of
    204      * cutting off any events that the eventHelper thread might
    205      * be trying to send.
    206      */
    207     transport_close();
    208     debugMonitorDestroy(cmdQueueLock);
    209 
    210     // ANDROID-CHANGED: Tell vmDebug we have disconnected.
    211     vmDebug_onDisconnect();
    212     // ANDROID-CHANGED: DDM needs to call some functions when we disconnect.
    213     DDM_onDisconnect();
    214 
    215     /* Reset for a new connection to this VM if it's still alive */
    216     if ( ! gdata->vmDead ) {
    217         debugInit_reset(getEnv());
    218     }
    219 }
    220 
    221 /* Command reader */
    222 static void JNICALL
    223 reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg)
    224 {
    225     jdwpPacket packet;
    226     jdwpCmdPacket *cmd;
    227     jboolean shouldListen = JNI_TRUE;
    228 
    229     LOG_MISC(("Begin reader thread"));
    230 
    231     while (shouldListen) {
    232         jint rc;
    233 
    234         rc = transport_receivePacket(&packet);
    235 
    236         /* I/O error or EOF */
    237         if (rc != 0 || (rc == 0 && packet.type.cmd.len == 0)) {
    238             shouldListen = JNI_FALSE;
    239             notifyTransportError();
    240         } else if (packet.type.cmd.flags != JDWPTRANSPORT_FLAGS_NONE) {
    241             /*
    242              * Close the connection when we get a jdwpCmdPacket with an
    243              * invalid flags field value. This is a protocol violation
    244              * so we drop the connection. Also this could be a web
    245              * browser generating an HTTP request that passes the JDWP
    246              * handshake. HTTP requests requires that everything be in
    247              * the ASCII printable range so a flags value of
    248              * JDWPTRANSPORT_FLAGS_NONE(0) cannot be generated via HTTP.
    249              */
    250             ERROR_MESSAGE(("Received jdwpPacket with flags != 0x%d (actual=0x%x) when a jdwpCmdPacket was expected.",
    251                            JDWPTRANSPORT_FLAGS_NONE, packet.type.cmd.flags));
    252             shouldListen = JNI_FALSE;
    253             notifyTransportError();
    254         } else {
    255             cmd = &packet.type.cmd;
    256 
    257             LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd));
    258 
    259             /*
    260              * FIXME! We need to deal with high priority
    261              * packets and queue flushes!
    262              */
    263             enqueue(&packet);
    264 
    265             shouldListen = !lastCommand(cmd);
    266         }
    267     }
    268     LOG_MISC(("End reader thread"));
    269 }
    270 
    271 /*
    272  * The current system for queueing packets is highly
    273  * inefficient, and should be rewritten! It'd be nice
    274  * to avoid any additional memory allocations.
    275  */
    276 
    277 static void
    278 enqueue(jdwpPacket *packet)
    279 {
    280     struct PacketList *pL;
    281     struct PacketList *walker;
    282 
    283     pL = jvmtiAllocate((jint)sizeof(struct PacketList));
    284     if (pL == NULL) {
    285         EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"packet list");
    286     }
    287 
    288     pL->packet = *packet;
    289     pL->next = NULL;
    290 
    291     debugMonitorEnter(cmdQueueLock);
    292 
    293     if (cmdQueue == NULL) {
    294         cmdQueue = pL;
    295         debugMonitorNotify(cmdQueueLock);
    296     } else {
    297         walker = (struct PacketList *)cmdQueue;
    298         while (walker->next != NULL)
    299             walker = walker->next;
    300 
    301         walker->next = pL;
    302     }
    303 
    304     debugMonitorExit(cmdQueueLock);
    305 }
    306 
    307 static jboolean
    308 dequeue(jdwpPacket *packet) {
    309     struct PacketList *node = NULL;
    310 
    311     debugMonitorEnter(cmdQueueLock);
    312 
    313     while (!transportError && (cmdQueue == NULL)) {
    314         debugMonitorWait(cmdQueueLock);
    315     }
    316 
    317     if (cmdQueue != NULL) {
    318         node = (struct PacketList *)cmdQueue;
    319         cmdQueue = node->next;
    320     }
    321     debugMonitorExit(cmdQueueLock);
    322 
    323     if (node != NULL) {
    324         *packet = node->packet;
    325         jvmtiDeallocate(node);
    326     }
    327     return (node != NULL);
    328 }
    329 
    330 static void
    331 notifyTransportError(void) {
    332     debugMonitorEnter(cmdQueueLock);
    333     transportError = JNI_TRUE;
    334     debugMonitorNotify(cmdQueueLock);
    335     debugMonitorExit(cmdQueueLock);
    336 }
    337