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