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 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 vmDeathLock; 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 void 64 debugLoop_initialize(void) 65 { 66 vmDeathLock = debugMonitorCreate("JDWP VM_DEATH Lock"); 67 } 68 69 void 70 debugLoop_sync(void) 71 { 72 debugMonitorEnter(vmDeathLock); 73 debugMonitorExit(vmDeathLock); 74 } 75 76 /* 77 * This is where all the work gets done. 78 */ 79 80 void 81 debugLoop_run(void) 82 { 83 jboolean shouldListen; 84 jdwpPacket p; 85 jvmtiStartFunction func; 86 87 /* Initialize all statics */ 88 /* We may be starting a new connection after an error */ 89 cmdQueue = NULL; 90 cmdQueueLock = debugMonitorCreate("JDWP Command Queue Lock"); 91 transportError = JNI_FALSE; 92 93 shouldListen = JNI_TRUE; 94 95 func = &reader; 96 (void)spawnNewThread(func, NULL, "JDWP Command Reader"); 97 98 standardHandlers_onConnect(); 99 threadControl_onConnect(); 100 101 /* Okay, start reading cmds! */ 102 while (shouldListen) { 103 if (!dequeue(&p)) { 104 break; 105 } 106 107 if (p.type.cmd.flags & JDWPTRANSPORT_FLAGS_REPLY) { 108 /* 109 * Its a reply packet. 110 */ 111 continue; 112 } else { 113 /* 114 * Its a cmd packet. 115 */ 116 jdwpCmdPacket *cmd = &p.type.cmd; 117 PacketInputStream in; 118 PacketOutputStream out; 119 CommandHandler func; 120 121 /* Should reply be sent to sender. 122 * For error handling, assume yes, since 123 * only VM/exit does not reply 124 */ 125 jboolean replyToSender = JNI_TRUE; 126 127 /* 128 * For all commands we hold the vmDeathLock 129 * while executing and replying to the command. This ensures 130 * that a command after VM_DEATH will be allowed to complete 131 * before the thread posting the VM_DEATH continues VM 132 * termination. 133 */ 134 debugMonitorEnter(vmDeathLock); 135 136 /* Initialize the input and output streams */ 137 inStream_init(&in, p); 138 outStream_initReply(&out, inStream_id(&in)); 139 140 LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd)); 141 142 func = debugDispatch_getHandler(cmd->cmdSet,cmd->cmd); 143 if (func == NULL) { 144 /* we've never heard of this, so I guess we 145 * haven't implemented it. 146 * Handle gracefully for future expansion 147 * and platform / vendor expansion. 148 */ 149 outStream_setError(&out, JDWP_ERROR(NOT_IMPLEMENTED)); 150 } else if (gdata->vmDead && 151 ((cmd->cmdSet) != JDWP_COMMAND_SET(VirtualMachine))) { 152 /* Protect the VM from calls while dead. 153 * VirtualMachine cmdSet quietly ignores some cmds 154 * after VM death, so, it sends it's own errors. 155 */ 156 outStream_setError(&out, JDWP_ERROR(VM_DEAD)); 157 } else { 158 /* Call the command handler */ 159 replyToSender = func(&in, &out); 160 } 161 162 /* Reply to the sender */ 163 if (replyToSender) { 164 if (inStream_error(&in)) { 165 outStream_setError(&out, inStream_error(&in)); 166 } 167 outStream_sendReply(&out); 168 } 169 170 /* 171 * Release the vmDeathLock as the reply has been posted. 172 */ 173 debugMonitorExit(vmDeathLock); 174 175 inStream_destroy(&in); 176 outStream_destroy(&out); 177 178 shouldListen = !lastCommand(cmd); 179 } 180 } 181 threadControl_onDisconnect(); 182 standardHandlers_onDisconnect(); 183 184 /* 185 * Cut off the transport immediately. This has the effect of 186 * cutting off any events that the eventHelper thread might 187 * be trying to send. 188 */ 189 transport_close(); 190 debugMonitorDestroy(cmdQueueLock); 191 192 /* Reset for a new connection to this VM if it's still alive */ 193 if ( ! gdata->vmDead ) { 194 debugInit_reset(getEnv()); 195 } 196 } 197 198 /* Command reader */ 199 static void JNICALL 200 reader(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg) 201 { 202 jdwpPacket packet; 203 jdwpCmdPacket *cmd; 204 jboolean shouldListen = JNI_TRUE; 205 206 LOG_MISC(("Begin reader thread")); 207 208 while (shouldListen) { 209 jint rc; 210 211 rc = transport_receivePacket(&packet); 212 213 /* I/O error or EOF */ 214 if (rc != 0 || (rc == 0 && packet.type.cmd.len == 0)) { 215 shouldListen = JNI_FALSE; 216 notifyTransportError(); 217 } else if (packet.type.cmd.flags != JDWPTRANSPORT_FLAGS_NONE) { 218 /* 219 * Close the connection when we get a jdwpCmdPacket with an 220 * invalid flags field value. This is a protocol violation 221 * so we drop the connection. Also this could be a web 222 * browser generating an HTTP request that passes the JDWP 223 * handshake. HTTP requests requires that everything be in 224 * the ASCII printable range so a flags value of 225 * JDWPTRANSPORT_FLAGS_NONE(0) cannot be generated via HTTP. 226 */ 227 ERROR_MESSAGE(("Received jdwpPacket with flags != 0x%d (actual=0x%x) when a jdwpCmdPacket was expected.", 228 JDWPTRANSPORT_FLAGS_NONE, packet.type.cmd.flags)); 229 shouldListen = JNI_FALSE; 230 notifyTransportError(); 231 } else { 232 cmd = &packet.type.cmd; 233 234 LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd)); 235 236 /* 237 * FIXME! We need to deal with high priority 238 * packets and queue flushes! 239 */ 240 enqueue(&packet); 241 242 shouldListen = !lastCommand(cmd); 243 } 244 } 245 LOG_MISC(("End reader thread")); 246 } 247 248 /* 249 * The current system for queueing packets is highly 250 * inefficient, and should be rewritten! It'd be nice 251 * to avoid any additional memory allocations. 252 */ 253 254 static void 255 enqueue(jdwpPacket *packet) 256 { 257 struct PacketList *pL; 258 struct PacketList *walker; 259 260 pL = jvmtiAllocate((jint)sizeof(struct PacketList)); 261 if (pL == NULL) { 262 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"packet list"); 263 } 264 265 pL->packet = *packet; 266 pL->next = NULL; 267 268 debugMonitorEnter(cmdQueueLock); 269 270 if (cmdQueue == NULL) { 271 cmdQueue = pL; 272 debugMonitorNotify(cmdQueueLock); 273 } else { 274 walker = (struct PacketList *)cmdQueue; 275 while (walker->next != NULL) 276 walker = walker->next; 277 278 walker->next = pL; 279 } 280 281 debugMonitorExit(cmdQueueLock); 282 } 283 284 static jboolean 285 dequeue(jdwpPacket *packet) { 286 struct PacketList *node = NULL; 287 288 debugMonitorEnter(cmdQueueLock); 289 290 while (!transportError && (cmdQueue == NULL)) { 291 debugMonitorWait(cmdQueueLock); 292 } 293 294 if (cmdQueue != NULL) { 295 node = (struct PacketList *)cmdQueue; 296 cmdQueue = node->next; 297 } 298 debugMonitorExit(cmdQueueLock); 299 300 if (node != NULL) { 301 *packet = node->packet; 302 jvmtiDeallocate(node); 303 } 304 return (node != NULL); 305 } 306 307 static void 308 notifyTransportError(void) { 309 debugMonitorEnter(cmdQueueLock); 310 transportError = JNI_TRUE; 311 debugMonitorNotify(cmdQueueLock); 312 debugMonitorExit(cmdQueueLock); 313 }