Exemplo n.º 1
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);
    }
}
Exemplo n.º 2
0
/*
 * Tell the debugger that we have finished initializing.  This is always
 * sent, even if the debugger hasn't requested it.
 *
 * This should be sent "before the main thread is started and before
 * any application code has been executed".  The thread ID in the message
 * must be for the main thread.
 */
bool dvmJdwpPostVMStart(JdwpState* state, bool suspend)
{
    JdwpSuspendPolicy suspendPolicy;
    ObjectId threadId = dvmDbgGetThreadSelfId();

    if (suspend)
        suspendPolicy = SP_ALL;
    else
        suspendPolicy = SP_NONE;

    /* probably don't need this here */
    lockEventMutex(state);

    ExpandBuf* pReq = NULL;
    if (true) {
        ALOGV("EVENT: %s", dvmJdwpEventKindStr(EK_VM_START));
        ALOGV("  suspendPolicy=%s", dvmJdwpSuspendPolicyStr(suspendPolicy));

        pReq = eventPrep();
        expandBufAdd1(pReq, suspendPolicy);
        expandBufAdd4BE(pReq, 1);

        expandBufAdd1(pReq, EK_VM_START);
        expandBufAdd4BE(pReq, 0);       /* requestId */
        expandBufAdd8BE(pReq, threadId);
    }

    unlockEventMutex(state);

    /* send request and possibly suspend ourselves */
    if (pReq != NULL) {
        int oldStatus = dvmDbgThreadWaiting();
        if (suspendPolicy != SP_NONE)
            dvmJdwpSetWaitForEventThread(state, threadId);

        eventFinish(state, pReq);

        suspendByPolicy(state, suspendPolicy);
        dvmDbgThreadContinuing(oldStatus);
    }

    return true;
}
Exemplo n.º 3
0
/*
 * A thread is starting or stopping.
 *
 * Valid mods:
 *  Count, ThreadOnly
 */
bool dvmJdwpPostThreadChange(JdwpState* state, ObjectId threadId, bool start)
{
    JdwpSuspendPolicy suspendPolicy = SP_NONE;

    assert(threadId == dvmDbgGetThreadSelfId());

    /*
     * I don't think this can happen.
     */
    if (invokeInProgress(state)) {
        ALOGW("Not posting thread change during invoke");
        return false;
    }

    ModBasket basket;
    memset(&basket, 0, sizeof(basket));
    basket.threadId = threadId;

    /* don't allow the list to be updated while we scan it */
    lockEventMutex(state);

    JdwpEvent** matchList = allocMatchList(state);
    int matchCount = 0;

    if (start)
        findMatchingEvents(state, EK_THREAD_START, &basket, matchList,
            &matchCount);
    else
        findMatchingEvents(state, EK_THREAD_DEATH, &basket, matchList,
            &matchCount);

    ExpandBuf* pReq = NULL;
    if (matchCount != 0) {
        ALOGV("EVENT: %s(%d total) thread=%llx)",
            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
            basket.threadId);

        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
        ALOGV("  suspendPolicy=%s",
            dvmJdwpSuspendPolicyStr(suspendPolicy));

        pReq = eventPrep();
        expandBufAdd1(pReq, suspendPolicy);
        expandBufAdd4BE(pReq, matchCount);

        for (int i = 0; i < matchCount; i++) {
            expandBufAdd1(pReq, matchList[i]->eventKind);
            expandBufAdd4BE(pReq, matchList[i]->requestId);
            expandBufAdd8BE(pReq, basket.threadId);
        }

    }

    cleanupMatchList(state, matchList, matchCount);
    unlockEventMutex(state);

    /* send request and possibly suspend ourselves */
    if (pReq != NULL) {
        int oldStatus = dvmDbgThreadWaiting();
        if (suspendPolicy != SP_NONE)
            dvmJdwpSetWaitForEventThread(state, basket.threadId);

        eventFinish(state, pReq);

        suspendByPolicy(state, suspendPolicy);
        dvmDbgThreadContinuing(oldStatus);
    }

    return matchCount != 0;
}
Exemplo n.º 4
0
/*
 * A location of interest has been reached.  This handles:
 *   Breakpoint
 *   SingleStep
 *   MethodEntry
 *   MethodExit
 * These four types must be grouped together in a single response.  The
 * "eventFlags" indicates the type of event(s) that have happened.
 *
 * Valid mods:
 *   Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, InstanceOnly
 *   LocationOnly (for breakpoint/step only)
 *   Step (for step only)
 *
 * Interesting test cases:
 *  - Put a breakpoint on a native method.  Eclipse creates METHOD_ENTRY
 *    and METHOD_EXIT events with a ClassOnly mod on the method's class.
 *  - Use "run to line".  Eclipse creates a BREAKPOINT with Count=1.
 *  - Single-step to a line with a breakpoint.  Should get a single
 *    event message with both events in it.
 */
