File: | jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c |
Warning: | line 1153, column 11 Null pointer passed to 1st parameter expecting 'nonnull' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* | |||
2 | * Copyright (c) 1998, 2021, 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 "outStream.h" | |||
28 | #include "eventHandler.h" | |||
29 | #include "threadControl.h" | |||
30 | #include "invoker.h" | |||
31 | #include "signature.h" | |||
32 | ||||
33 | ||||
34 | #define COMMAND_LOOP_THREAD_NAME"JDWP Event Helper Thread" "JDWP Event Helper Thread" | |||
35 | ||||
36 | /* | |||
37 | * Event helper thread command commandKinds | |||
38 | */ | |||
39 | #define COMMAND_REPORT_EVENT_COMPOSITE1 1 | |||
40 | #define COMMAND_REPORT_INVOKE_DONE2 2 | |||
41 | #define COMMAND_REPORT_VM_INIT3 3 | |||
42 | #define COMMAND_SUSPEND_THREAD4 4 | |||
43 | ||||
44 | /* | |||
45 | * Event helper thread command singleKinds | |||
46 | */ | |||
47 | #define COMMAND_SINGLE_EVENT11 11 | |||
48 | #define COMMAND_SINGLE_UNLOAD12 12 | |||
49 | #define COMMAND_SINGLE_FRAME_EVENT13 13 | |||
50 | ||||
51 | typedef struct EventCommandSingle { | |||
52 | jbyte suspendPolicy; /* NOTE: Must be the first field */ | |||
53 | jint id; | |||
54 | EventInfo info; | |||
55 | } EventCommandSingle; | |||
56 | ||||
57 | typedef struct UnloadCommandSingle { | |||
58 | char *classSignature; | |||
59 | jint id; | |||
60 | } UnloadCommandSingle; | |||
61 | ||||
62 | typedef struct FrameEventCommandSingle { | |||
63 | jbyte suspendPolicy; /* NOTE: Must be the first field */ | |||
64 | jint id; | |||
65 | EventIndex ei; | |||
66 | jthread thread; | |||
67 | jclass clazz; | |||
68 | jmethodID method; | |||
69 | jlocation location; | |||
70 | char typeKey; /* Not used for method entry events */ | |||
71 | /* If typeKey is 0, then no return value is needed */ | |||
72 | jvalue returnValue; /* Not used for method entry events */ | |||
73 | } FrameEventCommandSingle; | |||
74 | ||||
75 | typedef struct CommandSingle { | |||
76 | jint singleKind; | |||
77 | union { | |||
78 | EventCommandSingle eventCommand; | |||
79 | UnloadCommandSingle unloadCommand; | |||
80 | FrameEventCommandSingle frameEventCommand; | |||
81 | } u; | |||
82 | } CommandSingle; | |||
83 | ||||
84 | typedef struct ReportInvokeDoneCommand { | |||
85 | jthread thread; | |||
86 | } ReportInvokeDoneCommand; | |||
87 | ||||
88 | typedef struct ReportVMInitCommand { | |||
89 | jbyte suspendPolicy; /* NOTE: Must be the first field */ | |||
90 | jthread thread; | |||
91 | } ReportVMInitCommand; | |||
92 | ||||
93 | typedef struct SuspendThreadCommand { | |||
94 | jthread thread; | |||
95 | } SuspendThreadCommand; | |||
96 | ||||
97 | typedef struct ReportEventCompositeCommand { | |||
98 | jbyte suspendPolicy; /* NOTE: Must be the first field */ | |||
99 | jint eventCount; | |||
100 | CommandSingle singleCommand[1]; /* variable length */ | |||
101 | } ReportEventCompositeCommand; | |||
102 | ||||
103 | typedef struct HelperCommand { | |||
104 | jint commandKind; | |||
105 | jboolean done; | |||
106 | jboolean waiting; | |||
107 | jbyte sessionID; | |||
108 | struct HelperCommand *next; | |||
109 | union { | |||
110 | /* NOTE: Each of the structs below must have the same first field */ | |||
111 | ReportEventCompositeCommand reportEventComposite; | |||
112 | ReportInvokeDoneCommand reportInvokeDone; | |||
113 | ReportVMInitCommand reportVMInit; | |||
114 | SuspendThreadCommand suspendThread; | |||
115 | } u; | |||
116 | /* composite array expand out, put nothing after */ | |||
117 | } HelperCommand; | |||
118 | ||||
119 | typedef struct { | |||
120 | HelperCommand *head; | |||
121 | HelperCommand *tail; | |||
122 | } CommandQueue; | |||
123 | ||||
124 | static CommandQueue commandQueue; | |||
125 | static jrawMonitorID commandQueueLock; | |||
126 | static jrawMonitorID commandCompleteLock; | |||
127 | static jrawMonitorID blockCommandLoopLock; | |||
128 | static jrawMonitorID vmDeathLock; | |||
129 | static volatile jboolean commandLoopEnteredVmDeathLock = JNI_FALSE0; | |||
130 | ||||
131 | static jint maxQueueSize = 50 * 1024; /* TO DO: Make this configurable */ | |||
132 | static jboolean holdEvents; | |||
133 | static jint currentQueueSize = 0; | |||
134 | static jint currentSessionID; | |||
135 | ||||
136 | static void saveEventInfoRefs(JNIEnv *env, EventInfo *evinfo); | |||
137 | static void tossEventInfoRefs(JNIEnv *env, EventInfo *evinfo); | |||
138 | ||||
139 | static jint | |||
140 | commandSize(HelperCommand *command) | |||
141 | { | |||
142 | jint size = sizeof(HelperCommand); | |||
143 | if (command->commandKind == COMMAND_REPORT_EVENT_COMPOSITE1) { | |||
144 | /* | |||
145 | * One event is accounted for in the Helper Command. If there are | |||
146 | * more, add to size here. | |||
147 | */ | |||
148 | /*LINTED*/ | |||
149 | size += ((int)sizeof(CommandSingle) * | |||
150 | (command->u.reportEventComposite.eventCount - 1)); | |||
151 | } | |||
152 | return size; | |||
153 | } | |||
154 | ||||
155 | static void | |||
156 | freeCommand(HelperCommand *command) | |||
157 | { | |||
158 | if ( command == NULL((void*)0) ) | |||
159 | return; | |||
160 | jvmtiDeallocate(command); | |||
161 | } | |||
162 | ||||
163 | static void | |||
164 | enqueueCommand(HelperCommand *command, | |||
165 | jboolean wait, jboolean reportingVMDeath) | |||
166 | { | |||
167 | static jboolean vmDeathReported = JNI_FALSE0; | |||
168 | CommandQueue *queue = &commandQueue; | |||
169 | jint size = commandSize(command); | |||
170 | ||||
171 | command->done = JNI_FALSE0; | |||
172 | command->waiting = wait; | |||
173 | command->next = NULL((void*)0); | |||
174 | ||||
175 | debugMonitorEnter(commandQueueLock); | |||
176 | while (size + currentQueueSize > maxQueueSize) { | |||
177 | debugMonitorWait(commandQueueLock); | |||
178 | } | |||
179 | log_debugee_location("enqueueCommand(): HelperCommand being processed", NULL((void*)0), NULL((void*)0), 0); | |||
180 | if (vmDeathReported) { | |||
181 | /* send no more events after VMDeath and don't wait */ | |||
182 | wait = JNI_FALSE0; | |||
183 | } else { | |||
184 | currentQueueSize += size; | |||
185 | ||||
186 | if (queue->head == NULL((void*)0)) { | |||
187 | queue->head = command; | |||
188 | } else { | |||
189 | queue->tail->next = command; | |||
190 | } | |||
191 | queue->tail = command; | |||
192 | ||||
193 | if (reportingVMDeath) { | |||
194 | vmDeathReported = JNI_TRUE1; | |||
195 | } | |||
196 | } | |||
197 | debugMonitorNotifyAll(commandQueueLock); | |||
198 | debugMonitorExit(commandQueueLock); | |||
199 | ||||
200 | if (wait) { | |||
201 | debugMonitorEnter(commandCompleteLock); | |||
202 | while (!command->done) { | |||
203 | log_debugee_location("enqueueCommand(): HelperCommand wait", NULL((void*)0), NULL((void*)0), 0); | |||
204 | debugMonitorWait(commandCompleteLock); | |||
205 | } | |||
206 | freeCommand(command); | |||
207 | debugMonitorExit(commandCompleteLock); | |||
208 | } | |||
209 | } | |||
210 | ||||
211 | static void | |||
212 | completeCommand(HelperCommand *command) | |||
213 | { | |||
214 | if (command->waiting) { | |||
215 | debugMonitorEnter(commandCompleteLock); | |||
216 | command->done = JNI_TRUE1; | |||
217 | log_debugee_location("completeCommand(): HelperCommand done waiting", NULL((void*)0), NULL((void*)0), 0); | |||
218 | debugMonitorNotifyAll(commandCompleteLock); | |||
219 | debugMonitorExit(commandCompleteLock); | |||
220 | } else { | |||
221 | freeCommand(command); | |||
222 | } | |||
223 | } | |||
224 | ||||
225 | static HelperCommand * | |||
226 | dequeueCommand(void) | |||
227 | { | |||
228 | HelperCommand *command = NULL((void*)0); | |||
229 | CommandQueue *queue = &commandQueue; | |||
230 | jint size; | |||
231 | ||||
232 | debugMonitorEnter(commandQueueLock); | |||
233 | ||||
234 | while (command == NULL((void*)0)) { | |||
235 | while (holdEvents || (queue->head == NULL((void*)0))) { | |||
236 | debugMonitorWait(commandQueueLock); | |||
237 | } | |||
238 | ||||
239 | JDI_ASSERT(queue->head)do { if (gdata && gdata->assertOn && !(queue ->head)) { jdiAssertionFailed("/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 239, "queue->head"); } } while (0); | |||
240 | command = queue->head; | |||
241 | queue->head = command->next; | |||
242 | if (queue->tail == command) { | |||
243 | queue->tail = NULL((void*)0); | |||
244 | } | |||
245 | ||||
246 | log_debugee_location("dequeueCommand(): command being dequeued", NULL((void*)0), NULL((void*)0), 0); | |||
247 | ||||
248 | size = commandSize(command); | |||
249 | /* | |||
250 | * Immediately close out any commands enqueued from | |||
251 | * a dead VM or a previously attached debugger. | |||
252 | */ | |||
253 | if (gdata->vmDead || command->sessionID != currentSessionID) { | |||
254 | log_debugee_location("dequeueCommand(): command session removal", NULL((void*)0), NULL((void*)0), 0); | |||
255 | completeCommand(command); | |||
256 | command = NULL((void*)0); | |||
257 | } | |||
258 | ||||
259 | /* | |||
260 | * There's room in the queue for more. | |||
261 | */ | |||
262 | currentQueueSize -= size; | |||
263 | debugMonitorNotifyAll(commandQueueLock); | |||
264 | } | |||
265 | ||||
266 | debugMonitorExit(commandQueueLock); | |||
267 | ||||
268 | return command; | |||
269 | } | |||
270 | ||||
271 | void eventHelper_holdEvents(void) | |||
272 | { | |||
273 | debugMonitorEnter(commandQueueLock); | |||
274 | holdEvents = JNI_TRUE1; | |||
275 | debugMonitorNotifyAll(commandQueueLock); | |||
276 | debugMonitorExit(commandQueueLock); | |||
277 | } | |||
278 | ||||
279 | void eventHelper_releaseEvents(void) | |||
280 | { | |||
281 | debugMonitorEnter(commandQueueLock); | |||
282 | holdEvents = JNI_FALSE0; | |||
283 | debugMonitorNotifyAll(commandQueueLock); | |||
284 | debugMonitorExit(commandQueueLock); | |||
285 | } | |||
286 | ||||
287 | static void | |||
288 | writeSingleStepEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo) | |||
289 | { | |||
290 | (void)outStream_writeObjectRef(env, out, evinfo->thread); | |||
291 | writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location); | |||
292 | } | |||
293 | ||||
294 | static void | |||
295 | writeBreakpointEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo) | |||
296 | { | |||
297 | (void)outStream_writeObjectRef(env, out, evinfo->thread); | |||
298 | writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location); | |||
299 | } | |||
300 | ||||
301 | static void | |||
302 | writeFieldAccessEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo) | |||
303 | { | |||
304 | jbyte fieldClassTag; | |||
305 | ||||
306 | fieldClassTag = referenceTypeTag(evinfo->u.field_access.field_clazz); | |||
307 | ||||
308 | (void)outStream_writeObjectRef(env, out, evinfo->thread); | |||
309 | writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location); | |||
310 | (void)outStream_writeByte(out, fieldClassTag); | |||
311 | (void)outStream_writeObjectRef(env, out, evinfo->u.field_access.field_clazz); | |||
312 | (void)outStream_writeFieldID(out, evinfo->u.field_access.field); | |||
313 | (void)outStream_writeObjectTag(env, out, evinfo->object); | |||
314 | (void)outStream_writeObjectRef(env, out, evinfo->object); | |||
315 | } | |||
316 | ||||
317 | static void | |||
318 | writeFieldModificationEvent(JNIEnv *env, PacketOutputStream *out, | |||
319 | EventInfo *evinfo) | |||
320 | { | |||
321 | jbyte fieldClassTag; | |||
322 | ||||
323 | fieldClassTag = referenceTypeTag(evinfo->u.field_modification.field_clazz); | |||
324 | ||||
325 | (void)outStream_writeObjectRef(env, out, evinfo->thread); | |||
326 | writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location); | |||
327 | (void)outStream_writeByte(out, fieldClassTag); | |||
328 | (void)outStream_writeObjectRef(env, out, evinfo->u.field_modification.field_clazz); | |||
329 | (void)outStream_writeFieldID(out, evinfo->u.field_modification.field); | |||
330 | (void)outStream_writeObjectTag(env, out, evinfo->object); | |||
331 | (void)outStream_writeObjectRef(env, out, evinfo->object); | |||
332 | (void)outStream_writeValue(env, out, (jbyte)evinfo->u.field_modification.signature_type, | |||
333 | evinfo->u.field_modification.new_value); | |||
334 | } | |||
335 | ||||
336 | static void | |||
337 | writeExceptionEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo) | |||
338 | { | |||
339 | (void)outStream_writeObjectRef(env, out, evinfo->thread); | |||
340 | writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location); | |||
341 | (void)outStream_writeObjectTag(env, out, evinfo->object); | |||
342 | (void)outStream_writeObjectRef(env, out, evinfo->object); | |||
343 | writeCodeLocation(out, evinfo->u.exception.catch_clazz, | |||
344 | evinfo->u.exception.catch_method, evinfo->u.exception.catch_location); | |||
345 | } | |||
346 | ||||
347 | static void | |||
348 | writeThreadEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo) | |||
349 | { | |||
350 | (void)outStream_writeObjectRef(env, out, evinfo->thread); | |||
351 | } | |||
352 | ||||
353 | static void | |||
354 | writeMonitorEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo) | |||
355 | { | |||
356 | jclass klass; | |||
357 | (void)outStream_writeObjectRef(env, out, evinfo->thread); | |||
358 | (void)outStream_writeObjectTag(env, out, evinfo->object); | |||
359 | (void)outStream_writeObjectRef(env, out, evinfo->object); | |||
360 | if (evinfo->ei == EI_MONITOR_WAIT || evinfo->ei == EI_MONITOR_WAITED) { | |||
361 | /* clazz of evinfo was set to class of monitor object for monitor wait event class filtering. | |||
362 | * So get the method class to write location info. | |||
363 | * See cbMonitorWait() and cbMonitorWaited() function in eventHandler.c. | |||
364 | */ | |||
365 | klass=getMethodClass(gdata->jvmti, evinfo->method); | |||
366 | writeCodeLocation(out, klass, evinfo->method, evinfo->location); | |||
367 | if (evinfo->ei == EI_MONITOR_WAIT) { | |||
368 | (void)outStream_writeLong(out, evinfo->u.monitor.timeout); | |||
369 | } else if (evinfo->ei == EI_MONITOR_WAITED) { | |||
370 | (void)outStream_writeBoolean(out, evinfo->u.monitor.timed_out); | |||
371 | } | |||
372 | /* This runs in a command loop and this thread may not return to java. | |||
373 | * So we need to delete the local ref created by jvmti GetMethodDeclaringClass. | |||
374 | */ | |||
375 | JNI_FUNC_PTR(env,DeleteLocalRef)(*((*((((gdata->log_flags & (0x00000002)) ?(log_message_begin ("JNI","/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" ,375), log_message_end ("%s()","DeleteLocalRef")):((void)0)), (env))))->DeleteLocalRef))(env, klass); | |||
376 | } else { | |||
377 | writeCodeLocation(out, evinfo->clazz, evinfo->method, evinfo->location); | |||
378 | } | |||
379 | } | |||
380 | ||||
381 | static void | |||
382 | writeClassEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo) | |||
383 | { | |||
384 | jbyte classTag; | |||
385 | jint status; | |||
386 | char *signature = NULL((void*)0); | |||
387 | jvmtiError error; | |||
388 | ||||
389 | classTag = referenceTypeTag(evinfo->clazz); | |||
390 | error = classSignature(evinfo->clazz, &signature, NULL((void*)0)); | |||
391 | if (error != JVMTI_ERROR_NONE) { | |||
392 | EXIT_ERROR(error,"signature"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)error), error, ("signature"==((void *)0)?"":"signature"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 392); debugInit_exit((jvmtiError)error, "signature"); }; | |||
393 | } | |||
394 | status = classStatus(evinfo->clazz); | |||
395 | ||||
396 | (void)outStream_writeObjectRef(env, out, evinfo->thread); | |||
397 | (void)outStream_writeByte(out, classTag); | |||
398 | (void)outStream_writeObjectRef(env, out, evinfo->clazz); | |||
399 | (void)outStream_writeString(out, signature); | |||
400 | (void)outStream_writeInt(out, map2jdwpClassStatus(status)); | |||
401 | jvmtiDeallocate(signature); | |||
402 | } | |||
403 | ||||
404 | static void | |||
405 | writeVMDeathEvent(JNIEnv *env, PacketOutputStream *out, EventInfo *evinfo) | |||
406 | { | |||
407 | } | |||
408 | ||||
409 | static void | |||
410 | handleEventCommandSingle(JNIEnv *env, PacketOutputStream *out, | |||
411 | EventCommandSingle *command) | |||
412 | { | |||
413 | EventInfo *evinfo = &command->info; | |||
414 | ||||
415 | (void)outStream_writeByte(out, eventIndex2jdwp(evinfo->ei)); | |||
416 | (void)outStream_writeInt(out, command->id); | |||
417 | ||||
418 | switch (evinfo->ei) { | |||
419 | case EI_SINGLE_STEP: | |||
420 | writeSingleStepEvent(env, out, evinfo); | |||
421 | break; | |||
422 | case EI_BREAKPOINT: | |||
423 | writeBreakpointEvent(env, out, evinfo); | |||
424 | break; | |||
425 | case EI_FIELD_ACCESS: | |||
426 | writeFieldAccessEvent(env, out, evinfo); | |||
427 | break; | |||
428 | case EI_FIELD_MODIFICATION: | |||
429 | writeFieldModificationEvent(env, out, evinfo); | |||
430 | break; | |||
431 | case EI_EXCEPTION: | |||
432 | writeExceptionEvent(env, out, evinfo); | |||
433 | break; | |||
434 | case EI_THREAD_START: | |||
435 | case EI_THREAD_END: | |||
436 | writeThreadEvent(env, out, evinfo); | |||
437 | break; | |||
438 | case EI_CLASS_LOAD: | |||
439 | case EI_CLASS_PREPARE: | |||
440 | writeClassEvent(env, out, evinfo); | |||
441 | break; | |||
442 | case EI_MONITOR_CONTENDED_ENTER: | |||
443 | case EI_MONITOR_CONTENDED_ENTERED: | |||
444 | case EI_MONITOR_WAIT: | |||
445 | case EI_MONITOR_WAITED: | |||
446 | writeMonitorEvent(env, out, evinfo); | |||
447 | break; | |||
448 | case EI_VM_DEATH: | |||
449 | writeVMDeathEvent(env, out, evinfo); | |||
450 | break; | |||
451 | default: | |||
452 | EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"unknown event index"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +24))), ((jvmtiError)(JVMTI_ERROR_MAX+64+24)), ("unknown event index" ==((void*)0)?"":"unknown event index"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 452); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+24)), "unknown event index"); }; | |||
453 | break; | |||
454 | } | |||
455 | tossEventInfoRefs(env, evinfo); | |||
456 | } | |||
457 | ||||
458 | static void | |||
459 | handleUnloadCommandSingle(JNIEnv* env, PacketOutputStream *out, | |||
460 | UnloadCommandSingle *command) | |||
461 | { | |||
462 | (void)outStream_writeByte(out, JDWP_EVENT(CLASS_UNLOAD)9); | |||
463 | (void)outStream_writeInt(out, command->id); | |||
464 | (void)outStream_writeString(out, command->classSignature); | |||
465 | jvmtiDeallocate(command->classSignature); | |||
466 | command->classSignature = NULL((void*)0); | |||
467 | } | |||
468 | ||||
469 | static void | |||
470 | handleFrameEventCommandSingle(JNIEnv* env, PacketOutputStream *out, | |||
471 | FrameEventCommandSingle *command) | |||
472 | { | |||
473 | if (command->typeKey) { | |||
474 | (void)outStream_writeByte(out, JDWP_EVENT(METHOD_EXIT_WITH_RETURN_VALUE)42); | |||
475 | } else { | |||
476 | (void)outStream_writeByte(out, eventIndex2jdwp(command->ei)); | |||
477 | } | |||
478 | (void)outStream_writeInt(out, command->id); | |||
479 | (void)outStream_writeObjectRef(env, out, command->thread); | |||
480 | writeCodeLocation(out, command->clazz, command->method, command->location); | |||
481 | if (command->typeKey) { | |||
482 | (void)outStream_writeValue(env, out, command->typeKey, command->returnValue); | |||
483 | if (isReferenceTag(command->typeKey) && | |||
484 | command->returnValue.l != NULL((void*)0)) { | |||
485 | tossGlobalRef(env, &(command->returnValue.l)); | |||
486 | } | |||
487 | } | |||
488 | tossGlobalRef(env, &(command->thread)); | |||
489 | tossGlobalRef(env, &(command->clazz)); | |||
490 | } | |||
491 | ||||
492 | static void | |||
493 | suspendWithInvokeEnabled(jbyte policy, jthread thread) | |||
494 | { | |||
495 | invoker_enableInvokeRequests(thread); | |||
496 | ||||
497 | if (policy == JDWP_SUSPEND_POLICY(ALL)2) { | |||
498 | (void)threadControl_suspendAll(); | |||
499 | } else { | |||
500 | (void)threadControl_suspendThread(thread, JNI_FALSE0); | |||
501 | } | |||
502 | } | |||
503 | ||||
504 | static void | |||
505 | handleReportEventCompositeCommand(JNIEnv *env, | |||
506 | ReportEventCompositeCommand *recc) | |||
507 | { | |||
508 | PacketOutputStream out; | |||
509 | jint count = recc->eventCount; | |||
510 | jint i; | |||
511 | ||||
512 | if (recc->suspendPolicy != JDWP_SUSPEND_POLICY(NONE)0) { | |||
513 | /* must determine thread to interrupt before writing */ | |||
514 | /* since writing destroys it */ | |||
515 | jthread thread = NULL((void*)0); | |||
516 | for (i = 0; i < count; i++) { | |||
517 | CommandSingle *single = &(recc->singleCommand[i]); | |||
518 | switch (single->singleKind) { | |||
519 | case COMMAND_SINGLE_EVENT11: | |||
520 | thread = single->u.eventCommand.info.thread; | |||
521 | break; | |||
522 | case COMMAND_SINGLE_FRAME_EVENT13: | |||
523 | thread = single->u.frameEventCommand.thread; | |||
524 | break; | |||
525 | } | |||
526 | if (thread != NULL((void*)0)) { | |||
527 | break; | |||
528 | } | |||
529 | } | |||
530 | ||||
531 | if (thread == NULL((void*)0)) { | |||
532 | (void)threadControl_suspendAll(); | |||
533 | } else { | |||
534 | suspendWithInvokeEnabled(recc->suspendPolicy, thread); | |||
535 | } | |||
536 | } | |||
537 | ||||
538 | outStream_initCommand(&out, uniqueID(), 0x0, | |||
539 | JDWP_COMMAND_SET(Event)64, | |||
540 | JDWP_COMMAND(Event, Composite)100); | |||
541 | (void)outStream_writeByte(&out, recc->suspendPolicy); | |||
542 | (void)outStream_writeInt(&out, count); | |||
543 | ||||
544 | for (i = 0; i < count; i++) { | |||
545 | CommandSingle *single = &(recc->singleCommand[i]); | |||
546 | switch (single->singleKind) { | |||
547 | case COMMAND_SINGLE_EVENT11: | |||
548 | handleEventCommandSingle(env, &out, | |||
549 | &single->u.eventCommand); | |||
550 | break; | |||
551 | case COMMAND_SINGLE_UNLOAD12: | |||
552 | handleUnloadCommandSingle(env, &out, | |||
553 | &single->u.unloadCommand); | |||
554 | break; | |||
555 | case COMMAND_SINGLE_FRAME_EVENT13: | |||
556 | handleFrameEventCommandSingle(env, &out, | |||
557 | &single->u.frameEventCommand); | |||
558 | break; | |||
559 | } | |||
560 | } | |||
561 | ||||
562 | outStream_sendCommand(&out); | |||
563 | outStream_destroy(&out); | |||
564 | } | |||
565 | ||||
566 | static void | |||
567 | handleReportInvokeDoneCommand(JNIEnv* env, ReportInvokeDoneCommand *command) | |||
568 | { | |||
569 | invoker_completeInvokeRequest(command->thread); | |||
570 | tossGlobalRef(env, &(command->thread)); | |||
571 | } | |||
572 | ||||
573 | static void | |||
574 | handleReportVMInitCommand(JNIEnv* env, ReportVMInitCommand *command) | |||
575 | { | |||
576 | PacketOutputStream out; | |||
577 | ||||
578 | if (command->suspendPolicy == JDWP_SUSPEND_POLICY(ALL)2) { | |||
579 | (void)threadControl_suspendAll(); | |||
580 | } else if (command->suspendPolicy == JDWP_SUSPEND_POLICY(EVENT_THREAD)1) { | |||
581 | (void)threadControl_suspendThread(command->thread, JNI_FALSE0); | |||
582 | } | |||
583 | ||||
584 | outStream_initCommand(&out, uniqueID(), 0x0, | |||
585 | JDWP_COMMAND_SET(Event)64, | |||
586 | JDWP_COMMAND(Event, Composite)100); | |||
587 | (void)outStream_writeByte(&out, command->suspendPolicy); | |||
588 | (void)outStream_writeInt(&out, 1); /* Always one component */ | |||
589 | (void)outStream_writeByte(&out, JDWP_EVENT(VM_INIT)90); | |||
590 | (void)outStream_writeInt(&out, 0); /* Not in response to an event req. */ | |||
591 | ||||
592 | (void)outStream_writeObjectRef(env, &out, command->thread); | |||
593 | ||||
594 | outStream_sendCommand(&out); | |||
595 | outStream_destroy(&out); | |||
596 | /* Why aren't we tossing this: tossGlobalRef(env, &(command->thread)); */ | |||
597 | } | |||
598 | ||||
599 | static void | |||
600 | handleSuspendThreadCommand(JNIEnv* env, SuspendThreadCommand *command) | |||
601 | { | |||
602 | /* | |||
603 | * For the moment, there's nothing that can be done with the | |||
604 | * return code, so we don't check it here. | |||
605 | */ | |||
606 | (void)threadControl_suspendThread(command->thread, JNI_TRUE1); | |||
607 | tossGlobalRef(env, &(command->thread)); | |||
608 | } | |||
609 | ||||
610 | static void | |||
611 | handleCommand(JNIEnv *env, HelperCommand *command) | |||
612 | { | |||
613 | switch (command->commandKind) { | |||
614 | case COMMAND_REPORT_EVENT_COMPOSITE1: | |||
615 | handleReportEventCompositeCommand(env, | |||
616 | &command->u.reportEventComposite); | |||
617 | break; | |||
618 | case COMMAND_REPORT_INVOKE_DONE2: | |||
619 | handleReportInvokeDoneCommand(env, &command->u.reportInvokeDone); | |||
620 | break; | |||
621 | case COMMAND_REPORT_VM_INIT3: | |||
622 | handleReportVMInitCommand(env, &command->u.reportVMInit); | |||
623 | break; | |||
624 | case COMMAND_SUSPEND_THREAD4: | |||
625 | handleSuspendThreadCommand(env, &command->u.suspendThread); | |||
626 | break; | |||
627 | default: | |||
628 | EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"Event Helper Command"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +24))), ((jvmtiError)(JVMTI_ERROR_MAX+64+24)), ("Event Helper Command" ==((void*)0)?"":"Event Helper Command"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 628); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+24)), "Event Helper Command"); }; | |||
629 | break; | |||
630 | } | |||
631 | } | |||
632 | ||||
633 | /* | |||
634 | * There was an assumption that only one event with a suspend-all | |||
635 | * policy could be processed by commandLoop() at one time. It was | |||
636 | * assumed that native thread suspension from the first suspend-all | |||
637 | * event would prevent the second suspend-all event from making it | |||
638 | * into the command queue. For the Classic VM, this was a reasonable | |||
639 | * assumption. However, in HotSpot all thread suspension requires a | |||
640 | * VM operation and VM operations take time. | |||
641 | * | |||
642 | * The solution is to add a mechanism to prevent commandLoop() from | |||
643 | * processing more than one event with a suspend-all policy. This is | |||
644 | * accomplished by forcing commandLoop() to wait for either | |||
645 | * ThreadReferenceImpl.c: resume() or VirtualMachineImpl.c: resume() | |||
646 | * when an event with a suspend-all policy has been completed. | |||
647 | */ | |||
648 | static jboolean blockCommandLoop = JNI_FALSE0; | |||
649 | ||||
650 | /* | |||
651 | * We wait for either ThreadReferenceImpl.c: resume() or | |||
652 | * VirtualMachineImpl.c: resume() to be called. | |||
653 | */ | |||
654 | static void | |||
655 | doBlockCommandLoop(void) { | |||
656 | debugMonitorEnter(blockCommandLoopLock); | |||
657 | while (blockCommandLoop == JNI_TRUE1) { | |||
658 | debugMonitorWait(blockCommandLoopLock); | |||
659 | } | |||
660 | debugMonitorExit(blockCommandLoopLock); | |||
661 | } | |||
662 | ||||
663 | /* | |||
664 | * If the command that we are about to execute has a suspend-all | |||
665 | * policy, then prepare for either ThreadReferenceImpl.c: resume() | |||
666 | * or VirtualMachineImpl.c: resume() to be called. | |||
667 | */ | |||
668 | static jboolean | |||
669 | needBlockCommandLoop(HelperCommand *cmd) { | |||
670 | if (cmd->commandKind == COMMAND_REPORT_EVENT_COMPOSITE1 | |||
671 | && cmd->u.reportEventComposite.suspendPolicy == JDWP_SUSPEND_POLICY(ALL)2) { | |||
672 | debugMonitorEnter(blockCommandLoopLock); | |||
673 | blockCommandLoop = JNI_TRUE1; | |||
674 | debugMonitorExit(blockCommandLoopLock); | |||
675 | ||||
676 | return JNI_TRUE1; | |||
677 | } | |||
678 | ||||
679 | return JNI_FALSE0; | |||
680 | } | |||
681 | ||||
682 | /* | |||
683 | * Used by either ThreadReferenceImpl.c: resume() or | |||
684 | * VirtualMachineImpl.c: resume() to resume commandLoop(). | |||
685 | */ | |||
686 | void | |||
687 | unblockCommandLoop(void) { | |||
688 | debugMonitorEnter(blockCommandLoopLock); | |||
689 | blockCommandLoop = JNI_FALSE0; | |||
690 | debugMonitorNotifyAll(blockCommandLoopLock); | |||
691 | debugMonitorExit(blockCommandLoopLock); | |||
692 | } | |||
693 | ||||
694 | /* | |||
695 | * The event helper thread. Dequeues commands and processes them. | |||
696 | */ | |||
697 | static void JNICALL | |||
698 | commandLoop(jvmtiEnv* jvmti_env, JNIEnv* jni_env, void* arg) | |||
699 | { | |||
700 | LOG_MISC(("Begin command loop thread"))((gdata->log_flags & (0x00000008)) ?(log_message_begin ("MISC","/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" ,700), log_message_end ("Begin command loop thread")):((void) 0)); | |||
701 | ||||
702 | while (JNI_TRUE1) { | |||
703 | HelperCommand *command = dequeueCommand(); | |||
704 | if (command != NULL((void*)0)) { | |||
705 | /* | |||
706 | * Setup for a potential doBlockCommand() call before calling | |||
707 | * handleCommand() to prevent any races. | |||
708 | */ | |||
709 | jboolean doBlock = needBlockCommandLoop(command); | |||
710 | debugMonitorEnter(vmDeathLock); | |||
711 | commandLoopEnteredVmDeathLock = JNI_TRUE1; | |||
712 | if (!gdata->vmDead) { | |||
713 | log_debugee_location("commandLoop(): command being handled", NULL((void*)0), NULL((void*)0), 0); | |||
714 | handleCommand(jni_env, command); | |||
715 | } | |||
716 | completeCommand(command); | |||
717 | debugMonitorExit(vmDeathLock); | |||
718 | commandLoopEnteredVmDeathLock = JNI_FALSE0; | |||
719 | /* if we just finished a suspend-all cmd, then we block here */ | |||
720 | if (doBlock) { | |||
721 | doBlockCommandLoop(); | |||
722 | } | |||
723 | } | |||
724 | } | |||
725 | /* This loop never ends, even as connections come and go with server=y */ | |||
726 | } | |||
727 | ||||
728 | void | |||
729 | eventHelper_initialize(jbyte sessionID) | |||
730 | { | |||
731 | jvmtiStartFunction func; | |||
732 | ||||
733 | currentSessionID = sessionID; | |||
734 | holdEvents = JNI_FALSE0; | |||
735 | commandQueue.head = NULL((void*)0); | |||
736 | commandQueue.tail = NULL((void*)0); | |||
737 | ||||
738 | commandQueueLock = debugMonitorCreate("JDWP Event Helper Queue Monitor"); | |||
739 | commandCompleteLock = debugMonitorCreate("JDWP Event Helper Completion Monitor"); | |||
740 | blockCommandLoopLock = debugMonitorCreate("JDWP Event Block CommandLoop Monitor"); | |||
741 | vmDeathLock = debugMonitorCreate("JDWP VM_DEATH CommandLoop Monitor"); | |||
742 | ||||
743 | /* Start the event handler thread */ | |||
744 | func = &commandLoop; | |||
745 | (void)spawnNewThread(func, NULL((void*)0), COMMAND_LOOP_THREAD_NAME"JDWP Event Helper Thread"); | |||
746 | } | |||
747 | ||||
748 | void | |||
749 | eventHelper_reset(jbyte newSessionID) | |||
750 | { | |||
751 | debugMonitorEnter(commandQueueLock); | |||
752 | currentSessionID = newSessionID; | |||
753 | holdEvents = JNI_FALSE0; | |||
754 | debugMonitorNotifyAll(commandQueueLock); | |||
755 | debugMonitorExit(commandQueueLock); | |||
756 | unblockCommandLoop(); | |||
757 | } | |||
758 | ||||
759 | /* | |||
760 | * Provide a means for threadControl to ensure that crucial locks are not | |||
761 | * held by suspended threads. | |||
762 | */ | |||
763 | void | |||
764 | eventHelper_lock(void) | |||
765 | { | |||
766 | debugMonitorEnter(commandQueueLock); | |||
767 | debugMonitorEnter(commandCompleteLock); | |||
768 | } | |||
769 | ||||
770 | void | |||
771 | eventHelper_unlock(void) | |||
772 | { | |||
773 | debugMonitorExit(commandCompleteLock); | |||
774 | debugMonitorExit(commandQueueLock); | |||
775 | } | |||
776 | ||||
777 | void commandLoop_exitVmDeathLockOnError() | |||
778 | { | |||
779 | const char* MSG_BASE = "exitVmDeathLockOnError: error in JVMTI %s: %d\n"; | |||
780 | jthread cur_thread = NULL((void*)0); | |||
781 | jvmtiThreadInfo thread_info; | |||
782 | jvmtiError err = JVMTI_ERROR_NONE; | |||
783 | ||||
784 | err = JVMTI_FUNC_PTR(gdata->jvmti, GetCurrentThread)(*((*((((gdata->log_flags & (0x00000004))?(log_message_begin ("JVMTI","/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" ,784), log_message_end ("%s()","GetCurrentThread")):((void)0) ),(gdata->jvmti))))->GetCurrentThread)) | |||
785 | (gdata->jvmti, &cur_thread); | |||
786 | if (err != JVMTI_ERROR_NONE) { | |||
787 | LOG_ERROR((MSG_BASE, "GetCurrentThread", err))((gdata->log_flags & (0x00000080))?(log_message_begin( "ERROR","/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" ,787), log_message_end (MSG_BASE, "GetCurrentThread", err)):( (void)0)); | |||
788 | return; | |||
789 | } | |||
790 | ||||
791 | err = JVMTI_FUNC_PTR(gdata->jvmti, GetThreadInfo)(*((*((((gdata->log_flags & (0x00000004))?(log_message_begin ("JVMTI","/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" ,791), log_message_end ("%s()","GetThreadInfo")):((void)0)),( gdata->jvmti))))->GetThreadInfo)) | |||
792 | (gdata->jvmti, cur_thread, &thread_info); | |||
793 | if (err != JVMTI_ERROR_NONE) { | |||
794 | LOG_ERROR((MSG_BASE, "GetThreadInfo", err))((gdata->log_flags & (0x00000080))?(log_message_begin( "ERROR","/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" ,794), log_message_end (MSG_BASE, "GetThreadInfo", err)):((void )0)); | |||
795 | return; | |||
796 | } | |||
797 | if (strcmp(thread_info.name, COMMAND_LOOP_THREAD_NAME"JDWP Event Helper Thread") != 0) { | |||
798 | return; | |||
799 | } | |||
800 | if (commandLoopEnteredVmDeathLock == JNI_TRUE1) { | |||
801 | debugMonitorExit(vmDeathLock); | |||
802 | commandLoopEnteredVmDeathLock = JNI_FALSE0; | |||
803 | } | |||
804 | } | |||
805 | ||||
806 | void | |||
807 | commandLoop_sync(void) | |||
808 | { | |||
809 | debugMonitorEnter(vmDeathLock); | |||
810 | debugMonitorExit(vmDeathLock); | |||
811 | } | |||
812 | ||||
813 | /* Change all references to global in the EventInfo struct */ | |||
814 | static void | |||
815 | saveEventInfoRefs(JNIEnv *env, EventInfo *evinfo) | |||
816 | { | |||
817 | jthread *pthread; | |||
818 | jclass *pclazz; | |||
819 | jobject *pobject; | |||
820 | jthread thread; | |||
821 | jclass clazz; | |||
822 | jobject object; | |||
823 | char sig; | |||
824 | ||||
825 | JNI_FUNC_PTR(env,ExceptionClear)(*((*((((gdata->log_flags & (0x00000002)) ?(log_message_begin ("JNI","/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" ,825), log_message_end ("%s()","ExceptionClear")):((void)0)), (env))))->ExceptionClear))(env); | |||
826 | ||||
827 | if ( evinfo->thread != NULL((void*)0) ) { | |||
828 | pthread = &(evinfo->thread); | |||
829 | thread = *pthread; | |||
830 | *pthread = NULL((void*)0); | |||
831 | saveGlobalRef(env, thread, pthread); | |||
832 | } | |||
833 | if ( evinfo->clazz != NULL((void*)0) ) { | |||
834 | pclazz = &(evinfo->clazz); | |||
835 | clazz = *pclazz; | |||
836 | *pclazz = NULL((void*)0); | |||
837 | saveGlobalRef(env, clazz, pclazz); | |||
838 | } | |||
839 | if ( evinfo->object != NULL((void*)0) ) { | |||
840 | pobject = &(evinfo->object); | |||
841 | object = *pobject; | |||
842 | *pobject = NULL((void*)0); | |||
843 | saveGlobalRef(env, object, pobject); | |||
844 | } | |||
845 | ||||
846 | switch (evinfo->ei) { | |||
847 | case EI_FIELD_MODIFICATION: | |||
848 | if ( evinfo->u.field_modification.field_clazz != NULL((void*)0) ) { | |||
849 | pclazz = &(evinfo->u.field_modification.field_clazz); | |||
850 | clazz = *pclazz; | |||
851 | *pclazz = NULL((void*)0); | |||
852 | saveGlobalRef(env, clazz, pclazz); | |||
853 | } | |||
854 | sig = evinfo->u.field_modification.signature_type; | |||
855 | if (isReferenceTag(sig)) { | |||
856 | if ( evinfo->u.field_modification.new_value.l != NULL((void*)0) ) { | |||
857 | pobject = &(evinfo->u.field_modification.new_value.l); | |||
858 | object = *pobject; | |||
859 | *pobject = NULL((void*)0); | |||
860 | saveGlobalRef(env, object, pobject); | |||
861 | } | |||
862 | } | |||
863 | break; | |||
864 | case EI_FIELD_ACCESS: | |||
865 | if ( evinfo->u.field_access.field_clazz != NULL((void*)0) ) { | |||
866 | pclazz = &(evinfo->u.field_access.field_clazz); | |||
867 | clazz = *pclazz; | |||
868 | *pclazz = NULL((void*)0); | |||
869 | saveGlobalRef(env, clazz, pclazz); | |||
870 | } | |||
871 | break; | |||
872 | case EI_EXCEPTION: | |||
873 | if ( evinfo->u.exception.catch_clazz != NULL((void*)0) ) { | |||
874 | pclazz = &(evinfo->u.exception.catch_clazz); | |||
875 | clazz = *pclazz; | |||
876 | *pclazz = NULL((void*)0); | |||
877 | saveGlobalRef(env, clazz, pclazz); | |||
878 | } | |||
879 | break; | |||
880 | default: | |||
881 | break; | |||
882 | } | |||
883 | ||||
884 | if (JNI_FUNC_PTR(env,ExceptionOccurred)(*((*((((gdata->log_flags & (0x00000002)) ?(log_message_begin ("JNI","/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" ,884), log_message_end ("%s()","ExceptionOccurred")):((void)0 )), (env))))->ExceptionOccurred))(env)) { | |||
885 | EXIT_ERROR(AGENT_ERROR_INVALID_EVENT_TYPE,"ExceptionOccurred"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +24))), ((jvmtiError)(JVMTI_ERROR_MAX+64+24)), ("ExceptionOccurred" ==((void*)0)?"":"ExceptionOccurred"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 885); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+24)), "ExceptionOccurred"); }; | |||
886 | } | |||
887 | } | |||
888 | ||||
889 | static void | |||
890 | tossEventInfoRefs(JNIEnv *env, EventInfo *evinfo) | |||
891 | { | |||
892 | char sig; | |||
893 | if ( evinfo->thread != NULL((void*)0) ) { | |||
894 | tossGlobalRef(env, &(evinfo->thread)); | |||
895 | } | |||
896 | if ( evinfo->clazz != NULL((void*)0) ) { | |||
897 | tossGlobalRef(env, &(evinfo->clazz)); | |||
898 | } | |||
899 | if ( evinfo->object != NULL((void*)0) ) { | |||
900 | tossGlobalRef(env, &(evinfo->object)); | |||
901 | } | |||
902 | switch (evinfo->ei) { | |||
903 | case EI_FIELD_MODIFICATION: | |||
904 | if ( evinfo->u.field_modification.field_clazz != NULL((void*)0) ) { | |||
905 | tossGlobalRef(env, &(evinfo->u.field_modification.field_clazz)); | |||
906 | } | |||
907 | sig = evinfo->u.field_modification.signature_type; | |||
908 | if (isReferenceTag(sig)) { | |||
909 | if ( evinfo->u.field_modification.new_value.l != NULL((void*)0) ) { | |||
910 | tossGlobalRef(env, &(evinfo->u.field_modification.new_value.l)); | |||
911 | } | |||
912 | } | |||
913 | break; | |||
914 | case EI_FIELD_ACCESS: | |||
915 | if ( evinfo->u.field_access.field_clazz != NULL((void*)0) ) { | |||
916 | tossGlobalRef(env, &(evinfo->u.field_access.field_clazz)); | |||
917 | } | |||
918 | break; | |||
919 | case EI_EXCEPTION: | |||
920 | if ( evinfo->u.exception.catch_clazz != NULL((void*)0) ) { | |||
921 | tossGlobalRef(env, &(evinfo->u.exception.catch_clazz)); | |||
922 | } | |||
923 | break; | |||
924 | default: | |||
925 | break; | |||
926 | } | |||
927 | } | |||
928 | ||||
929 | struct bag * | |||
930 | eventHelper_createEventBag(void) | |||
931 | { | |||
932 | return bagCreateBag(sizeof(CommandSingle), 5 /* events */ ); | |||
933 | } | |||
934 | ||||
935 | /* Return the combined suspend policy for the event set | |||
936 | */ | |||
937 | static jboolean | |||
938 | enumForCombinedSuspendPolicy(void *cv, void *arg) | |||
939 | { | |||
940 | CommandSingle *command = cv; | |||
941 | jbyte thisPolicy; | |||
942 | jbyte *policy = arg; | |||
943 | ||||
944 | switch(command->singleKind) { | |||
945 | case COMMAND_SINGLE_EVENT11: | |||
946 | thisPolicy = command->u.eventCommand.suspendPolicy; | |||
947 | break; | |||
948 | case COMMAND_SINGLE_FRAME_EVENT13: | |||
949 | thisPolicy = command->u.frameEventCommand.suspendPolicy; | |||
950 | break; | |||
951 | default: | |||
952 | thisPolicy = JDWP_SUSPEND_POLICY(NONE)0; | |||
953 | } | |||
954 | /* Expand running policy value if this policy demands it */ | |||
955 | if (*policy == JDWP_SUSPEND_POLICY(NONE)0) { | |||
956 | *policy = thisPolicy; | |||
957 | } else if (*policy == JDWP_SUSPEND_POLICY(EVENT_THREAD)1) { | |||
958 | *policy = (thisPolicy == JDWP_SUSPEND_POLICY(ALL)2)? | |||
959 | thisPolicy : *policy; | |||
960 | } | |||
961 | ||||
962 | /* Short circuit if we reached maximal suspend policy */ | |||
963 | if (*policy == JDWP_SUSPEND_POLICY(ALL)2) { | |||
964 | return JNI_FALSE0; | |||
965 | } else { | |||
966 | return JNI_TRUE1; | |||
967 | } | |||
968 | } | |||
969 | ||||
970 | /* Determine whether we are reporting VM death | |||
971 | */ | |||
972 | static jboolean | |||
973 | enumForVMDeath(void *cv, void *arg) | |||
974 | { | |||
975 | CommandSingle *command = cv; | |||
976 | jboolean *reportingVMDeath = arg; | |||
977 | ||||
978 | if (command->singleKind == COMMAND_SINGLE_EVENT11) { | |||
979 | if (command->u.eventCommand.info.ei == EI_VM_DEATH) { | |||
980 | *reportingVMDeath = JNI_TRUE1; | |||
981 | return JNI_FALSE0; | |||
982 | } | |||
983 | } | |||
984 | return JNI_TRUE1; | |||
985 | } | |||
986 | ||||
987 | struct singleTracker { | |||
988 | ReportEventCompositeCommand *recc; | |||
989 | int index; | |||
990 | }; | |||
991 | ||||
992 | static jboolean | |||
993 | enumForCopyingSingles(void *command, void *tv) | |||
994 | { | |||
995 | struct singleTracker *tracker = (struct singleTracker *)tv; | |||
996 | (void)memcpy(&tracker->recc->singleCommand[tracker->index++], | |||
997 | command, | |||
998 | sizeof(CommandSingle)); | |||
999 | return JNI_TRUE1; | |||
1000 | } | |||
1001 | ||||
1002 | jbyte | |||
1003 | eventHelper_reportEvents(jbyte sessionID, struct bag *eventBag) | |||
1004 | { | |||
1005 | int size = bagSize(eventBag); | |||
1006 | jbyte suspendPolicy = JDWP_SUSPEND_POLICY(NONE)0; | |||
1007 | jboolean reportingVMDeath = JNI_FALSE0; | |||
1008 | jboolean wait; | |||
1009 | int command_size; | |||
1010 | ||||
1011 | HelperCommand *command; | |||
1012 | ReportEventCompositeCommand *recc; | |||
1013 | struct singleTracker tracker; | |||
1014 | ||||
1015 | if (size == 0) { | |||
1016 | return suspendPolicy; | |||
1017 | } | |||
1018 | (void)bagEnumerateOver(eventBag, enumForCombinedSuspendPolicy, &suspendPolicy); | |||
1019 | (void)bagEnumerateOver(eventBag, enumForVMDeath, &reportingVMDeath); | |||
1020 | ||||
1021 | /*LINTED*/ | |||
1022 | command_size = (int)(sizeof(HelperCommand) + | |||
1023 | sizeof(CommandSingle)*(size-1)); | |||
1024 | command = jvmtiAllocate(command_size); | |||
1025 | (void)memset(command, 0, command_size); | |||
1026 | command->commandKind = COMMAND_REPORT_EVENT_COMPOSITE1; | |||
1027 | command->sessionID = sessionID; | |||
1028 | recc = &command->u.reportEventComposite; | |||
1029 | recc->suspendPolicy = suspendPolicy; | |||
1030 | recc->eventCount = size; | |||
1031 | tracker.recc = recc; | |||
1032 | tracker.index = 0; | |||
1033 | (void)bagEnumerateOver(eventBag, enumForCopyingSingles, &tracker); | |||
1034 | ||||
1035 | /* | |||
1036 | * We must wait if this thread (the event thread) is to be | |||
1037 | * suspended or if the VM is about to die. (Waiting in the latter | |||
1038 | * case ensures that we get the event out before the process dies.) | |||
1039 | */ | |||
1040 | wait = (jboolean)((suspendPolicy != JDWP_SUSPEND_POLICY(NONE)0) || | |||
1041 | reportingVMDeath); | |||
1042 | enqueueCommand(command, wait, reportingVMDeath); | |||
1043 | return suspendPolicy; | |||
1044 | } | |||
1045 | ||||
1046 | void | |||
1047 | eventHelper_recordEvent(EventInfo *evinfo, jint id, jbyte suspendPolicy, | |||
1048 | struct bag *eventBag) | |||
1049 | { | |||
1050 | JNIEnv *env = getEnv(); | |||
1051 | CommandSingle *command = bagAdd(eventBag); | |||
1052 | if (command == NULL((void*)0)) { | |||
1053 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"bagAdd(eventBag)"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +8))), ((jvmtiError)(JVMTI_ERROR_MAX+64+8)), ("bagAdd(eventBag)" ==((void*)0)?"":"bagAdd(eventBag)"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 1053); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+8)), "bagAdd(eventBag)"); }; | |||
1054 | } | |||
1055 | ||||
1056 | command->singleKind = COMMAND_SINGLE_EVENT11; | |||
1057 | command->u.eventCommand.suspendPolicy = suspendPolicy; | |||
1058 | command->u.eventCommand.id = id; | |||
1059 | ||||
1060 | /* | |||
1061 | * Copy the event into the command so that it can be used | |||
1062 | * asynchronously by the event helper thread. | |||
1063 | */ | |||
1064 | (void)memcpy(&command->u.eventCommand.info, evinfo, sizeof(*evinfo)); | |||
1065 | saveEventInfoRefs(env, &command->u.eventCommand.info); | |||
1066 | } | |||
1067 | ||||
1068 | void | |||
1069 | eventHelper_recordClassUnload(jint id, char *signature, struct bag *eventBag) | |||
1070 | { | |||
1071 | CommandSingle *command = bagAdd(eventBag); | |||
1072 | if (command == NULL((void*)0)) { | |||
1073 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"bagAdd(eventBag)"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +8))), ((jvmtiError)(JVMTI_ERROR_MAX+64+8)), ("bagAdd(eventBag)" ==((void*)0)?"":"bagAdd(eventBag)"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 1073); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+8)), "bagAdd(eventBag)"); }; | |||
1074 | } | |||
1075 | command->singleKind = COMMAND_SINGLE_UNLOAD12; | |||
1076 | command->u.unloadCommand.id = id; | |||
1077 | command->u.unloadCommand.classSignature = signature; | |||
1078 | } | |||
1079 | ||||
1080 | void | |||
1081 | eventHelper_recordFrameEvent(jint id, jbyte suspendPolicy, EventIndex ei, | |||
1082 | jthread thread, jclass clazz, | |||
1083 | jmethodID method, jlocation location, | |||
1084 | int needReturnValue, | |||
1085 | jvalue returnValue, | |||
1086 | struct bag *eventBag) | |||
1087 | { | |||
1088 | JNIEnv *env = getEnv(); | |||
1089 | FrameEventCommandSingle *frameCommand; | |||
1090 | CommandSingle *command = bagAdd(eventBag); | |||
1091 | jvmtiError err = JVMTI_ERROR_NONE; | |||
1092 | if (command == NULL((void*)0)) { | |||
1093 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"bagAdd(eventBag)"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +8))), ((jvmtiError)(JVMTI_ERROR_MAX+64+8)), ("bagAdd(eventBag)" ==((void*)0)?"":"bagAdd(eventBag)"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 1093); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+8)), "bagAdd(eventBag)"); }; | |||
1094 | } | |||
1095 | ||||
1096 | command->singleKind = COMMAND_SINGLE_FRAME_EVENT13; | |||
1097 | frameCommand = &command->u.frameEventCommand; | |||
1098 | frameCommand->suspendPolicy = suspendPolicy; | |||
1099 | frameCommand->id = id; | |||
1100 | frameCommand->ei = ei; | |||
1101 | saveGlobalRef(env, thread, &(frameCommand->thread)); | |||
1102 | saveGlobalRef(env, clazz, &(frameCommand->clazz)); | |||
1103 | frameCommand->method = method; | |||
1104 | frameCommand->location = location; | |||
1105 | if (needReturnValue) { | |||
1106 | err = methodReturnType(method, &frameCommand->typeKey); | |||
1107 | JDI_ASSERT(err == JVMTI_ERROR_NONE)do { if (gdata && gdata->assertOn && !(err == JVMTI_ERROR_NONE)) { jdiAssertionFailed("/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 1107, "err == JVMTI_ERROR_NONE"); } } while (0); | |||
1108 | ||||
1109 | /* | |||
1110 | * V or B C D F I J S Z L <classname> ; [ ComponentType | |||
1111 | */ | |||
1112 | if (isReferenceTag(frameCommand->typeKey) && | |||
1113 | returnValue.l != NULL((void*)0)) { | |||
1114 | saveGlobalRef(env, returnValue.l, &(frameCommand->returnValue.l)); | |||
1115 | } else { | |||
1116 | frameCommand->returnValue = returnValue; | |||
1117 | } | |||
1118 | } else { | |||
1119 | /* This is not a JDWP METHOD_EXIT_WITH_RETURN_VALUE request, | |||
1120 | * so signal this by setting typeKey = 0 which is not | |||
1121 | * a legal typekey. | |||
1122 | */ | |||
1123 | frameCommand->typeKey = 0; | |||
1124 | } | |||
1125 | } | |||
1126 | ||||
1127 | void | |||
1128 | eventHelper_reportInvokeDone(jbyte sessionID, jthread thread) | |||
1129 | { | |||
1130 | JNIEnv *env = getEnv(); | |||
1131 | HelperCommand *command = jvmtiAllocate(sizeof(*command)); | |||
1132 | if (command == NULL((void*)0)) { | |||
1133 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"HelperCommand"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +8))), ((jvmtiError)(JVMTI_ERROR_MAX+64+8)), ("HelperCommand" ==((void*)0)?"":"HelperCommand"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 1133); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+8)), "HelperCommand"); }; | |||
1134 | } | |||
1135 | (void)memset(command, 0, sizeof(*command)); | |||
1136 | command->commandKind = COMMAND_REPORT_INVOKE_DONE2; | |||
1137 | command->sessionID = sessionID; | |||
1138 | saveGlobalRef(env, thread, &(command->u.reportInvokeDone.thread)); | |||
1139 | enqueueCommand(command, JNI_TRUE1, JNI_FALSE0); | |||
1140 | } | |||
1141 | ||||
1142 | /* | |||
1143 | * This, currently, cannot go through the normal event handling code | |||
1144 | * because the JVMTI event does not contain a thread. | |||
1145 | */ | |||
1146 | void | |||
1147 | eventHelper_reportVMInit(JNIEnv *env, jbyte sessionID, jthread thread, jbyte suspendPolicy) | |||
1148 | { | |||
1149 | HelperCommand *command = jvmtiAllocate(sizeof(*command)); | |||
| ||||
1150 | if (command == NULL((void*)0)) { | |||
1151 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"HelperCommmand"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +8))), ((jvmtiError)(JVMTI_ERROR_MAX+64+8)), ("HelperCommmand" ==((void*)0)?"":"HelperCommmand"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 1151); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+8)), "HelperCommmand"); }; | |||
1152 | } | |||
1153 | (void)memset(command, 0, sizeof(*command)); | |||
| ||||
1154 | command->commandKind = COMMAND_REPORT_VM_INIT3; | |||
1155 | command->sessionID = sessionID; | |||
1156 | saveGlobalRef(env, thread, &(command->u.reportVMInit.thread)); | |||
1157 | command->u.reportVMInit.suspendPolicy = suspendPolicy; | |||
1158 | enqueueCommand(command, JNI_TRUE1, JNI_FALSE0); | |||
1159 | } | |||
1160 | ||||
1161 | void | |||
1162 | eventHelper_suspendThread(jbyte sessionID, jthread thread) | |||
1163 | { | |||
1164 | JNIEnv *env = getEnv(); | |||
1165 | HelperCommand *command = jvmtiAllocate(sizeof(*command)); | |||
1166 | if (command == NULL((void*)0)) { | |||
1167 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"HelperCommmand"){ print_message(stderr, "JDWP exit error ", "\n", "%s(%d): %s [%s:%d]" , jvmtiErrorText((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX+64 +8))), ((jvmtiError)(JVMTI_ERROR_MAX+64+8)), ("HelperCommmand" ==((void*)0)?"":"HelperCommmand"), "/home/daniel/Projects/java/jdk/src/jdk.jdwp.agent/share/native/libjdwp/eventHelper.c" , 1167); debugInit_exit((jvmtiError)((jvmtiError)(JVMTI_ERROR_MAX +64+8)), "HelperCommmand"); }; | |||
1168 | } | |||
1169 | (void)memset(command, 0, sizeof(*command)); | |||
1170 | command->commandKind = COMMAND_SUSPEND_THREAD4; | |||
1171 | command->sessionID = sessionID; | |||
1172 | saveGlobalRef(env, thread, &(command->u.suspendThread.thread)); | |||
1173 | enqueueCommand(command, JNI_TRUE1, JNI_FALSE0); | |||
1174 | } |