threadControl.c revision 0
2362N/A * Copyright 1998-2007 Sun Microsystems, Inc. All Rights Reserved. 0N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 0N/A * This code is free software; you can redistribute it and/or modify it 0N/A * under the terms of the GNU General Public License version 2 only, as 2362N/A * published by the Free Software Foundation. Sun designates this 0N/A * particular file as subject to the "Classpath" exception as provided 2362N/A * by Sun in the LICENSE file that accompanied this code. 0N/A * This code is distributed in the hope that it will be useful, but WITHOUT 0N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 0N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 0N/A * version 2 for more details (a copy is included in the LICENSE file that 0N/A * accompanied this code). 0N/A * You should have received a copy of the GNU General Public License version 0N/A * 2 along with this work; if not, write to the Free Software Foundation, 0N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 2362N/A * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, 2362N/A * CA 95054 USA or visit www.sun.com if you need additional information or 0N/A * Collection of info for properly handling co-located events. 0N/A * If the ei field is non-zero, then one of the possible 0N/A * co-located events has been posted and the other fields describe 0N/A * the event's location. 0N/A * The main data structure in threadControl is the ThreadNode. 0N/A * This is a per-thread structure that is allocated on the 0N/A * first event that occurs in a thread. It is freed after the 0N/A * thread's thread end event has completed processing. The 0N/A * structure contains state information on its thread including 0N/A * suspend counts. It also acts as a repository for other 0N/A * per-thread state such as the current method invocation or 0N/A * suspendCount is the number of outstanding suspends 0N/A * from the debugger. suspends from the app itself are 0N/A * not included in this count. * popFrameEventLock is used to notify that the event has been received * popFrameProceedLock is used to assure that the event thread is * re-suspended immediately after the event is acknowledged. * Threads which have issued thread start events and not yet issued thread * end events are maintained in the "runningThreads" list. All other threads known * to this module are kept in the "otherThreads" list. /* Get the state of the thread direct from JVMTI */ /* Set TLS on a specific jthread to the ThreadNode* */ /* Just return, thread hasn't started yet */ /* The jthread object must be valid, so this must be a fatal error */ /* Get TLS on a specific jthread, which is the ThreadNode* */ /* Just return NULL, thread hasn't started yet */ /* The jthread object must be valid, so this must be a fatal error */ /* Search list for nodes that don't have TLS set and match this thread. * It assumed that this logic is never dealing with terminated threads, * since the ThreadEnd events always delete the ThreadNode while the * jthread is still alive. So we can only look at the ThreadNode's that * have never had their TLS set, making the search much faster. * But keep in mind, this kind of search should rarely be needed. * These functions maintain the linked list of currently running threads. * All assume that the threadLock is held before calling. * If list==NULL, search both lists. /* Get thread local storage for quick thread -> node access */ /* In some rare cases we might get NULL, so we check the list manually for * any threads that we could match. /* Here we make another attempt to set TLS, it's ok if this fails */ /* If a list is supplied, only return ones in this list */ /* Remove a ThreadNode from a ThreadList */ /* Add a ThreadNode to a ThreadList */ * Init all flags false, all refs NULL, all counts 0 * Remember if it is a debug thread * If there is a pending suspendAll, all new threads should * be initialized as if they were suspended by the suspendAll, * and the thread will need to be suspended when it starts. /* Set thread local storage for quick thread -> node access. * Some threads may not be in a state that allows setting of TLS, * which is ok, see findThread, it deals with threads without TLS set. /* Clear out TLS on this thread (just a cleanup action) */ /* record single step mode */ EXIT_ERROR(
error,
"cannot process deferred thread event notifications at thread start");
* Anything which might be locked as part of the handling of * a JVMTI event (which means: might be locked by an application * thread) needs to be grabbed here. This allows thread control * code to safely suspend and resume the application threads * while ensuring they don't hold a critical lock. /* Get the java.lang.Thread.resume() method beginning location */ * Hold up any attempt to resume as long as the debugger * has suspended the resumee. * Track the resuming thread by marking it as being within * a resume and by setting up for notification on * a frame pop or exception. We won't allow the debugger * to suspend threads while any thread is within a * call to resume. This (along with the block above) * ensures that when the debugger * suspends a thread it will remain suspended. * As soon as the event hook is in place, we need to initialize * the thread list with already-existing threads. The threadLock * has been held since initialize, so we don't need to worry about * insertions or deletions from the event handlers while we do this * Prevent any event processing until OnHook has been called * This is a tiny bit risky. We have to assume that the * pre-existing threads have been started because we * can't rely on a thread start event for them. The chances * of a problem related to this are pretty slim though, and * there's really no choice because without setting this flag * there is no way to enable stepping and other events on * the threads that already exist (e.g. the finalizer thread). * Mark for resume only if suspend succeeded * If the thread was suspended by another app thread, * do nothing and report no error (we won't resume it later). * Deferred suspends happen when the suspend is attempted on a thread * that is not started. Bookkeeping (suspendCount,etc.) * is handled by the original request, and once the thread actually * starts, an actual suspend is attempted. This function does the * deferred suspend without changing the bookkeeping that is already /* Ignore requests for suspending debugger threads */ * Do the actual suspend only if a subsequent resume hasn't * Attempt to clean up from any error by decrementing the * suspend count. This compensates for the increment that * happens when suspendOnStart is set to true. /* Ignore requests for suspending debugger threads */ * Just increment the suspend count if we are waiting * for a deferred suspend. * This error means that the thread is either a zombie or not yet * started. In either case, we ignore the error. If the thread * started, it will be suspended for real during the processing * of its thread start event. /* never suspended by debugger => don't ever try to resume */ * We successfully "suspended" this thread, but * we never received a THREAD_START event for it. * Since the thread never ran, we can ignore our * failure to resume the thread. * Suspends and resumes add and subtract from a count respectively. * The thread is only suspended when the count goes from 0 to 1 and * resumed only when the count goes from 1 to 0. * These functions suspend and resume application threads * state of threads that were already suspended beforehand. * They must not be called from an application thread because * that thread may be suspended somewhere in the middle of things. getLocks();
/* Avoid debugger deadlocks */ * Delay any suspend while a call to java.lang.Thread.resume is in * progress (not including those in suspended threads). The wait is * timed because the threads suspended through * it may change the result of pendingAppResume() * This is ugly but we need to release the locks from getLocks * or else the notify will never happen. The locks must be * released and reacquired in the right order. else deadlocks * can happen. It is possible that, during this dance, the * notify will be missed, but since the wait needs to be timed * anyway, it won't be a disaster. Note that this code will * execute only on very rare occasions anyway. * This function must be called after preSuspend and before postSuspend. * If the thread is not between its start and end events, we should * still suspend it. To keep track of things, add the thread * to a separate list of threads so that we'll resume it later. /* never suspended by debugger => don't ever try to resume */ /* nested suspend so just undo one level */ * This thread was marked for suspension since its THREAD_START * event came in during a suspendAll, but the helper hasn't * completed the job yet. We decrement the count so the helper * won't suspend this thread after we are done with the resumeAll. * Another case to be handled here is when the debugger suspends * the thread while the app has it suspended. In this case, * the toBeResumed flag has been cleared indicating that * the thread should not be resumed when the debugger does a resume. * In this case, we also have to decrement the suspend count. * If we don't then when the app resumes the thread and our Thread.resume * bkpt handler is called, blockOnDebuggerSuspend will not resume * the thread because suspendCount will be 1 meaning that the * debugger has the thread suspended. See bug 6224859. /* nothing to hard resume so we're done */ * This is tricky. A suspendCount of 1 and toBeResumed means that * JVM/DI SuspendThread() or JVM/DI SuspendThreadList() was called * on this thread. The check for !suspendOnStart is paranoia that * we inherited from resumeThreadByNode(). /* never suspended by debugger => don't ever try to resume */ * This is tricky. A suspendCount of 1 and toBeResumed means that * JVM/DI SuspendThread() or JVM/DI SuspendThreadList() was called * on this thread. The check for !suspendOnStart is paranoia that * we inherited from resumeThreadByNode(). * This function must be called with the threadLock held. * Two facts conspire to make this routine complicated: * 1) the VM doesn't support nested external suspend * 2) the original resumeAll code structure doesn't retrieve the * entire thread list from JVMTI so we use the runningThreads * list and two helpers to get the job done. * Because we hold the threadLock, state seen by resumeCountHelper() * is the same state seen in resumeCopyHelper(). resumeCountHelper() * just counts up the number of threads to be hard resumed. * resumeCopyHelper() does the accounting for nested suspends and * special cases and, finally, populates the list of hard resume * threads to be passed to ResumeThreadList(). * At first glance, you might think that the accounting could be done * in resumeCountHelper(), but then resumeCopyHelper() would see * "post-resume" state in the accounting values (suspendCount and * toBeResumed) and would not be able to distinguish between a thread * that needs a hard resume versus a thread that is already running. /* count number of threads to hard resume */ /* nothing to hard resume so do just the accounting part */ /* copy the jthread values for threads to hard resume */ for (i = 0; i <
reqCnt; i++) {
* resumeThreadByNode() assumes that JVM/DI ResumeThread() * always works and does all the accounting updates. We do * the same here. We also don't clear the error. * This function must be called after preSuspend and before postSuspend. * Go through the initial list and see if we have anything to suspend. * If the thread is not between its start and end events, we should * still suspend it. To keep track of things, add the thread * to a separate list of threads so that we'll resume it later. /* Ignore requests for suspending debugger threads */ * Just increment the suspend count if we are waiting * for a deferred suspend or if this is a nested suspend. /* thread is not suspended yet so put it on the request list */ * We have something to suspend so try to do it. for (i = 0; i <
reqCnt; i++) {
/* thread was suspended as requested */ * If the thread was suspended by another app thread, * do nothing and report no error (we won't resume it later). * This error means that the suspend request failed * because the thread is either a zombie or not yet * started. In either case, we ignore the error. If the * thread is not started, it will be suspended for real * during the processing of its thread start event. /* count real, app and deferred (suspendOnStart) suspensions */ * The thread is normally between its start and end events, but if * not, check the auxiliary list used by threadControl_suspendThread. * If the node is in neither list, the debugger never suspended * this thread, so do nothing. /* let eventHelper.c: commandLoop() know we resumed one thread */ * If the node is in neither list, the debugger never suspended * this thread, so the suspend count is 0. for (i = 0; i <
count; i++) {
* Get a list of all threads and suspend them. for (i = 0; i <
count; i++) {
* Update the suspend count of any threads not yet (or no longer) * in the thread list above. * Since this helper is called with the threadLock held, we * don't need to recheck to see if the node is still on one * of the two thread lists. * Resume only those threads that the debugger has suspended. All * such threads must have a node in one of the thread lists, so there's * no need to get the whole thread list from JVMTI (unlike * Special event handler for events on the popped thread * that occur during the pop operation. /* notify that we got the event */ /* make sure we get suspended again */ * Pop one frame off the stack of thread. * popFrameEventLock is already held /* resume the popped thread so that the pop occurs and so we */ /* will get the event (step or method entry) after the pop */ /* wait for the event to occur */ /* make sure not to suspend until the popped thread is on the wait */ /* return popped thread to suspended state */ /* notify popped thread so it can proceed when resumed */ * pop frames of the stack of 'thread' until 'frame' is popped. /* compute the number of frames to pop */ /* enable instruction level single step, but first note prev value */ * Fix bug 6517249. The pop processing will disable invokes, * so remember if invokes are enabled now and restore * that state after we finish popping. /* Inform eventHandler logic we are in a popFrame for this thread */ /* pop frames using single step */ /* Reset StepRequest info (fromLine and stackDepth) after popframes * only if stepping is enabled. /* Check to see if any events are being consumed by a popFrame(). */ /* Thread wants to end? let it. */ /* This is an event we requested to mark the */ /* completion of the pop frame */ /* Tell event handler to assume event has been consumed. */ /* Pretend we were never called */ /* Events during pop commands may need to be ignored here. */ /* Always restore any exception (see below). */ * Check the list of unknown threads maintained by suspend * and resume. If this thread is currently present in the * moved to the runningThreads list, since it is a * Get a thread node for the reporting thread. For thread start * events, or if this event precedes a thread start event, * the thread node may need to be created. * It is possible for certain events (notably method entry/exit) * to precede thread start for some VM implementations. * An attempt was made to suspend this thread before it started. * We must suspend it now, before it starts to run. This must * be done with no locks held. * Clean up mechanism used to detect end of /* No point in doing this if the thread is about to die.*/ /* Returns JDWP flavored status and status flags. */ * While processing an event, an application thread is always * considered to be running even if its handler happens to be * cond waiting on an internal debugger monitor, etc. * Leave suspend status untouched since it is not possible * to distinguish debugger suspends from app suspends. * Hold any interrupts until after the event is processed. /* Create a class ref that will live beyond */ /* the end of this call */ /* if returned clazz is NULL, we just won't match */ * Hold any stops until after the event is processed. /* Everything should have been resumed */ * Returns the current thread, if the thread has generated at least * one event, and has not generated a thread end event.