void ThreadManager::PerformPopFrames()

in modules/jpda/src/main/native/jdwp/common/agent/core/ThreadManager.cpp [879:1000]


void ThreadManager::PerformPopFrames(JNIEnv* jni, jint framesToPop, jthread thread) 
    throw(AgentException)
{
    JDWP_TRACE_ENTRY("PerformPopFrames(" << jni << ',' << framesToPop << ',' << thread << ')');

    MonitorAutoLock thrdmgrMonitorLock(m_thrdmgrMonitor JDWP_FILE_LINE);
    
    // thread should be suspended
    if (!GetThreadManager().IsSuspended(thread)) {
        throw AgentException(JDWP_ERROR_THREAD_NOT_SUSPENDED);
    }

    // The following check is disabled, because JVMTI and JDWP specifications 
    // are unclear if PopFrames should be invoked only for thread on an event.
    // Current algorithm supports PopFrames command for any suspended thread.
    /*
    if (!GetThreadManager().IsSuspendedOnEvent(jni, thread)) {
        throw AgentException(JDWP_ERROR_THREAD_NOT_SUSPENDED);
    }
    */

    jvmtiError err;
    char* threadName = 0;
#ifndef NDEBUG
        if (JDWP_TRACE_ENABLED(LOG_KIND_THREAD)) {
            jvmtiThreadInfo threadInfo;
            JVMTI_TRACE(err, GetJvmtiEnv()->GetThreadInfo(thread, &threadInfo));
            threadName = threadInfo.name;
        }
#endif // NDEBUG
    JvmtiAutoFree af(threadName);
    
    // The following check is just for sure.
    // This algorithm supposes that there are no invoke method handlers registered.
    // Otherwise, active invoke handlers could break popFrame process.
    {
        MonitorAutoLock lockExecList(m_execMonitor JDWP_FILE_LINE);
        if (!m_execList.empty()) {
            throw AgentException(JDWP_ERROR_THREAD_NOT_SUSPENDED);
        }
    }

    jint frameCount;
    JVMTI_TRACE(err, GetJvmtiEnv()->GetFrameCount(thread, &frameCount));
    if (err != JVMTI_ERROR_NONE) {
        throw AgentException(err);
    }
    if (frameCount <= framesToPop) {
        throw AgentException(JDWP_ERROR_INVALID_FRAMEID);
    }

    // if there is native frame, pop frame can't be performed
    CheckNativeFrameExistence(thread, framesToPop);

    MonitorAutoLock popFramesMonitorLock(m_popFramesMonitor JDWP_FILE_LINE);
    try {
        m_popFramesThread = thread;

        // enabling step request on thread where PopFrame command is performed
        JDWP_TRACE_THREAD("PerformPopFrames: enable internal step: thread=" 
            << JDWP_CHECK_NULL(threadName));
        GetRequestManager().EnableInternalStepRequest(jni, m_popFramesThread);

        // cycle where topmost frames are popped one after one
        for (int i = 0; i < framesToPop; i++) {
            // pop topmost frame, thread is already suspended on event
            JDWP_TRACE_THREAD("PerformPopFrames: pop: frame#=" << i 
                << ", thread=" << JDWP_CHECK_NULL(threadName));
            JVMTI_TRACE(err, GetJvmtiEnv()->PopFrame(m_popFramesThread));
            if (err != JVMTI_ERROR_NONE) {
                throw AgentException(err);
            }

            // resume thread
            JDWP_TRACE_THREAD("PerformPopFrames: resume: thread=" 
                << JDWP_CHECK_NULL(threadName));
            JVMTI_TRACE(err, GetJvmtiEnv()->ResumeThread(m_popFramesThread));
            JDWP_ASSERT(err != JVMTI_ERROR_THREAD_NOT_SUSPENDED);
            if (err != JVMTI_ERROR_NONE)
                throw AgentException(err);
            
            // wait for thread to achieve suspention point in InternalSingleStep handler
            JDWP_TRACE_THREAD("PerformPopFrames: wait for step: thread=" 
                << JDWP_CHECK_NULL(threadName));
            m_popFramesMonitorReleased = false;
            while (!m_popFramesMonitorReleased) {
                m_popFramesMonitor->Wait();
            }
            
            {
                // suspend thread on suspention point
                MonitorAutoLock stepMonitorLock(m_stepMonitor JDWP_FILE_LINE);
                JDWP_TRACE_THREAD("PerformPopFrames: suspend: thread=" 
                    << JDWP_CHECK_NULL(threadName));
                JVMTI_TRACE(err, GetJvmtiEnv()->SuspendThread(m_popFramesThread));
                JDWP_ASSERT(err != JVMTI_ERROR_THREAD_SUSPENDED);
                if (err != JVMTI_ERROR_NONE)
                    throw AgentException(err);

                // notify suspended thread on suspention point
                m_stepMonitorReleased = true;
                m_stepMonitor->NotifyAll();
                JDWP_TRACE_THREAD("PerformPopFrames: notify: thread=" 
                    << JDWP_CHECK_NULL(threadName));
            }
        }
        GetObjectManager().DeleteFrameIDs(jni, m_popFramesThread);
        
        JDWP_TRACE_THREAD("PerformPopFrames: disable internal step: thread=" 
            << JDWP_CHECK_NULL(threadName));
        GetRequestManager().DisableInternalStepRequest(jni, m_popFramesThread);
        
        m_popFramesThread = 0;
    } catch (AgentException& e) {
        JDWP_INFO("JDWP error: " << e.what() << " [" << e.ErrCode() << "]");
        JDWP_TRACE_THREAD("PerformPopFrames: disable internal step: thread=" 
            << JDWP_CHECK_NULL(threadName));
        GetRequestManager().DisableInternalStepRequest(jni, m_popFramesThread);
        m_popFramesThread = 0;
        throw(e);
    }
}