/* Return the combined suspend policy for the event set */ static jboolean enumForCombinedSuspendPolicy(void *cv, void *arg) { CommandSingle *command = cv; jbyte thisPolicy; jbyte *policy = arg; switch(command->singleKind) { case COMMAND_SINGLE_EVENT: thisPolicy = command->u.eventCommand.suspendPolicy; break; case COMMAND_SINGLE_FRAME_EVENT: thisPolicy = command->u.frameEventCommand.suspendPolicy; break; default: thisPolicy = JDWP_SUSPEND_POLICY(NONE); } /* Expand running policy value if this policy demands it */ if (*policy == JDWP_SUSPEND_POLICY(NONE)) { *policy = thisPolicy; } else if (*policy == JDWP_SUSPEND_POLICY(EVENT_THREAD)) { *policy = (thisPolicy == JDWP_SUSPEND_POLICY(ALL))? thisPolicy : *policy; } /* Short circuit if we reached maximal suspend policy */ if (*policy == JDWP_SUSPEND_POLICY(ALL)) { return JNI_FALSE; } else { return JNI_TRUE; } }
static void handleReportVMInitCommand(JNIEnv* env, ReportVMInitCommand *command) { PacketOutputStream out; if (command->suspendPolicy == JDWP_SUSPEND_POLICY(ALL)) { (void)threadControl_suspendAll(); } else if (command->suspendPolicy == JDWP_SUSPEND_POLICY(EVENT_THREAD)) { (void)threadControl_suspendThread(command->thread, JNI_FALSE); } outStream_initCommand(&out, uniqueID(), 0x0, JDWP_COMMAND_SET(Event), JDWP_COMMAND(Event, Composite)); (void)outStream_writeByte(&out, command->suspendPolicy); (void)outStream_writeInt(&out, 1); /* Always one component */ (void)outStream_writeByte(&out, JDWP_EVENT(VM_INIT)); (void)outStream_writeInt(&out, 0); /* Not in response to an event req. */ (void)outStream_writeObjectRef(env, &out, command->thread); outStream_sendCommand(&out); outStream_destroy(&out); /* Why aren't we tossing this: tossGlobalRef(env, &(command->thread)); */ }
jbyte eventHelper_reportEvents(jbyte sessionID, struct bag *eventBag) { int size = bagSize(eventBag); jbyte suspendPolicy = JDWP_SUSPEND_POLICY(NONE); jboolean reportingVMDeath = JNI_FALSE; jboolean wait; int command_size; HelperCommand *command; ReportEventCompositeCommand *recc; struct singleTracker tracker; if (size == 0) { return suspendPolicy; } (void)bagEnumerateOver(eventBag, enumForCombinedSuspendPolicy, &suspendPolicy); (void)bagEnumerateOver(eventBag, enumForVMDeath, &reportingVMDeath); /*LINTED*/ command_size = (int)(sizeof(HelperCommand) + sizeof(CommandSingle)*(size-1)); command = jvmtiAllocate(command_size); (void)memset(command, 0, command_size); command->commandKind = COMMAND_REPORT_EVENT_COMPOSITE; command->sessionID = sessionID; recc = &command->u.reportEventComposite; recc->suspendPolicy = suspendPolicy; recc->eventCount = size; tracker.recc = recc; tracker.index = 0; (void)bagEnumerateOver(eventBag, enumForCopyingSingles, &tracker); /* * We must wait if this thread (the event thread) is to be * suspended or if the VM is about to die. (Waiting in the latter * case ensures that we get the event out before the process dies.) */ wait = (jboolean)((suspendPolicy != JDWP_SUSPEND_POLICY(NONE)) || reportingVMDeath); enqueueCommand(command, wait, reportingVMDeath); return suspendPolicy; }
static void suspendWithInvokeEnabled(jbyte policy, jthread thread) { invoker_enableInvokeRequests(thread); if (policy == JDWP_SUSPEND_POLICY(ALL)) { (void)threadControl_suspendAll(); } else { (void)threadControl_suspendThread(thread, JNI_FALSE); } }
/* * If the command that we are about to execute has a suspend-all * policy, then prepare for either ThreadReferenceImpl.c: resume() * or VirtualMachineImpl.c: resume() to be called. */ static jboolean needBlockCommandLoop(HelperCommand *cmd) { if (cmd->commandKind == COMMAND_REPORT_EVENT_COMPOSITE && cmd->u.reportEventComposite.suspendPolicy == JDWP_SUSPEND_POLICY(ALL)) { debugMonitorEnter(blockCommandLoopLock); blockCommandLoop = JNI_TRUE; debugMonitorExit(blockCommandLoopLock); return JNI_TRUE; } return JNI_FALSE; }
static void handleClassPrepare(JNIEnv *env, EventInfo *evinfo, HandlerNode *node, struct bag *eventBag) { jthread thread = evinfo->thread; /* We try hard to avoid class loads/prepares in debugger * threads, but it is still possible for them to happen (most * likely for exceptions that are thrown within JNI * methods). If such an event occurs, we must report it, but * we cannot suspend the debugger thread. * * 1) We report the thread as NULL because we don't want the * application to get hold of a debugger thread object. * 2) We try to do the right thing wrt to suspending * threads without suspending debugger threads. If the * requested suspend policy is NONE, there's no problem. If * the requested policy is ALL, we can just suspend all * application threads without producing any surprising * results by leaving the debugger thread running. However, * if the requested policy is EVENT_THREAD, we are forced * to do something different than requested. The most * useful behavior is to suspend all application threads * (just as if the policy was ALL). This allows the * application to operate on the class before it gets into * circulation and so it is preferable to the other * alternative of suspending no threads. */ if (threadControl_isDebugThread(thread)) { evinfo->thread = NULL; if (node->suspendPolicy == JDWP_SUSPEND_POLICY(EVENT_THREAD)) { node->suspendPolicy = JDWP_SUSPEND_POLICY(ALL); } } eventHelper_recordEvent(evinfo, node->handlerID, node->suspendPolicy, eventBag); }
/* * Initialize debugger back end modules */ static void initialize(JNIEnv *env, jthread thread, EventIndex triggering_ei) { jvmtiError error; EnumerateArg arg; jbyte suspendPolicy; LOG_MISC(("Begin initialize()")); currentSessionID = 0; initComplete = JNI_FALSE; if ( gdata->vmDead ) { EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead at initialize() time"); } /* Turn off the initial JVMTI event notifications */ error = set_event_notification(JVMTI_DISABLE, EI_EXCEPTION); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "unable to disable JVMTI event notification"); } error = set_event_notification(JVMTI_DISABLE, EI_VM_INIT); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "unable to disable JVMTI event notification"); } error = set_event_notification(JVMTI_DISABLE, EI_VM_DEATH); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "unable to disable JVMTI event notification"); } /* Remove initial event callbacks */ (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks)); error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventCallbacks) (gdata->jvmti, &(gdata->callbacks), sizeof(gdata->callbacks)); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error, "unable to clear JVMTI callbacks"); } commonRef_initialize(); util_initialize(env); threadControl_initialize(); stepControl_initialize(); invoker_initialize(); debugDispatch_initialize(); classTrack_initialize(env); debugLoop_initialize(); initMonitor = debugMonitorCreate("JDWP Initialization Monitor"); /* * Initialize transports */ arg.isServer = isServer; arg.error = JDWP_ERROR(NONE); arg.startCount = 0; transport_initialize(); (void)bagEnumerateOver(transports, startTransport, &arg); /* * Exit with an error only if * 1) none of the transports was successfully started, and * 2) the application has not yet started running */ if ((arg.error != JDWP_ERROR(NONE)) && (arg.startCount == 0) && initOnStartup) { EXIT_ERROR(map2jvmtiError(arg.error), "No transports initialized"); } eventHandler_initialize(currentSessionID); signalInitComplete(); transport_waitForConnection(); suspendPolicy = suspendOnInit ? JDWP_SUSPEND_POLICY(ALL) : JDWP_SUSPEND_POLICY(NONE); if (triggering_ei == EI_VM_INIT) { LOG_MISC(("triggering_ei == EI_VM_INIT")); eventHelper_reportVMInit(env, currentSessionID, thread, suspendPolicy); } else { /* * TO DO: Kludgy way of getting the triggering event to the * just-attached debugger. It would be nice to make this a little * cleaner. There is also a race condition where other events * can get in the queue (from other not-yet-suspended threads) * before this one does. (Also need to handle allocation error below?) */ EventInfo info; struct bag *initEventBag; LOG_MISC(("triggering_ei != EI_VM_INIT")); initEventBag = eventHelper_createEventBag(); (void)memset(&info,0,sizeof(info)); info.ei = triggering_ei; eventHelper_recordEvent(&info, 0, suspendPolicy, initEventBag); (void)eventHelper_reportEvents(currentSessionID, initEventBag); bagDestroyBag(initEventBag); } if ( gdata->vmDead ) { EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead before initialize() completes"); } LOG_MISC(("End initialize()")); }
static void handleReportEventCompositeCommand(JNIEnv *env, ReportEventCompositeCommand *recc) { PacketOutputStream out; jint count = recc->eventCount; jint i; if (recc->suspendPolicy != JDWP_SUSPEND_POLICY(NONE)) { /* must determine thread to interrupt before writing */ /* since writing destroys it */ jthread thread = NULL; for (i = 0; i < count; i++) { CommandSingle *single = &(recc->singleCommand[i]); switch (single->singleKind) { case COMMAND_SINGLE_EVENT: thread = single->u.eventCommand.info.thread; break; case COMMAND_SINGLE_FRAME_EVENT: thread = single->u.frameEventCommand.thread; break; } if (thread != NULL) { break; } } if (thread == NULL) { (void)threadControl_suspendAll(); } else { suspendWithInvokeEnabled(recc->suspendPolicy, thread); } } outStream_initCommand(&out, uniqueID(), 0x0, JDWP_COMMAND_SET(Event), JDWP_COMMAND(Event, Composite)); (void)outStream_writeByte(&out, recc->suspendPolicy); (void)outStream_writeInt(&out, count); for (i = 0; i < count; i++) { CommandSingle *single = &(recc->singleCommand[i]); switch (single->singleKind) { case COMMAND_SINGLE_EVENT: handleEventCommandSingle(env, &out, &single->u.eventCommand); break; case COMMAND_SINGLE_UNLOAD: handleUnloadCommandSingle(env, &out, &single->u.unloadCommand); break; case COMMAND_SINGLE_FRAME_EVENT: handleFrameEventCommandSingle(env, &out, &single->u.frameEventCommand); break; } } outStream_sendCommand(&out); outStream_destroy(&out); }