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