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