< prev index next >

src/jdk.jdwp.agent/share/native/libjdwp/eventHandler.c

Print this page

        

*** 1,7 **** /* ! * Copyright (c) 1998, 2017, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this --- 1,7 ---- /* ! * Copyright (c) 1998, 2019, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. Oracle designates this
*** 266,276 **** return JNI_FALSE; } /* BREAKPOINT, METHOD_ENTRY and SINGLE_STEP events are covered by * the co-location of events policy. Of these three co-located ! * events, METHOD_ENTRY is always reported first and BREAKPOINT * is always reported last. Here are the possible combinations and * their order: * * (p1) METHOD_ENTRY, BREAKPOINT (existing) * (p2) METHOD_ENTRY, BREAKPOINT (new) --- 266,276 ---- return JNI_FALSE; } /* BREAKPOINT, METHOD_ENTRY and SINGLE_STEP events are covered by * the co-location of events policy. Of these three co-located ! * events, METHOD_ENTRY is always reported first and BREAKPOINT * is always reported last. Here are the possible combinations and * their order: * * (p1) METHOD_ENTRY, BREAKPOINT (existing) * (p2) METHOD_ENTRY, BREAKPOINT (new)
*** 531,555 **** } /* Garbage Collection Happened */ static unsigned int garbageCollected = 0; ! /* The JVMTI generic event callback. Each event is passed to a sequence of * handlers in a chain until the chain ends or one handler * consumes the event. */ static void ! event_callback(JNIEnv *env, EventInfo *evinfo) { struct bag *eventBag; jbyte eventSessionID = currentSessionID; /* session could change */ jthrowable currentException; jthread thread; ! LOG_MISC(("event_callback(): ei=%s", eventText(evinfo->ei))); log_debugee_location("event_callback()", evinfo->thread, evinfo->method, evinfo->location); /* We want to preserve any current exception that might get * wiped out during event handling (e.g. JNI calls). We have * to rely on space for the local reference on the current * frame because doing a PushLocalFrame here might itself * generate an exception. --- 531,710 ---- } /* Garbage Collection Happened */ static unsigned int garbageCollected = 0; ! /* ! * Run the event through each HandlerNode's filter, and if it passes, call the HandlerNode's ! * HandlerFunction for the event, and then report all accumulated events to the debugger. ! */ ! static void ! filterAndHandleEvent(JNIEnv *env, EventInfo *evinfo, EventIndex ei, ! struct bag *eventBag, jbyte eventSessionID) ! { ! debugMonitorEnter(handlerLock); ! { ! HandlerNode *node; ! char *classname; ! ! /* We must keep track of all classes prepared to know what's unloaded */ ! if (evinfo->ei == EI_CLASS_PREPARE) { ! classTrack_addPreparedClass(env, evinfo->clazz); ! } ! ! node = getHandlerChain(ei)->first; ! classname = getClassname(evinfo->clazz); ! ! /* Filter the event over each handler node. */ ! while (node != NULL) { ! /* Save next so handlers can remove themselves. */ ! HandlerNode *next = NEXT(node); ! jboolean shouldDelete; ! ! /* passesFilter() will set evinfo->matchesFiber true if appropriate. */ ! if (eventFilterRestricted_passesFilter(env, classname, ! evinfo, node, ! &shouldDelete, JNI_FALSE /* filterOnly */)) { ! HandlerFunction func = HANDLER_FUNCTION(node); ! if (func == NULL) { ! EXIT_ERROR(AGENT_ERROR_INTERNAL,"handler function NULL"); ! } ! /* Handle the event by calling the event handler. */ ! (*func)(env, evinfo, node, eventBag); ! } ! if (shouldDelete) { ! /* We can safely free the node now that we are done using it. */ ! (void)freeHandler(node); ! } ! node = next; ! } ! jvmtiDeallocate(classname); ! } ! debugMonitorExit(handlerLock); ! ! /* ! * The events destined for the debugger were accumulated in eventBag. Report all these events. ! */ ! if (eventBag != NULL) { ! reportEvents(env, eventSessionID, evinfo->thread, evinfo->ei, ! evinfo->clazz, evinfo->method, evinfo->location, eventBag); ! } ! } ! ! /* ! * Called when we are not notifying the debugging of all fibers, and we are seeing an event ! * on a fiber that we have not notified the debugger about yet. If the event passes the event ! * filters, then we will notify the debugger with a THREAD_START and add it to our list of ! * fibers. Otherwise we ignore it. ! */ ! static void ! filterAndAddFiber(JNIEnv *env, EventInfo *evinfo, EventIndex ei, jbyte eventSessionID, ! jthread thread, jthread fiber) ! { ! jboolean needToAddFiber = JNI_FALSE; /* Assume we won't need to add the fiber. */; ! ! /* ! * Although we have received a JVMTI event for a fiber that we have not added yet, ! * we only need to add it if we are going to pass the fiber on to the debugger. This ! * might not end up happening due to event filtering. For example, we got a breakpoint ! * on a carrier thread running a fiber, but the breakpoint HandlerNode specifies that ! * event must be on a certain thread, so the event may end up being dropped. Therefore ! * we need to do a dry run with the filtering to see if we really need to add this fiber. ! */ ! { ! HandlerNode *node = getHandlerChain(ei)->first; ! char *classname = getClassname(evinfo->clazz); ! ! debugMonitorEnter(handlerLock); ! ! /* Filter the event over each handler node. */ ! while (node != NULL) { ! /* Save next so handlers can remove themselves */ ! HandlerNode *next = NEXT(node); ! jboolean shouldDelete; ! ! /* passesFilter() will set evinfo->matchesFiber true if appropriate. */ ! if (eventFilterRestricted_passesFilter(env, classname, ! evinfo, node, ! &shouldDelete, JNI_TRUE /* filterOnly */)) { ! if (evinfo->matchesFiber) { ! /* If we match even one filter and it matches on the fiber, then we need ! * to add the fiber. */ ! needToAddFiber = JNI_TRUE; ! break; ! } ! } ! JDI_ASSERT(!shouldDelete); ! node = next; ! } ! ! debugMonitorExit(handlerLock); ! jvmtiDeallocate(classname); ! } ! ! if (needToAddFiber) { ! /* Make sure this fiber gets added to the fibers list. */ ! threadControl_addFiber(fiber); ! ! /* ! * When the FIBER_SCHEDULED event arrived for this fiber, we ignored it since we don't ! * want to notify the debugger about fibers until there is a non-fiber event that ! * arrives on it (like a breakpoint). Now that this has happened, we need to send ! * a FIBER_SCHEDULED event (which will be converted into a THREAD_START event) so ! * the debugger will know about the fiber. Otherwise it will be unhappy when it gets ! * an event for a fiber that it never got a THREAD_START event for. ! */ ! EventInfo info; ! struct bag *eventBag = eventHelper_createEventBag(); ! ! JDI_ASSERT(evinfo->fiber != NULL); ! (void)memset(&info,0,sizeof(info)); ! info.ei = EI_FIBER_SCHEDULED; ! info.thread = thread; ! info.fiber = fiber; ! info.matchesFiber = JNI_TRUE; ! ! /* Note: filterAndHandleEvent() expects EI_THREAD_START instead of EI_FIBER_SCHEDULED. */ ! filterAndHandleEvent(env, &info, EI_THREAD_START, eventBag, eventSessionID); ! JDI_ASSERT(bagSize(eventBag) == 0); ! bagDestroyBag(eventBag); ! } ! } ! ! /* ! * The JVMTI generic event callback. Each event is passed to a sequence of * handlers in a chain until the chain ends or one handler * consumes the event. */ static void ! event_callback_helper(JNIEnv *env, EventInfo *evinfo) { struct bag *eventBag; jbyte eventSessionID = currentSessionID; /* session could change */ jthrowable currentException; jthread thread; + EventIndex ei = evinfo->ei; ! LOG_MISC(("event_callback(): ei=%s", eventText(ei))); log_debugee_location("event_callback()", evinfo->thread, evinfo->method, evinfo->location); + if (evinfo->thread != NULL) { + /* fiber fixme: ignore all events on the fiber helper thread. Remove this when + * the need for helper threads goes away. + */ + jclass threadClass = JNI_FUNC_PTR(env, GetObjectClass)(env, evinfo->thread); + if (JNI_FUNC_PTR(env, IsSameObject)(env, threadClass, gdata->innocuousThreadClass)) { + return; + } + } + + /* fiber fixme: little hack to ignore THREAD_START events while we are creating a fiber + * helper thread. The need for this will eventually go away. */ + if (gdata->ignoreEvents) { + return; + } + /* We want to preserve any current exception that might get * wiped out during event handling (e.g. JNI calls). We have * to rely on space for the local reference on the current * frame because doing a PushLocalFrame here might itself * generate an exception.
*** 592,610 **** } } thread = evinfo->thread; if (thread != NULL) { /* * Record the fact that we're entering an event * handler so that thread operations (status, interrupt, * stop) can be done correctly and so that thread * resources can be allocated. This must be done before * grabbing any locks. */ ! eventBag = threadControl_onEventHandlerEntry(eventSessionID, ! evinfo->ei, thread, currentException); if ( eventBag == NULL ) { jboolean invoking; do { /* The event has been 'handled' and this * thread is about to continue, but it may --- 747,775 ---- } } thread = evinfo->thread; if (thread != NULL) { + if (gdata->fibersSupported) { + /* + * Get the event fiber if one is running on this thread and has not already + * been set, which is the case for fiber related JVMTI events. + */ + if (evinfo->fiber == NULL) { + evinfo->fiber = getThreadFiber(thread); + } + } + /* * Record the fact that we're entering an event * handler so that thread operations (status, interrupt, * stop) can be done correctly and so that thread * resources can be allocated. This must be done before * grabbing any locks. */ ! eventBag = threadControl_onEventHandlerEntry( ! eventSessionID, evinfo, currentException); if ( eventBag == NULL ) { jboolean invoking; do { /* The event has been 'handled' and this * thread is about to continue, but it may
*** 630,684 **** */ eventBag = NULL; /* to shut up lint */ } } ! debugMonitorEnter(handlerLock); ! { ! HandlerNode *node; ! char *classname; ! ! /* We must keep track of all classes prepared to know what's unloaded */ ! if (evinfo->ei == EI_CLASS_PREPARE) { ! classTrack_addPreparedClass(env, evinfo->clazz); ! } ! ! node = getHandlerChain(evinfo->ei)->first; ! classname = getClassname(evinfo->clazz); ! ! while (node != NULL) { ! /* save next so handlers can remove themselves */ ! HandlerNode *next = NEXT(node); ! jboolean shouldDelete; ! ! if (eventFilterRestricted_passesFilter(env, classname, ! evinfo, node, ! &shouldDelete)) { ! HandlerFunction func; ! ! func = HANDLER_FUNCTION(node); ! if ( func == NULL ) { ! EXIT_ERROR(AGENT_ERROR_INTERNAL,"handler function NULL"); ! } ! (*func)(env, evinfo, node, eventBag); ! } ! if (shouldDelete) { ! /* We can safely free the node now that we are done ! * using it. ! */ ! (void)freeHandler(node); } - node = next; } - jvmtiDeallocate(classname); } - debugMonitorExit(handlerLock); ! if (eventBag != NULL) { ! reportEvents(env, eventSessionID, thread, evinfo->ei, ! evinfo->clazz, evinfo->method, evinfo->location, eventBag); ! } /* we are continuing after VMDeathEvent - now we are dead */ if (evinfo->ei == EI_VM_DEATH) { gdata->vmDead = JNI_TRUE; } --- 795,826 ---- */ eventBag = NULL; /* to shut up lint */ } } ! /* We want the fiber scheduled/terminated events to mimic thread start/end events */ ! if (ei == EI_FIBER_SCHEDULED) { ! ei = EI_THREAD_START; ! } ! if (ei == EI_FIBER_TERMINATED) { ! ei = EI_THREAD_END; ! } ! ! if (gdata->fibersSupported) { ! /* Add the fiber if we haven't added it before. */ ! if (evinfo->fiber != NULL && !threadControl_isKnownFiber(evinfo->fiber)) { ! if (gdata->notifyDebuggerOfAllFibers) { ! /* Make sure this fiber gets added to the fibers list. */ ! threadControl_addFiber(evinfo->fiber); ! } else { ! /* Add this fiber if it passes the event filters. */ ! filterAndAddFiber(env, evinfo, ei, eventSessionID, thread, evinfo->fiber); } } } ! filterAndHandleEvent(env, evinfo, ei, eventBag, eventSessionID); /* we are continuing after VMDeathEvent - now we are dead */ if (evinfo->ei == EI_VM_DEATH) { gdata->vmDead = JNI_TRUE; }
*** 708,717 **** --- 850,869 ---- if (thread != NULL) { threadControl_onEventHandlerExit(evinfo->ei, thread, eventBag); } } + static void + event_callback(JNIEnv *env, EventInfo *evinfo) + { + /* fiber fixme: There are a bunch of WITH_LOCAL_REFS that we can remove now that + * we are doing one here. */ + WITH_LOCAL_REFS(env, 64) { + event_callback_helper(env, evinfo); + } END_WITH_LOCAL_REFS(env); + } + /* Returns a local ref to the declaring class for an object. */ static jclass getObjectClass(jobject object) { jclass clazz;
*** 1292,1301 **** --- 1444,1603 ---- debugLoop_sync(); LOG_MISC(("END cbVMDeath")); } + /* Event callback for JVMTI_EVENT_FIBER_SCHEDULED */ + static void JNICALL + cbFiberScheduled(jvmtiEnv *jvmti_env, JNIEnv *env, + jthread thread, jthread fiber) + { + EventInfo info; + + LOG_CB(("cbFiberScheduled: thread=%p", thread)); + /*tty_message("cbFiberScheduled: thread=%p", thread);*/ + JDI_ASSERT(gdata->fibersSupported); + + /* + * Now would be a good time to cache the ThreadGroup for fibers (carrier threads) + * if we haven't already. + * + * fiber fixme: the is kind of a hack. What happens if custom + * scheduler uses carrier threads in multple group? What if as a result of this a + * Fiber can move between thread groups? We should have a dedicated + * thread group for all fibers, or allow a fiber to set its ThreadGroup. + */ + if (gdata->fiberThreadGroup == NULL) { + jvmtiThreadInfo info; + jvmtiError error; + + (void)memset(&info, 0, sizeof(info)); + error = JVMTI_FUNC_PTR(gdata->jvmti,GetThreadInfo) + (gdata->jvmti, thread, &info); + + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error, "could not get fiber ThreadGroup"); + } else { + debugMonitorEnter(callbackBlock); /* fiber fixme: any monitor will do, but there probably is a better choice. */ + /* Make sure there wasn't a race before setting. saveGlobalRef() will assert if the + * target is not NULL. */ + if (gdata->fiberThreadGroup == NULL) { + saveGlobalRef(env, info.thread_group, &gdata->fiberThreadGroup); + } + debugMonitorExit(callbackBlock); + } + } + + /* Ignore FIBER_SCHEDULED events unless we are notifying the debugger of all fibers. */ + if (!gdata->notifyDebuggerOfAllFibers) { + return; + } + + BEGIN_CALLBACK() { + (void)memset(&info,0,sizeof(info)); + info.ei = EI_FIBER_SCHEDULED; + info.thread = thread; + info.fiber = fiber; + event_callback(env, &info); + } END_CALLBACK(); + + LOG_MISC(("END cbFiberScheduled")); + } + + /* Event callback for JVMTI_EVENT_FIBER_TERMINATED */ + static void JNICALL + cbFiberTerminated(jvmtiEnv *jvmti_env, JNIEnv *env, + jthread thread, jthread fiber) + { + EventInfo info; + + LOG_CB(("cbFiberTerminated: thread=%p", thread)); + /*tty_message("cbFiberTerminated: thread=%p", thread);*/ + JDI_ASSERT(gdata->fibersSupported); + + if (!gdata->notifyDebuggerOfAllFibers && !threadControl_isKnownFiber(fiber)) { + /* This is not a fiber we are tracking, so don't deliver a FIBER_TERMINATED event for it. */ + return; + } + + BEGIN_CALLBACK() { + (void)memset(&info,0,sizeof(info)); + info.ei = EI_FIBER_TERMINATED; + info.thread = thread; + info.fiber = fiber; + event_callback(env, &info); + } END_CALLBACK(); + + LOG_MISC(("END cbFiberTerminated")); + } + + /* Event callback for JVMTI_EVENT_FIBER_MOUNT */ + static void JNICALL + cbFiberMount(jvmtiEnv *jvmti_env, JNIEnv *env, + jthread thread, jthread fiber) + { + LOG_CB(("cbFiberMount: thread=%p", thread)); + /*tty_message("cbFiberMount: thread=%p", thread);*/ + JDI_ASSERT(gdata->fibersSupported); + + /* Ignore FIBER_MOUNT events unless we are doing fiber debugging. */ + if (!gdata->fibersSupported) { + return; + } + + threadControl_mountFiber(fiber, thread, currentSessionID); + + LOG_MISC(("END cbFiberMount")); + } + + /* Event callback for JVMTI_EVENT_FIBER_UNMOUNT */ + static void JNICALL + cbFiberUnmount(jvmtiEnv *jvmti_env, JNIEnv *env, + jthread thread, jthread fiber) + { + LOG_CB(("cbFiberUnmount: thread=%p", thread)); + /*tty_message("cbFiberUnmount: thread=%p", thread);*/ + JDI_ASSERT(gdata->fibersSupported); + + /* Ignore FIBER_UNMOUNT events unless we are doing fiber debugging. */ + if (!gdata->fibersSupported) { + return; + } + + threadControl_unmountFiber(fiber, thread); + + LOG_MISC(("END cbFiberUnmount")); + } + + /* Event callback for JVMTI_EVENT_CONTINUATION_RUN */ + static void JNICALL + cbContinuationRun(jvmtiEnv *jvmti_env, JNIEnv *env, + jthread thread, jint continuation_frame_count) + { + LOG_CB(("cbContinuationRun: thread=%p", thread)); + //tty_message("cbContinuationRun: thread=%p continuation_frame_count=%d", thread, continuation_frame_count); + JDI_ASSERT(gdata->fibersSupported); + + threadControl_continuationRun(thread, continuation_frame_count); + + LOG_MISC(("END cbContinuationRun")); + } + + /* Event callback for JVMTI_EVENT_CONTINUATION_YIELD */ + static void JNICALL + cbContinuationYield(jvmtiEnv *jvmti_env, JNIEnv *env, + jthread thread, jint continuation_frame_count) + { + LOG_CB(("cbContinuationYield: thread=%p", thread)); + //tty_message("cbContinuationYield: thread=%p continuation_frame_count=%d", thread, continuation_frame_count); + JDI_ASSERT(gdata->fibersSupported); + + threadControl_continuationYield(thread, continuation_frame_count); + + LOG_MISC(("END cbContinuationYield")); + } + /** * Delete this handler (do not delete permanent handlers): * Deinsert handler from active list, * make it inactive, and free it's memory * Assumes handlerLock held.
*** 1480,1489 **** --- 1782,1824 ---- error = threadControl_setEventMode(JVMTI_ENABLE, EI_GC_FINISH, NULL); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error,"Can't enable garbage collection finish events"); } + /* Only enable fiber events if fiber support is enabled. */ + if (gdata->fibersSupported) { + error = threadControl_setEventMode(JVMTI_ENABLE, + EI_FIBER_SCHEDULED, NULL); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error,"Can't enable fiber scheduled events"); + } + error = threadControl_setEventMode(JVMTI_ENABLE, + EI_FIBER_TERMINATED, NULL); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error,"Can't enable fiber terminated events"); + } + error = threadControl_setEventMode(JVMTI_ENABLE, + EI_FIBER_MOUNT, NULL); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error,"Can't enable fiber mount events"); + } + error = threadControl_setEventMode(JVMTI_ENABLE, + EI_FIBER_UNMOUNT, NULL); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error,"Can't enable fiber unmount events"); + } + error = threadControl_setEventMode(JVMTI_ENABLE, + EI_CONTINUATION_RUN, NULL); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error,"Can't enable continuation run events"); + } + error = threadControl_setEventMode(JVMTI_ENABLE, + EI_CONTINUATION_YIELD, NULL); + if (error != JVMTI_ERROR_NONE) { + EXIT_ERROR(error,"Can't enable continuation yield events"); + } + } (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks)); /* Event callback for JVMTI_EVENT_SINGLE_STEP */ gdata->callbacks.SingleStep = &cbSingleStep; /* Event callback for JVMTI_EVENT_BREAKPOINT */
*** 1522,1531 **** --- 1857,1878 ---- gdata->callbacks.VMInit = &cbVMInit; /* Event callback for JVMTI_EVENT_VM_DEATH */ gdata->callbacks.VMDeath = &cbVMDeath; /* Event callback for JVMTI_EVENT_GARBAGE_COLLECTION_FINISH */ gdata->callbacks.GarbageCollectionFinish = &cbGarbageCollectionFinish; + /* Event callback for JVMTI_EVENT_FIBER_SCHEDULED */ + gdata->callbacks.FiberScheduled = &cbFiberScheduled; + /* Event callback for JVMTI_EVENT_FIBER_TERMINATED */ + gdata->callbacks.FiberTerminated = &cbFiberTerminated; + /* Event callback for JVMTI_EVENT_FIBER_MOUNT */ + gdata->callbacks.FiberMount = &cbFiberMount; + /* Event callback for JVMTI_EVENT_FIBER_UNMOUNT */ + gdata->callbacks.FiberUnmount = &cbFiberUnmount; + /* Event callback for JVMTI_EVENT_CONTINUATION_RUN */ + gdata->callbacks.ContinuationRun = &cbContinuationRun; + /* Event callback for JVMTI_EVENT_CONTINUATION_YIELD */ + gdata->callbacks.ContinuationYield = &cbContinuationYield; error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventCallbacks) (gdata->jvmti, &(gdata->callbacks), sizeof(gdata->callbacks)); if (error != JVMTI_ERROR_NONE) { EXIT_ERROR(error,"Can't set event callbacks");
*** 1707,1711 **** --- 2054,2098 ---- { return installHandler(node, standardHandlers_defaultHandler(node->ei), JNI_TRUE); } + + /***** debugging *****/ + + void + eventHandler_dumpAllHandlers(jboolean dumpPermanent) + { + int ei; + for (ei = EI_min; ei <= EI_max; ++ei) { + eventHandler_dumpHandlers(ei, dumpPermanent); + } + } + + void + eventHandler_dumpHandlers(EventIndex ei, jboolean dumpPermanent) + { + HandlerNode *nextNode; + nextNode = getHandlerChain(ei)->first; + if (nextNode != NULL) { + tty_message("\nHandlers for %s(%d)", eventIndex2EventName(ei), ei); + while (nextNode != NULL) { + HandlerNode *node = nextNode; + nextNode = NEXT(node); + + if (node->permanent && !dumpPermanent) { + continue; // ignore permanent handlers + } + + tty_message("node(%p) handlerID(%d) suspendPolicy(%d) permanent(%d)", + node, node->handlerID, node->suspendPolicy, node->permanent); + eventFilter_dumpHandlerFilters(node); + } + } + } + + void + eventHandler_dumpHandler(HandlerNode *node) + { + tty_message("Handler for %s(%d)\n", eventIndex2EventName(node->ei), node->ei); + eventFilter_dumpHandlerFilters(node); + }
< prev index next >