/* * Perform some operations at the "top" of the interpreter loop. * This stuff is required to support debugging and profiling. * * Using" __attribute__((noinline))" seems to do more harm than good. This * is best when inlined due to the large number of parameters, most of * which are local vars in the main interp loop. */ static void checkDebugAndProf(const u2* pc, const u4* fp, Thread* self, const Method* method, bool* pIsMethodEntry) { /* check to see if we've run off end of method */ assert(pc >= method->insns && pc < method->insns + dvmGetMethodInsnsSize(method)); #if 0 /* * When we hit a specific method, enable verbose instruction logging. * Sometimes it's helpful to use the debugger attach as a trigger too. */ if (*pIsMethodEntry) { static const char* cd = "Landroid/test/Arithmetic;"; static const char* mn = "shiftTest2"; static const char* sg = "()V"; if (/*gDvm.debuggerActive &&*/ strcmp(method->clazz->descriptor, cd) == 0 && strcmp(method->name, mn) == 0 && strcmp(method->shorty, sg) == 0) { LOGW("Reached %s.%s, enabling verbose mode\n", method->clazz->descriptor, method->name); android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE); dumpRegs(method, fp, true); } if (!gDvm.debuggerActive) *pIsMethodEntry = false; } #endif /* * If the debugger is attached, check for events. If the profiler is * enabled, update that too. * * This code is executed for every instruction we interpret, so for * performance we use a couple of #ifdef blocks instead of runtime tests. */ #ifdef WITH_PROFILER /* profiler and probably debugger */ bool isEntry = *pIsMethodEntry; if (isEntry) { *pIsMethodEntry = false; TRACE_METHOD_ENTER(self, method); } if (gDvm.debuggerActive) { updateDebugger(method, pc, fp, isEntry, self); } if (gDvm.instructionCountEnableCount != 0) { /* * Count up the #of executed instructions. This isn't synchronized * for thread-safety; if we need that we should make this * thread-local and merge counts into the global area when threads * exit (perhaps suspending all other threads GC-style and pulling * the data out of them). */ int inst = *pc & 0xff; gDvm.executedInstrCounts[inst]++; } #else /* debugger only */ if (gDvm.debuggerActive) { bool isEntry = *pIsMethodEntry; updateDebugger(method, pc, fp, isEntry, self); if (isEntry) *pIsMethodEntry = false; } #endif }
/* * 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; }