Example #1
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;
}
Example #2
0
/*
 * Send up a chunk of DDM data.
 *
 * While this takes the form of a JDWP "event", it doesn't interact with
 * other debugger traffic, and can't suspend the VM, so we skip all of
 * the fun event token gymnastics.
 */
void dvmJdwpDdmSendChunkV(JdwpState* state, int type, const struct iovec* iov,
    int iovcnt)
{
    u1 header[kJDWPHeaderLen + 8];
    size_t dataLen = 0;

    assert(iov != NULL);
    assert(iovcnt > 0 && iovcnt < 10);

    /*
     * "Wrap" the contents of the iovec with a JDWP/DDMS header.  We do
     * this by creating a new copy of the vector with space for the header.
     */
    struct iovec wrapiov[iovcnt+1];
    for (int i = 0; i < iovcnt; i++) {
        wrapiov[i+1].iov_base = iov[i].iov_base;
        wrapiov[i+1].iov_len = iov[i].iov_len;
        dataLen += iov[i].iov_len;
    }

    /* form the header (JDWP plus DDMS) */
    set4BE(header, sizeof(header) + dataLen);
    set4BE(header+4, dvmJdwpNextRequestSerial(state));
    set1(header+8, 0);     /* flags */
    set1(header+9, kJDWPDdmCmdSet);
    set1(header+10, kJDWPDdmCmd);
    set4BE(header+11, type);
    set4BE(header+15, dataLen);

    wrapiov[0].iov_base = header;
    wrapiov[0].iov_len = sizeof(header);

    /*
     * Make sure we're in VMWAIT in case the write blocks.
     */
    int oldStatus = dvmDbgThreadWaiting();
    dvmJdwpSendBufferedRequest(state, wrapiov, iovcnt+1);
    dvmDbgThreadContinuing(oldStatus);
}
Example #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;
}
Example #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;
}
Example #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;
}
Example #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;
}
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;
}