/* * Send a polite "VM is dying" message to the debugger. * * Skips the usual "event token" stuff. */ bool dvmJdwpPostVMDeath(JdwpState* state) { ALOGV("EVENT: %s", dvmJdwpEventKindStr(EK_VM_DEATH)); ExpandBuf* pReq = eventPrep(); expandBufAdd1(pReq, SP_NONE); expandBufAdd4BE(pReq, 1); expandBufAdd1(pReq, EK_VM_DEATH); expandBufAdd4BE(pReq, 0); eventFinish(state, pReq); return true; }
/* * 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; }
/* check controller buttons */ inline int padCheckState ( padBtnData *pdata ) { switch ( padCheck ( pdata ) ) { case 0: // *pdata->exitapp = 1; break ; case PAD_TRIANGLE: dbgprintf ( "PAD_TRIANGLE" ) ; *pdata->exitapp = 1; break; case PAD_CIRCLE: dbgprintf ( "PAD_CIRCLE" ) ; *pdata->exitapp = 1; break ; case PAD_CROSS: dbgprintf ( "PAD_CROSS" ) ; *pdata->exitapp = 1; break ; case PAD_SQUARE: dbgprintf ( "PAD_SQUARE" ) ; *pdata->exitapp = 1; break; case PAD_SELECT: dbgprintf ( "PAD_SELECT" ) ; *pdata->exitapp = 1; break; case PAD_START: dbgprintf ( "PAD_START" ) ; eventFinish ( pdata->edata ) ; *pdata->exitapp = 0; break; default: dbgprintf ( "default" ) ; // *pdata->exitapp = 1; break ; } return 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; }
/* * 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; }
/* * 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; }
/* * 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; }