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 }