bool dvmJdwpPostLocationEvent(JdwpState* state, const JdwpLocation* pLoc,
    ObjectId thisPtr, int eventFlags)
{
    JdwpSuspendPolicy suspendPolicy = SP_NONE;
    ModBasket basket;
    char* nameAlloc = NULL;

    memset(&basket, 0, sizeof(basket));
    basket.pLoc = pLoc;
    basket.classId = pLoc->classId;
    basket.thisPtr = thisPtr;
    basket.threadId = dvmDbgGetThreadSelfId();
    basket.className = nameAlloc =
        dvmDescriptorToName(dvmDbgGetClassDescriptor(pLoc->classId));

    /*
     * On rare occasions we may need to execute interpreted code in the VM
     * while handling a request from the debugger.  Don't fire breakpoints
     * while doing so.  (I don't think we currently do this at all, so
     * this is mostly paranoia.)
     */
    if (basket.threadId == state->debugThreadId) {
        ALOGV("Ignoring location event in JDWP thread");
        free(nameAlloc);
        return false;
    }

    /*
     * The debugger variable display tab may invoke the interpreter to format
     * complex objects.  We want to ignore breakpoints and method entry/exit
     * traps while working on behalf of the debugger.
     *
     * If we don't ignore them, the VM will get hung up, because we'll
     * suspend on a breakpoint while the debugger is still waiting for its
     * method invocation to complete.
     */
    if (invokeInProgress(state)) {
        ALOGV("Not checking breakpoints during invoke (%s)", basket.className);
        free(nameAlloc);
        return false;
    }

    /* don't allow the list to be updated while we scan it */
    lockEventMutex(state);

    JdwpEvent** matchList = allocMatchList(state);
    int matchCount = 0;

    if ((eventFlags & DBG_BREAKPOINT) != 0)
        findMatchingEvents(state, EK_BREAKPOINT, &basket, matchList,
            &matchCount);
    if ((eventFlags & DBG_SINGLE_STEP) != 0)
        findMatchingEvents(state, EK_SINGLE_STEP, &basket, matchList,
            &matchCount);
    if ((eventFlags & DBG_METHOD_ENTRY) != 0)
        findMatchingEvents(state, EK_METHOD_ENTRY, &basket, matchList,
            &matchCount);
    if ((eventFlags & DBG_METHOD_EXIT) != 0)
        findMatchingEvents(state, EK_METHOD_EXIT, &basket, matchList,
            &matchCount);

    ExpandBuf* pReq = NULL;
    if (matchCount != 0) {
        ALOGV("EVENT: %s(%d total) %s.%s thread=%llx code=%llx)",
            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
            basket.className,
            dvmDbgGetMethodName(pLoc->classId, pLoc->methodId),
            basket.threadId, pLoc->idx);

        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
        ALOGV("  suspendPolicy=%s",
            dvmJdwpSuspendPolicyStr(suspendPolicy));

        pReq = eventPrep();
        expandBufAdd1(pReq, suspendPolicy);
        expandBufAdd4BE(pReq, matchCount);

        for (int i = 0; i < matchCount; i++) {
            expandBufAdd1(pReq, matchList[i]->eventKind);
            expandBufAdd4BE(pReq, matchList[i]->requestId);
            expandBufAdd8BE(pReq, basket.threadId);
            dvmJdwpAddLocation(pReq, pLoc);
        }
    }

    cleanupMatchList(state, matchList, matchCount);
    unlockEventMutex(state);

    /* send request and possibly suspend ourselves */
    if (pReq != NULL) {
        int oldStatus = dvmDbgThreadWaiting();
        if (suspendPolicy != SP_NONE)
            dvmJdwpSetWaitForEventThread(state, basket.threadId);

        eventFinish(state, pReq);

        suspendByPolicy(state, suspendPolicy);
        dvmDbgThreadContinuing(oldStatus);
    }

    free(nameAlloc);
    return matchCount != 0;
}
Exemplo n.º 5
0
/*
 * Announce that a class has been loaded.
 *
 * Valid mods:
 *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude
 */
