author | rkennke |
Fri, 12 Oct 2018 16:25:24 +0200 | |
changeset 52107 | 0c1e44da019c |
parent 47216 | 71c04702a3d5 |
permissions | -rw-r--r-- |
2 | 1 |
/* |
46841
76a15bd1de0a
8134103: JVMTI_ERROR_WRONG_PHASE(112): on checking for an interface
sspitsyn
parents:
41565
diff
changeset
|
2 |
* Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. |
2 | 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 |
|
5506 | 7 |
* published by the Free Software Foundation. Oracle designates this |
2 | 8 |
* particular file as subject to the "Classpath" exception as provided |
5506 | 9 |
* by Oracle in the LICENSE file that accompanied this code. |
2 | 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 |
* |
|
5506 | 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. |
|
2 | 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; |
|
27739
d185cfc165df
6988950: JDWP exit error JVMTI_ERROR_WRONG_PHASE(112)
sspitsyn
parents:
25859
diff
changeset
|
48 |
static jrawMonitorID vmDeathLock; |
2 | 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 |
void |
|
64 |
debugLoop_initialize(void) |
|
65 |
{ |
|
27739
d185cfc165df
6988950: JDWP exit error JVMTI_ERROR_WRONG_PHASE(112)
sspitsyn
parents:
25859
diff
changeset
|
66 |
vmDeathLock = debugMonitorCreate("JDWP VM_DEATH Lock"); |
2 | 67 |
} |
68 |
||
69 |
void |
|
70 |
debugLoop_sync(void) |
|
71 |
{ |
|
27739
d185cfc165df
6988950: JDWP exit error JVMTI_ERROR_WRONG_PHASE(112)
sspitsyn
parents:
25859
diff
changeset
|
72 |
debugMonitorEnter(vmDeathLock); |
d185cfc165df
6988950: JDWP exit error JVMTI_ERROR_WRONG_PHASE(112)
sspitsyn
parents:
25859
diff
changeset
|
73 |
debugMonitorExit(vmDeathLock); |
2 | 74 |
} |
75 |
||
76 |
/* |
|
77 |
* This is where all the work gets done. |
|
78 |
*/ |
|
79 |
||
80 |
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 |
/* |
|
46841
76a15bd1de0a
8134103: JVMTI_ERROR_WRONG_PHASE(112): on checking for an interface
sspitsyn
parents:
41565
diff
changeset
|
128 |
* For all commands we hold the vmDeathLock |
2 | 129 |
* while executing and replying to the command. This ensures |
46841
76a15bd1de0a
8134103: JVMTI_ERROR_WRONG_PHASE(112): on checking for an interface
sspitsyn
parents:
41565
diff
changeset
|
130 |
* that a command after VM_DEATH will be allowed to complete |
2 | 131 |
* before the thread posting the VM_DEATH continues VM |
132 |
* termination. |
|
133 |
*/ |
|
46841
76a15bd1de0a
8134103: JVMTI_ERROR_WRONG_PHASE(112): on checking for an interface
sspitsyn
parents:
41565
diff
changeset
|
134 |
debugMonitorEnter(vmDeathLock); |
2 | 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 |
/* |
|
27739
d185cfc165df
6988950: JDWP exit error JVMTI_ERROR_WRONG_PHASE(112)
sspitsyn
parents:
25859
diff
changeset
|
171 |
* Release the vmDeathLock as the reply has been posted. |
2 | 172 |
*/ |
46841
76a15bd1de0a
8134103: JVMTI_ERROR_WRONG_PHASE(112): on checking for an interface
sspitsyn
parents:
41565
diff
changeset
|
173 |
debugMonitorExit(vmDeathLock); |
2 | 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 |
|
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(); |
|
41565 | 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(); |
|
2 | 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 |
|
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 |
|
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 |
|
308 |
notifyTransportError(void) { |
|
309 |
debugMonitorEnter(cmdQueueLock); |
|
310 |
transportError = JNI_TRUE; |
|
311 |
debugMonitorNotify(cmdQueueLock); |
|
312 |
debugMonitorExit(cmdQueueLock); |
|
313 |
} |