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
lastCommand(jdwpCmdPacket * cmd)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
debugLoop_initialize(void)64 debugLoop_initialize(void)
65 {
66 vmDeathLock = debugMonitorCreate("JDWP VM_DEATH Lock");
67 }
68
69 void
debugLoop_sync(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
debugLoop_run(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
reader(jvmtiEnv * jvmti_env,JNIEnv * jni_env,void * arg)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
enqueue(jdwpPacket * packet)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
dequeue(jdwpPacket * packet)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
notifyTransportError(void)308 notifyTransportError(void) {
309 debugMonitorEnter(cmdQueueLock);
310 transportError = JNI_TRUE;
311 debugMonitorNotify(cmdQueueLock);
312 debugMonitorExit(cmdQueueLock);
313 }
314