bool dvmJdwpPostClassPrepare(JdwpState* state, int tag, RefTypeId refTypeId,
    const char* signature, int status)
{
    JdwpSuspendPolicy suspendPolicy = SP_NONE;
    ModBasket basket;
    char* nameAlloc = NULL;

    memset(&basket, 0, sizeof(basket));
    basket.classId = refTypeId;
    basket.threadId = dvmDbgGetThreadSelfId();
    basket.className = nameAlloc =
        dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));

    /* suppress class prep caused by debugger */
    if (invokeInProgress(state)) {
        ALOGV("Not posting class prep caused by invoke (%s)",basket.className);
        free(nameAlloc);
        return false;
    }

    /* don't allow the list to be updated while we scan it */
    lockEventMutex(state);

    JdwpEvent** matchList = allocMatchList(state);
    int matchCount = 0;

    findMatchingEvents(state, EK_CLASS_PREPARE, &basket, matchList,
        &matchCount);

    ExpandBuf* pReq = NULL;
    if (matchCount != 0) {
        ALOGV("EVENT: %s(%d total) thread=%llx)",
            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
            basket.threadId);

        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
        ALOGV("  suspendPolicy=%s",
            dvmJdwpSuspendPolicyStr(suspendPolicy));

        if (basket.threadId == state->debugThreadId) {
            /*
             * JDWP says that, for a class prep in the debugger thread, we
             * should set threadId to null and if any threads were supposed
             * to be suspended then we suspend all other threads.
             */
            ALOGV("  NOTE: class prepare in debugger thread!");
            basket.threadId = 0;
            if (suspendPolicy == SP_EVENT_THREAD)
                suspendPolicy = SP_ALL;
        }

        pReq = eventPrep();
        expandBufAdd1(pReq, suspendPolicy);
        expandBufAdd4BE(pReq, matchCount);

        for (int i = 0; i < matchCount; i++) {
            expandBufAdd1(pReq, matchList[i]->eventKind);
            expandBufAdd4BE(pReq, matchList[i]->requestId);
            expandBufAdd8BE(pReq, basket.threadId);

            expandBufAdd1(pReq, tag);
            expandBufAdd8BE(pReq, refTypeId);
            expandBufAddUtf8String(pReq, (const u1*) signature);
            expandBufAdd4BE(pReq, status);
        }
    }

    cleanupMatchList(state, matchList, matchCount);

    unlockEventMutex(state);

    /* send request and possibly suspend ourselves */
    if (pReq != NULL) {
        int oldStatus = dvmDbgThreadWaiting();
        if (suspendPolicy != SP_NONE)
            dvmJdwpSetWaitForEventThread(state, basket.threadId);

        eventFinish(state, pReq);

        suspendByPolicy(state, suspendPolicy);
        dvmDbgThreadContinuing(oldStatus);
    }

    free(nameAlloc);
    return matchCount != 0;
}
Exemplo n.º 6
0
/*
 * An exception has been thrown.  It may or may not have been caught.
 *
 * Valid mods:
 *  Count, ThreadOnly, ClassOnly, ClassMatch, ClassExclude, LocationOnly,
 *    ExceptionOnly, InstanceOnly
 *
 * The "exceptionId" has not been added to the GC-visible object registry,
 * because there's a pretty good chance that we're not going to send it
 * up the debugger.
 */
