Example #1
0
/*
 * We need the JDWP thread to hold off on doing stuff while we post an
 * event and then suspend ourselves.
 *
 * Call this with a threadId of zero if you just want to wait for the
 * current thread operation to complete.
 *
 * This could go to sleep waiting for another thread, so it's important
 * that the thread be marked as VMWAIT before calling here.
 */
void dvmJdwpSetWaitForEventThread(JdwpState* state, ObjectId threadId)
{
    bool waited = false;

    /* this is held for very brief periods; contention is unlikely */
    dvmDbgLockMutex(&state->eventThreadLock);

    /*
     * If another thread is already doing stuff, wait for it.  This can
     * go to sleep indefinitely.
     */
    while (state->eventThreadId != 0) {
        ALOGV("event in progress (0x%llx), 0x%llx sleeping",
            state->eventThreadId, threadId);
        waited = true;
        dvmDbgCondWait(&state->eventThreadCond, &state->eventThreadLock);
    }

    if (waited || threadId != 0)
        ALOGV("event token grabbed (0x%llx)", threadId);
    if (threadId != 0)
        state->eventThreadId = threadId;

    dvmDbgUnlockMutex(&state->eventThreadLock);
}
Example #2
0
/*
 * Get the next "event" serial number.  We use this in the response to
 * message type EventRequest.Set.
 */
u4 dvmJdwpNextEventSerial(JdwpState* state)
{
    dvmDbgLockMutex(&state->serialLock);
    u4 result = state->eventSerial++;
    dvmDbgUnlockMutex(&state->serialLock);

    return result;
}
Example #3
0
/*
 * Three possibilities:
 *  SP_NONE - do nothing
 *  SP_EVENT_THREAD - suspend ourselves
 *  SP_ALL - suspend everybody except JDWP support thread
 */
static void suspendByPolicy(JdwpState* state, JdwpSuspendPolicy suspendPolicy)
{
    if (suspendPolicy == SP_NONE)
        return;

    if (suspendPolicy == SP_ALL) {
        dvmDbgSuspendVM(true);
    } else {
        assert(suspendPolicy == SP_EVENT_THREAD);
    }

    /* this is rare but possible -- see CLASS_PREPARE handling */
    if (dvmDbgGetThreadSelfId() == state->debugThreadId) {
        ALOGI("NOTE: suspendByPolicy not suspending JDWP thread");
        return;
    }

    DebugInvokeReq* pReq = dvmDbgGetInvokeReq();
    while (true) {
        pReq->ready = true;
        dvmDbgSuspendSelf();
        pReq->ready = false;

        /*
         * The JDWP thread has told us (and possibly all other threads) to
         * resume.  See if it has left anything in our DebugInvokeReq mailbox.
         */
        if (!pReq->invokeNeeded) {
            /*LOGD("suspendByPolicy: no invoke needed");*/
            break;
        }

        /* grab this before posting/suspending again */
        dvmJdwpSetWaitForEventThread(state, dvmDbgGetThreadSelfId());

        /* leave pReq->invokeNeeded raised so we can check reentrancy */
        ALOGV("invoking method...");
        dvmDbgExecuteMethod(pReq);

        pReq->err = ERR_NONE;

        /* clear this before signaling */
        pReq->invokeNeeded = false;

        ALOGV("invoke complete, signaling and self-suspending");
        dvmDbgLockMutex(&pReq->lock);
        dvmDbgCondSignal(&pReq->cv);
        dvmDbgUnlockMutex(&pReq->lock);
    }
}
Example #4
0
/*
 * Clear the threadId and signal anybody waiting.
 */
void dvmJdwpClearWaitForEventThread(JdwpState* state)
{
    /*
     * Grab the mutex.  Don't try to go in/out of VMWAIT mode, as this
     * function is called by dvmSuspendSelf(), and the transition back
     * to RUNNING would confuse it.
     */
    dvmDbgLockMutex(&state->eventThreadLock);

    assert(state->eventThreadId != 0);
    ALOGV("cleared event token (0x%llx)", state->eventThreadId);

    state->eventThreadId = 0;

    dvmDbgCondSignal(&state->eventThreadCond);

    dvmDbgUnlockMutex(&state->eventThreadLock);
}
Example #5
0
/*
 * Unlock the "event" mutex.
 */
