|
1 /* |
|
2 * Copyright 1998-2005 Sun Microsystems, Inc. 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. Sun designates this |
|
8 * particular file as subject to the "Classpath" exception as provided |
|
9 * by Sun 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 Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, |
|
22 * CA 95054 USA or visit www.sun.com if you need additional information or |
|
23 * have any 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 { |
|
233 cmd = &packet.type.cmd; |
|
234 |
|
235 LOG_MISC(("Command set %d, command %d", cmd->cmdSet, cmd->cmd)); |
|
236 |
|
237 /* |
|
238 * FIXME! We need to deal with high priority |
|
239 * packets and queue flushes! |
|
240 */ |
|
241 enqueue(&packet); |
|
242 |
|
243 shouldListen = !lastCommand(cmd); |
|
244 } |
|
245 } |
|
246 LOG_MISC(("End reader thread")); |
|
247 } |
|
248 |
|
249 /* |
|
250 * The current system for queueing packets is highly |
|
251 * inefficient, and should be rewritten! It'd be nice |
|
252 * to avoid any additional memory allocations. |
|
253 */ |
|
254 |
|
255 static void |
|
256 enqueue(jdwpPacket *packet) |
|
257 { |
|
258 struct PacketList *pL; |
|
259 struct PacketList *walker; |
|
260 |
|
261 pL = jvmtiAllocate((jint)sizeof(struct PacketList)); |
|
262 if (pL == NULL) { |
|
263 EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"packet list"); |
|
264 } |
|
265 |
|
266 pL->packet = *packet; |
|
267 pL->next = NULL; |
|
268 |
|
269 debugMonitorEnter(cmdQueueLock); |
|
270 |
|
271 if (cmdQueue == NULL) { |
|
272 cmdQueue = pL; |
|
273 debugMonitorNotify(cmdQueueLock); |
|
274 } else { |
|
275 walker = (struct PacketList *)cmdQueue; |
|
276 while (walker->next != NULL) |
|
277 walker = walker->next; |
|
278 |
|
279 walker->next = pL; |
|
280 } |
|
281 |
|
282 debugMonitorExit(cmdQueueLock); |
|
283 } |
|
284 |
|
285 static jboolean |
|
286 dequeue(jdwpPacket *packet) { |
|
287 struct PacketList *node = NULL; |
|
288 |
|
289 debugMonitorEnter(cmdQueueLock); |
|
290 |
|
291 while (!transportError && (cmdQueue == NULL)) { |
|
292 debugMonitorWait(cmdQueueLock); |
|
293 } |
|
294 |
|
295 if (cmdQueue != NULL) { |
|
296 node = (struct PacketList *)cmdQueue; |
|
297 cmdQueue = node->next; |
|
298 } |
|
299 debugMonitorExit(cmdQueueLock); |
|
300 |
|
301 if (node != NULL) { |
|
302 *packet = node->packet; |
|
303 jvmtiDeallocate(node); |
|
304 } |
|
305 return (node != NULL); |
|
306 } |
|
307 |
|
308 static void |
|
309 notifyTransportError(void) { |
|
310 debugMonitorEnter(cmdQueueLock); |
|
311 transportError = JNI_TRUE; |
|
312 debugMonitorNotify(cmdQueueLock); |
|
313 debugMonitorExit(cmdQueueLock); |
|
314 } |