bool dvmJdwpPostException(JdwpState* state, const JdwpLocation* pThrowLoc,
    ObjectId exceptionId, RefTypeId exceptionClassId,
    const JdwpLocation* pCatchLoc, ObjectId thisPtr)
{
    JdwpSuspendPolicy suspendPolicy = SP_NONE;
    ModBasket basket;
    char* nameAlloc = NULL;

    memset(&basket, 0, sizeof(basket));
    basket.pLoc = pThrowLoc;
    basket.classId = pThrowLoc->classId;
    basket.threadId = dvmDbgGetThreadSelfId();
    basket.className = nameAlloc =
        dvmDescriptorToName(dvmDbgGetClassDescriptor(basket.classId));
    basket.excepClassId = exceptionClassId;
    basket.caught = (pCatchLoc->classId != 0);
    basket.thisPtr = thisPtr;

    /* don't try to post an exception caused by the debugger */
    if (invokeInProgress(state)) {
        ALOGV("Not posting exception hit during invoke (%s)",basket.className);
        free(nameAlloc);
        return false;
    }

    /* don't allow the list to be updated while we scan it */
    lockEventMutex(state);

    JdwpEvent** matchList = allocMatchList(state);
    int matchCount = 0;

    findMatchingEvents(state, EK_EXCEPTION, &basket, matchList, &matchCount);

    ExpandBuf* pReq = NULL;
    if (matchCount != 0) {
        ALOGV("EVENT: %s(%d total) thread=%llx exceptId=%llx caught=%d)",
            dvmJdwpEventKindStr(matchList[0]->eventKind), matchCount,
            basket.threadId, exceptionId, basket.caught);
        ALOGV("  throw: %d %llx %x %lld (%s.%s)", pThrowLoc->typeTag,
            pThrowLoc->classId, pThrowLoc->methodId, pThrowLoc->idx,
            dvmDbgGetClassDescriptor(pThrowLoc->classId),
            dvmDbgGetMethodName(pThrowLoc->classId, pThrowLoc->methodId));
        if (pCatchLoc->classId == 0) {
            ALOGV("  catch: (not caught)");
        } else {
            ALOGV("  catch: %d %llx %x %lld (%s.%s)", pCatchLoc->typeTag,
                pCatchLoc->classId, pCatchLoc->methodId, pCatchLoc->idx,
                dvmDbgGetClassDescriptor(pCatchLoc->classId),
                dvmDbgGetMethodName(pCatchLoc->classId, pCatchLoc->methodId));
        }

        suspendPolicy = scanSuspendPolicy(matchList, matchCount);
        ALOGV("  suspendPolicy=%s",
            dvmJdwpSuspendPolicyStr(suspendPolicy));

        pReq = eventPrep();
        expandBufAdd1(pReq, suspendPolicy);
        expandBufAdd4BE(pReq, matchCount);

        for (int i = 0; i < matchCount; i++) {
            expandBufAdd1(pReq, matchList[i]->eventKind);
            expandBufAdd4BE(pReq, matchList[i]->requestId);
            expandBufAdd8BE(pReq, basket.threadId);

            dvmJdwpAddLocation(pReq, pThrowLoc);
            expandBufAdd1(pReq, JT_OBJECT);
            expandBufAdd8BE(pReq, exceptionId);
            dvmJdwpAddLocation(pReq, pCatchLoc);
        }

        /* don't let the GC discard it */
        dvmDbgRegisterObjectId(exceptionId);
    }

    cleanupMatchList(state, matchList, matchCount);
    unlockEventMutex(state);

    /* send request and possibly suspend ourselves */
    if (pReq != NULL) {
        int oldStatus = dvmDbgThreadWaiting();
        if (suspendPolicy != SP_NONE)
            dvmJdwpSetWaitForEventThread(state, basket.threadId);

        eventFinish(state, pReq);

        suspendByPolicy(state, suspendPolicy);
        dvmDbgThreadContinuing(oldStatus);
    }

    free(nameAlloc);
    return matchCount != 0;
}
Exemplo n.º 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;
}