• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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
lastCommand(jdwpCmdPacket * cmd)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
debugLoop_initialize(void)70 debugLoop_initialize(void)
71 {
72     vmDeathLock = debugMonitorCreate("JDWP VM_DEATH Lock");
73 }
74 
75 void
debugLoop_sync(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
debugLoop_run(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
reader(jvmtiEnv * jvmti_env,JNIEnv * jni_env,void * arg)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
enqueue(jdwpPacket * packet)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
dequeue(jdwpPacket * packet)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
notifyTransportError(void)331 notifyTransportError(void) {
332     debugMonitorEnter(cmdQueueLock);
333     transportError = JNI_TRUE;
334     debugMonitorNotify(cmdQueueLock);
335     debugMonitorExit(cmdQueueLock);
336 }
337