#include "opcommon.h" HANDLE_OPCODE(OP_UNUSED_FF) /* * In portable interp, most unused opcodes will fall through to here. */ ALOGE("unknown opcode 0x%02x\n", INST_INST(inst)); dvmAbort(); FINISH(1); OP_END
HANDLE_OPCODE($opcode /*vAA*/) vdst = INST_AA(inst); ILOGV("|move-result%s v%d %s(v%d=0x%08x)", (INST_INST(inst) == OP_MOVE_RESULT) ? "" : "-object", vdst, kSpacing+4, vdst,retval.i); SET_REGISTER(vdst, retval.i); #if defined(LOCCS_DIAOS) #if INST_INST(inst) != OP_MOVE_RESULT //ALOG(LOG_VERBOSE,"YWB","need to verify object"); diaos_monitor_object(curMethod, (Object*)GET_REGISTER(vdst)); #endif #endif FINISH(1); OP_END
HANDLE_OPCODE($opcode /*vAA*/) vsrc1 = INST_AA(inst); ILOGV("|return%s v%d", (INST_INST(inst) == OP_RETURN) ? "" : "-object", vsrc1); retval.i = GET_REGISTER(vsrc1); GOTO(returnFromMethod); OP_END
/* * Update the debugger on interesting events, such as hitting a breakpoint * or a single-step point. This is called from the top of the interpreter * loop, before the current instruction is processed. * * Set "methodEntry" if we've just entered the method. This detects * method exit by checking to see if the next instruction is "return". * * This can't catch native method entry/exit, so we have to handle that * at the point of invocation. We also need to catch it in dvmCallMethod * if we want to capture native->native calls made through JNI. * * Notes to self: * - Don't want to switch to VMWAIT while posting events to the debugger. * Let the debugger code decide if we need to change state. * - We may want to check for debugger-induced thread suspensions on * every instruction. That would make a "suspend all" more responsive * and reduce the chances of multiple simultaneous events occurring. * However, it could change the behavior some. * * TODO: method entry/exit events are probably less common than location * breakpoints. We may be able to speed things up a bit if we don't query * the event list unless we know there's at least one lurking within. */ static void updateDebugger(const Method* method, const u2* pc, const u4* fp, bool methodEntry, Thread* self) { int eventFlags = 0; /* * Update xtra.currentPc on every instruction. We need to do this if * there's a chance that we could get suspended. This can happen if * eventFlags != 0 here, or somebody manually requests a suspend * (which gets handled at PERIOD_CHECKS time). One place where this * needs to be correct is in dvmAddSingleStep(). */ EXPORT_PC(); if (methodEntry) eventFlags |= DBG_METHOD_ENTRY; /* * See if we have a breakpoint here. * * Depending on the "mods" associated with event(s) on this address, * we may or may not actually send a message to the debugger. */ #ifdef WITH_DEBUGGER if (INST_INST(*pc) == OP_BREAKPOINT) { LOGV("+++ breakpoint hit at %p\n", pc); eventFlags |= DBG_BREAKPOINT; } #endif /* * If the debugger is single-stepping one of our threads, check to * see if we're that thread and we've reached a step point. */ const StepControl* pCtrl = &gDvm.stepControl; if (pCtrl->active && pCtrl->thread == self) { int line, frameDepth; bool doStop = false; const char* msg = NULL; assert(!dvmIsNativeMethod(method)); if (pCtrl->depth == SD_INTO) { /* * Step into method calls. We break when the line number * or method pointer changes. If we're in SS_MIN mode, we * always stop. */ if (pCtrl->method != method) { doStop = true; msg = "new method"; } else if (pCtrl->size == SS_MIN) { doStop = true; msg = "new instruction"; } else if (!dvmAddressSetGet( pCtrl->pAddressSet, pc - method->insns)) { doStop = true; msg = "new line"; } } else if (pCtrl->depth == SD_OVER) { /* * Step over method calls. We break when the line number is * different and the frame depth is <= the original frame * depth. (We can't just compare on the method, because we * might get unrolled past it by an exception, and it's tricky * to identify recursion.) */ frameDepth = dvmComputeVagueFrameDepth(self, fp); if (frameDepth < pCtrl->frameDepth) { /* popped up one or more frames, always trigger */ doStop = true; msg = "method pop"; } else if (frameDepth == pCtrl->frameDepth) { /* same depth, see if we moved */ if (pCtrl->size == SS_MIN) { doStop = true; msg = "new instruction"; } else if (!dvmAddressSetGet(pCtrl->pAddressSet, pc - method->insns)) { doStop = true; msg = "new line"; } } } else { assert(pCtrl->depth == SD_OUT); /* * Return from the current method. We break when the frame * depth pops up. * * This differs from the "method exit" break in that it stops * with the PC at the next instruction in the returned-to * function, rather than the end of the returning function. */ frameDepth = dvmComputeVagueFrameDepth(self, fp); if (frameDepth < pCtrl->frameDepth) { doStop = true; msg = "method pop"; } } if (doStop) { LOGV("#####S %s\n", msg); eventFlags |= DBG_SINGLE_STEP; } } /* * Check to see if this is a "return" instruction. JDWP says we should * send the event *after* the code has been executed, but it also says * the location we provide is the last instruction. Since the "return" * instruction has no interesting side effects, we should be safe. * (We can't just move this down to the returnFromMethod label because * we potentially need to combine it with other events.) * * We're also not supposed to generate a method exit event if the method * terminates "with a thrown exception". */ u2 inst = INST_INST(FETCH(0)); if (inst == OP_RETURN_VOID || inst == OP_RETURN || inst == OP_RETURN_WIDE || inst == OP_RETURN_OBJECT) { eventFlags |= DBG_METHOD_EXIT; } /* * If there's something interesting going on, see if it matches one * of the debugger filters. */ if (eventFlags != 0) { Object* thisPtr = dvmGetThisPtr(method, fp); if (thisPtr != NULL && !dvmIsValidObject(thisPtr)) { /* * TODO: remove this check if we're confident that the "this" * pointer is where it should be -- slows us down, especially * during single-step. */ char* desc = dexProtoCopyMethodDescriptor(&method->prototype); LOGE("HEY: invalid 'this' ptr %p (%s.%s %s)\n", thisPtr, method->clazz->descriptor, method->name, desc); free(desc); dvmAbort(); } dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr, eventFlags); } }
/* * Main interpreter loop. * * This was written with an ARM implementation in mind. */ bool INTERP_FUNC_NAME(Thread* self, InterpState* interpState) { #if defined(EASY_GDB) StackSaveArea* debugSaveArea = SAVEAREA_FROM_FP(self->curFrame); #endif #if INTERP_TYPE == INTERP_DBG bool debugIsMethodEntry = interpState->debugIsMethodEntry; #endif #if defined(WITH_TRACKREF_CHECKS) int debugTrackedRefStart = interpState->debugTrackedRefStart; #endif DvmDex* methodClassDex; // curMethod->clazz->pDvmDex JValue retval; /* core state */ const Method* curMethod; // method we're interpreting const u2* pc; // program counter u4* fp; // frame pointer u2 inst; // current instruction /* instruction decoding */ u2 ref; // 16-bit quantity fetched directly u2 vsrc1, vsrc2, vdst; // usually used for register indexes /* method call setup */ const Method* methodToCall; bool methodCallRange; #if defined(THREADED_INTERP) /* static computed goto table */ DEFINE_GOTO_TABLE(handlerTable); #endif #if defined(WITH_JIT) #if 0 LOGD("*DebugInterp - entrypoint is %d, tgt is 0x%x, %s\n", interpState->entryPoint, interpState->pc, interpState->method->name); #endif #if INTERP_TYPE == INTERP_DBG /* Check to see if we've got a trace selection request. */ if ( /* * Only perform dvmJitCheckTraceRequest if the entry point is * EntryInstr and the jit state is either kJitTSelectRequest or * kJitTSelectRequestHot. If debugger/profiler happens to be attached, * dvmJitCheckTraceRequest will change the jitState to kJitDone but * but stay in the dbg interpreter. */ (interpState->entryPoint == kInterpEntryInstr) && (interpState->jitState == kJitTSelectRequest || interpState->jitState == kJitTSelectRequestHot) && dvmJitCheckTraceRequest(self, interpState)) { interpState->nextMode = INTERP_STD; //LOGD("Invalid trace request, exiting\n"); return true; } #endif /* INTERP_TYPE == INTERP_DBG */ #endif /* WITH_JIT */ /* copy state in */ curMethod = interpState->method; pc = interpState->pc; fp = interpState->fp; retval = interpState->retval; /* only need for kInterpEntryReturn? */ methodClassDex = curMethod->clazz->pDvmDex; LOGVV("threadid=%d: entry(%s) %s.%s pc=0x%x fp=%p ep=%d\n", self->threadId, (interpState->nextMode == INTERP_STD) ? "STD" : "DBG", curMethod->clazz->descriptor, curMethod->name, pc - curMethod->insns, fp, interpState->entryPoint); /* * DEBUG: scramble this to ensure we're not relying on it. */ methodToCall = (const Method*) -1; #if INTERP_TYPE == INTERP_DBG if (debugIsMethodEntry) { ILOGD("|-- Now interpreting %s.%s", curMethod->clazz->descriptor, curMethod->name); DUMP_REGS(curMethod, interpState->fp, false); } #endif switch (interpState->entryPoint) { case kInterpEntryInstr: /* just fall through to instruction loop or threaded kickstart */ break; case kInterpEntryReturn: CHECK_JIT(); goto returnFromMethod; case kInterpEntryThrow: goto exceptionThrown; default: dvmAbort(); } #ifdef THREADED_INTERP FINISH(0); /* fetch and execute first instruction */ #else while (1) { CHECK_DEBUG_AND_PROF(); /* service debugger and profiling */ CHECK_TRACKED_REFS(); /* check local reference tracking */ /* fetch the next 16 bits from the instruction stream */ inst = FETCH(0); switch (INST_INST(inst)) {
HANDLE_OPCODE($opcode /*vAA, vBBBB*/) vdst = INST_AA(inst); vsrc1 = FETCH(1); ILOGV("|move%s/from16 v%d,v%d %s(v%d=0x%08x)", (INST_INST(inst) == OP_MOVE_FROM16) ? "" : "-object", vdst, vsrc1, kSpacing, vdst, GET_REGISTER(vsrc1)); SET_REGISTER(vdst, GET_REGISTER(vsrc1)); /* ifdef WITH_TAINT_TRACKING */ SET_REGISTER_TAINT(vdst, GET_REGISTER_TAINT(vsrc1)); /* endif */ FINISH(2); OP_END