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