/* * 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); }
/* * 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; }