static void unlockEventMutex(JdwpState* state)
{
    dvmDbgUnlockMutex(&state->eventLock);
}
Example #6
0
/*
 * Initialize JDWP.
 *
 * Does not return until JDWP thread is running, but may return before
 * the thread is accepting network connections.
 */
JdwpState* dvmJdwpStartup(const JdwpStartupParams* pParams)
{
    JdwpState* state = NULL;

    /* comment this out when debugging JDWP itself */
    android_setMinPriority(LOG_TAG, ANDROID_LOG_DEBUG);

    state = (JdwpState*) calloc(1, sizeof(JdwpState));

    state->params = *pParams;

    state->requestSerial = 0x10000000;
    state->eventSerial = 0x20000000;
    dvmDbgInitMutex(&state->threadStartLock);
    dvmDbgInitMutex(&state->attachLock);
    dvmDbgInitMutex(&state->serialLock);
    dvmDbgInitMutex(&state->eventLock);
    state->eventThreadId = 0;
    dvmDbgInitMutex(&state->eventThreadLock);
    dvmDbgInitCond(&state->threadStartCond);
    dvmDbgInitCond(&state->attachCond);
    dvmDbgInitCond(&state->eventThreadCond);

    switch (pParams->transport) {
    case kJdwpTransportSocket:
        // LOGD("prepping for JDWP over TCP\n");
        state->transport = dvmJdwpSocketTransport();
        break;
    case kJdwpTransportAndroidAdb:
        // LOGD("prepping for JDWP over ADB\n");
        state->transport = dvmJdwpAndroidAdbTransport();
        /* TODO */
        break;
    default:
        LOGE("Unknown transport %d\n", pParams->transport);
        assert(false);
        goto fail;
    }

    if (!dvmJdwpNetStartup(state, pParams))
        goto fail;

    /*
     * Grab a mutex or two before starting the thread.  This ensures they
     * won't signal the cond var before we're waiting.
     */
    dvmDbgLockMutex(&state->threadStartLock);
    if (pParams->suspend)
        dvmDbgLockMutex(&state->attachLock);

    /*
     * We have bound to a port, or are trying to connect outbound to a
     * debugger.  Create the JDWP thread and let it continue the mission.
     */
    if (!dvmCreateInternalThread(&state->debugThreadHandle, "JDWP",
            jdwpThreadStart, state))
    {
        /* state is getting tossed, but unlock these anyway for cleanliness */
        dvmDbgUnlockMutex(&state->threadStartLock);
        if (pParams->suspend)
            dvmDbgUnlockMutex(&state->attachLock);
        goto fail;
    }

    /*
     * Wait until the thread finishes basic initialization.
     * TODO: cond vars should be waited upon in a loop
     */
    dvmDbgCondWait(&state->threadStartCond, &state->threadStartLock);
    dvmDbgUnlockMutex(&state->threadStartLock);


    /*
     * For suspend=y, wait for the debugger to connect to us or for us to
     * connect to the debugger.
     *
     * The JDWP thread will signal us when it connects successfully or
     * times out (for timeout=xxx), so we have to check to see what happened
     * when we wake up.
     */
    if (pParams->suspend) {
        dvmChangeStatus(NULL, THREAD_VMWAIT);
        dvmDbgCondWait(&state->attachCond, &state->attachLock);
        dvmDbgUnlockMutex(&state->attachLock);
        dvmChangeStatus(NULL, THREAD_RUNNING);

        if (!dvmJdwpIsActive(state)) {
            LOGE("JDWP connection failed\n");
            goto fail;
        }

        LOGI("JDWP connected\n");

        /*
         * Ordinarily we would pause briefly to allow the debugger to set
         * breakpoints and so on, but for "suspend=y" the VM init code will
         * pause the VM when it sends the VM_START message.
         */
    }

    return state;

fail:
    dvmJdwpShutdown(state);     // frees state
    return NULL;
}
Example #7
0
/*
 * Entry point for JDWP thread.  The thread was created through the VM
 * mechanisms, so there is a java/lang/Thread associated with us.
 */
static void* jdwpThreadStart(void* arg)
{
    JdwpState* state = (JdwpState*) arg;

    LOGV("JDWP: thread running\n");

    /*
     * Finish initializing "state", then notify the creating thread that
     * we're running.
     */
    state->debugThreadHandle = dvmThreadSelf()->handle;
    state->run = true;
    android_atomic_release_store(true, &state->debugThreadStarted);

    dvmDbgLockMutex(&state->threadStartLock);
    dvmDbgCondBroadcast(&state->threadStartCond);
    dvmDbgUnlockMutex(&state->threadStartLock);

    /* set the thread state to VMWAIT so GCs don't wait for us */
    dvmDbgThreadWaiting();

    /*
     * Loop forever if we're in server mode, processing connections.  In
     * non-server mode, we bail out of the thread when the debugger drops
     * us.
     *
     * We broadcast a notification when a debugger attaches, after we
     * successfully process the handshake.
     */
    while (state->run) {
        bool first;

        if (state->params.server) {
            /*
             * Block forever, waiting for a connection.  To support the
             * "timeout=xxx" option we'll need to tweak this.
             */
            if (!dvmJdwpAcceptConnection(state))
                break;
        } else {
            /*
             * If we're not acting as a server, we need to connect out to the
             * debugger.  To support the "timeout=xxx" option we need to
             * have a timeout if the handshake reply isn't received in a
             * reasonable amount of time.
             */
            if (!dvmJdwpEstablishConnection(state)) {
                /* wake anybody who was waiting for us to succeed */
                dvmDbgLockMutex(&state->attachLock);
                dvmDbgCondBroadcast(&state->attachCond);
                dvmDbgUnlockMutex(&state->attachLock);
                break;
            }
        }

        /* prep debug code to handle the new connection */
        dvmDbgConnected();

        /* process requests until the debugger drops */
        first = true;
        while (true) {
            // sanity check -- shouldn't happen?
            if (dvmThreadSelf()->status != THREAD_VMWAIT) {
                LOGE("JDWP thread no longer in VMWAIT (now %d); resetting\n",
                    dvmThreadSelf()->status);
                dvmDbgThreadWaiting();
            }

            if (!dvmJdwpProcessIncoming(state))     /* blocking read */
                break;

            if (first && !dvmJdwpAwaitingHandshake(state)) {
                /* handshake worked, tell the interpreter that we're active */
                first = false;

                /* set thread ID; requires object registry to be active */
                state->debugThreadId = dvmDbgGetThreadSelfId();

                /* wake anybody who's waiting for us */
                dvmDbgLockMutex(&state->attachLock);
                dvmDbgCondBroadcast(&state->attachCond);
                dvmDbgUnlockMutex(&state->attachLock);
            }
        }

        dvmJdwpCloseConnection(state);

        if (state->ddmActive) {
            state->ddmActive = false;

            /* broadcast the disconnect; must be in RUNNING state */
            dvmDbgThreadRunning();
            dvmDbgDdmDisconnected();
            dvmDbgThreadWaiting();
        }

        /* release session state, e.g. remove breakpoint instructions */
        dvmJdwpResetState(state);

        /* tell the interpreter that the debugger is no longer around */
        dvmDbgDisconnected();

        /* if we had threads suspended, resume them now */
        dvmUndoDebuggerSuspensions();

        /* if we connected out, this was a one-shot deal */
        if (!state->params.server)
            state->run = false;
    }

    /* back to running, for thread shutdown */
    dvmDbgThreadRunning();

    LOGV("JDWP: thread exiting\n");
    return NULL;
}