C++程序  |  1977行  |  65.33 KB

/*
 * Copyright (C) 2008 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/*
 * Main interpreter entry point and support functions.
 *
 * The entry point selects the "standard" or "debug" interpreter and
 * facilitates switching between them.  The standard interpreter may
 * use the "fast" or "portable" implementation.
 *
 * Some debugger support functions are included here.
 */
#include "Dalvik.h"
#include "interp/InterpDefs.h"
#if defined(WITH_JIT)
#include "interp/Jit.h"
#endif


/*
 * ===========================================================================
 *      Debugger support
 * ===========================================================================
 */

// fwd
static BreakpointSet* dvmBreakpointSetAlloc();
static void dvmBreakpointSetFree(BreakpointSet* pSet);

#if defined(WITH_JIT)
/* Target-specific save/restore */
extern "C" void dvmJitCalleeSave(double *saveArea);
extern "C" void dvmJitCalleeRestore(double *saveArea);
/* Interpreter entry points from compiled code */
extern "C" void dvmJitToInterpNormal();
extern "C" void dvmJitToInterpNoChain();
extern "C" void dvmJitToInterpPunt();
extern "C" void dvmJitToInterpSingleStep();
extern "C" void dvmJitToInterpTraceSelect();
#if defined(WITH_SELF_VERIFICATION)
extern "C" void dvmJitToInterpBackwardBranch();
#endif
#endif

/*
 * Initialize global breakpoint structures.
 */
bool dvmBreakpointStartup()
{
    gDvm.breakpointSet = dvmBreakpointSetAlloc();
    return (gDvm.breakpointSet != NULL);
}

/*
 * Free resources.
 */
void dvmBreakpointShutdown()
{
    dvmBreakpointSetFree(gDvm.breakpointSet);
}


/*
 * This represents a breakpoint inserted in the instruction stream.
 *
 * The debugger may ask us to create the same breakpoint multiple times.
 * We only remove the breakpoint when the last instance is cleared.
 */
struct Breakpoint {
    Method*     method;                 /* method we're associated with */
    u2*         addr;                   /* absolute memory address */
    u1          originalOpcode;         /* original 8-bit opcode value */
    int         setCount;               /* #of times this breakpoint was set */
};

/*
 * Set of breakpoints.
 */
struct BreakpointSet {
    /* grab lock before reading or writing anything else in here */
    pthread_mutex_t lock;

    /* vector of breakpoint structures */
    int         alloc;
    int         count;
    Breakpoint* breakpoints;
};

/*
 * Initialize a BreakpointSet.  Initially empty.
 */
static BreakpointSet* dvmBreakpointSetAlloc()
{
    BreakpointSet* pSet = (BreakpointSet*) calloc(1, sizeof(*pSet));

    dvmInitMutex(&pSet->lock);
    /* leave the rest zeroed -- will alloc on first use */

    return pSet;
}

/*
 * Free storage associated with a BreakpointSet.
 */
static void dvmBreakpointSetFree(BreakpointSet* pSet)
{
    if (pSet == NULL)
        return;

    free(pSet->breakpoints);
    free(pSet);
}

/*
 * Lock the breakpoint set.
 *
 * It's not currently necessary to switch to VMWAIT in the event of
 * contention, because nothing in here can block.  However, it's possible
 * that the bytecode-updater code could become fancier in the future, so
 * we do the trylock dance as a bit of future-proofing.
 */
static void dvmBreakpointSetLock(BreakpointSet* pSet)
{
    if (dvmTryLockMutex(&pSet->lock) != 0) {
        Thread* self = dvmThreadSelf();
        ThreadStatus oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
        dvmLockMutex(&pSet->lock);
        dvmChangeStatus(self, oldStatus);
    }
}

/*
 * Unlock the breakpoint set.
 */
static void dvmBreakpointSetUnlock(BreakpointSet* pSet)
{
    dvmUnlockMutex(&pSet->lock);
}

/*
 * Return the #of breakpoints.
 */
static int dvmBreakpointSetCount(const BreakpointSet* pSet)
{
    return pSet->count;
}

/*
 * See if we already have an entry for this address.
 *
 * The BreakpointSet's lock must be acquired before calling here.
 *
 * Returns the index of the breakpoint entry, or -1 if not found.
 */
static int dvmBreakpointSetFind(const BreakpointSet* pSet, const u2* addr)
{
    int i;

    for (i = 0; i < pSet->count; i++) {
        Breakpoint* pBreak = &pSet->breakpoints[i];
        if (pBreak->addr == addr)
            return i;
    }

    return -1;
}

/*
 * Retrieve the opcode that was originally at the specified location.
 *
 * The BreakpointSet's lock must be acquired before calling here.
 *
 * Returns "true" with the opcode in *pOrig on success.
 */
static bool dvmBreakpointSetOriginalOpcode(const BreakpointSet* pSet,
    const u2* addr, u1* pOrig)
{
    int idx = dvmBreakpointSetFind(pSet, addr);
    if (idx < 0)
        return false;

    *pOrig = pSet->breakpoints[idx].originalOpcode;
    return true;
}

/*
 * Check the opcode.  If it's a "magic" NOP, indicating the start of
 * switch or array data in the instruction stream, we don't want to set
 * a breakpoint.
 *
 * This can happen because the line number information dx generates
 * associates the switch data with the switch statement's line number,
 * and some debuggers put breakpoints at every address associated with
 * a given line.  The result is that the breakpoint stomps on the NOP
 * instruction that doubles as a data table magic number, and an explicit
 * check in the interpreter results in an exception being thrown.
 *
 * We don't want to simply refuse to add the breakpoint to the table,
 * because that confuses the housekeeping.  We don't want to reject the
 * debugger's event request, and we want to be sure that there's exactly
 * one un-set operation for every set op.
 */
static bool instructionIsMagicNop(const u2* addr)
{
    u2 curVal = *addr;
    return ((GET_OPCODE(curVal)) == OP_NOP && (curVal >> 8) != 0);
}

/*
 * Add a breakpoint at a specific address.  If the address is already
 * present in the table, this just increments the count.
 *
 * For a new entry, this will extract and preserve the current opcode from
 * the instruction stream, and replace it with a breakpoint opcode.
 *
 * The BreakpointSet's lock must be acquired before calling here.
 *
 * Returns "true" on success.
 */
static bool dvmBreakpointSetAdd(BreakpointSet* pSet, Method* method,
    unsigned int instrOffset)
{
    const int kBreakpointGrowth = 10;
    const u2* addr = method->insns + instrOffset;
    int idx = dvmBreakpointSetFind(pSet, addr);
    Breakpoint* pBreak;

    if (idx < 0) {
        if (pSet->count == pSet->alloc) {
            int newSize = pSet->alloc + kBreakpointGrowth;
            Breakpoint* newVec;

            ALOGV("+++ increasing breakpoint set size to %d", newSize);

            /* pSet->breakpoints will be NULL on first entry */
            newVec = (Breakpoint*)realloc(pSet->breakpoints, newSize * sizeof(Breakpoint));
            if (newVec == NULL)
                return false;

            pSet->breakpoints = newVec;
            pSet->alloc = newSize;
        }

        pBreak = &pSet->breakpoints[pSet->count++];
        pBreak->method = method;
        pBreak->addr = (u2*)addr;
        pBreak->originalOpcode = *(u1*)addr;
        pBreak->setCount = 1;

        /*
         * Change the opcode.  We must ensure that the BreakpointSet
         * updates happen before we change the opcode.
         *
         * If the method has not been verified, we do NOT insert the
         * breakpoint yet, since that will screw up the verifier.  The
         * debugger is allowed to insert breakpoints in unverified code,
         * but since we don't execute unverified code we don't need to
         * alter the bytecode yet.
         *
         * The class init code will "flush" all pending opcode writes
         * before verification completes.
         */
        assert(*(u1*)addr != OP_BREAKPOINT);
        if (dvmIsClassVerified(method->clazz)) {
            ALOGV("Class %s verified, adding breakpoint at %p",
                method->clazz->descriptor, addr);
            if (instructionIsMagicNop(addr)) {
                ALOGV("Refusing to set breakpoint on %04x at %s.%s + %#x",
                    *addr, method->clazz->descriptor, method->name,
                    instrOffset);
            } else {
                ANDROID_MEMBAR_FULL();
                dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
                    OP_BREAKPOINT);
            }
        } else {
            ALOGV("Class %s NOT verified, deferring breakpoint at %p",
                method->clazz->descriptor, addr);
        }
    } else {
        /*
         * Breakpoint already exists, just increase the count.
         */
        pBreak = &pSet->breakpoints[idx];
        pBreak->setCount++;
    }

    return true;
}

/*
 * Remove one instance of the specified breakpoint.  When the count
 * reaches zero, the entry is removed from the table, and the original
 * opcode is restored.
 *
 * The BreakpointSet's lock must be acquired before calling here.
 */
static void dvmBreakpointSetRemove(BreakpointSet* pSet, Method* method,
    unsigned int instrOffset)
{
    const u2* addr = method->insns + instrOffset;
    int idx = dvmBreakpointSetFind(pSet, addr);

    if (idx < 0) {
        /* breakpoint not found in set -- unexpected */
        if (*(u1*)addr == OP_BREAKPOINT) {
            ALOGE("Unable to restore breakpoint opcode (%s.%s +%#x)",
                method->clazz->descriptor, method->name, instrOffset);
            dvmAbort();
        } else {
            ALOGW("Breakpoint was already restored? (%s.%s +%#x)",
                method->clazz->descriptor, method->name, instrOffset);
        }
    } else {
        Breakpoint* pBreak = &pSet->breakpoints[idx];
        if (pBreak->setCount == 1) {
            /*
             * Must restore opcode before removing set entry.
             *
             * If the breakpoint was never flushed, we could be ovewriting
             * a value with the same value.  Not a problem, though we
             * could end up causing a copy-on-write here when we didn't
             * need to.  (Not worth worrying about.)
             */
            dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr,
                pBreak->originalOpcode);
            ANDROID_MEMBAR_FULL();

            if (idx != pSet->count-1) {
                /* shift down */
                memmove(&pSet->breakpoints[idx], &pSet->breakpoints[idx+1],
                    (pSet->count-1 - idx) * sizeof(pSet->breakpoints[0]));
            }
            pSet->count--;
            pSet->breakpoints[pSet->count].addr = (u2*) 0xdecadead; // debug
        } else {
            pBreak->setCount--;
            assert(pBreak->setCount > 0);
        }
    }
}

/*
 * Flush any breakpoints associated with methods in "clazz".  We want to
 * change the opcode, which might not have happened when the breakpoint
 * was initially set because the class was in the process of being
 * verified.
 *
 * The BreakpointSet's lock must be acquired before calling here.
 */
static void dvmBreakpointSetFlush(BreakpointSet* pSet, ClassObject* clazz)
{
    int i;
    for (i = 0; i < pSet->count; i++) {
        Breakpoint* pBreak = &pSet->breakpoints[i];
        if (pBreak->method->clazz == clazz) {
            /*
             * The breakpoint is associated with a method in this class.
             * It might already be there or it might not; either way,
             * flush it out.
             */
            ALOGV("Flushing breakpoint at %p for %s",
                pBreak->addr, clazz->descriptor);
            if (instructionIsMagicNop(pBreak->addr)) {
                ALOGV("Refusing to flush breakpoint on %04x at %s.%s + %#x",
                    *pBreak->addr, pBreak->method->clazz->descriptor,
                    pBreak->method->name, pBreak->addr - pBreak->method->insns);
            } else {
                dvmDexChangeDex1(clazz->pDvmDex, (u1*)pBreak->addr,
                    OP_BREAKPOINT);
            }
        }
    }
}


/*
 * Do any debugger-attach-time initialization.
 */
void dvmInitBreakpoints()
{
    /* quick sanity check */
    BreakpointSet* pSet = gDvm.breakpointSet;
    dvmBreakpointSetLock(pSet);
    if (dvmBreakpointSetCount(pSet) != 0) {
        ALOGW("WARNING: %d leftover breakpoints", dvmBreakpointSetCount(pSet));
        /* generally not good, but we can keep going */
    }
    dvmBreakpointSetUnlock(pSet);
}

/*
 * Add an address to the list, putting it in the first non-empty slot.
 *
 * Sometimes the debugger likes to add two entries for one breakpoint.
 * We add two entries here, so that we get the right behavior when it's
 * removed twice.
 *
 * This will only be run from the JDWP thread, and it will happen while
 * we are updating the event list, which is synchronized.  We're guaranteed
 * to be the only one adding entries, and the lock ensures that nobody
 * will be trying to remove them while we're in here.
 *
 * "addr" is the absolute address of the breakpoint bytecode.
 */
void dvmAddBreakAddr(Method* method, unsigned int instrOffset)
{
    BreakpointSet* pSet = gDvm.breakpointSet;
    dvmBreakpointSetLock(pSet);
    dvmBreakpointSetAdd(pSet, method, instrOffset);
    dvmBreakpointSetUnlock(pSet);
}

/*
 * Remove an address from the list by setting the entry to NULL.
 *
 * This can be called from the JDWP thread (because the debugger has
 * cancelled the breakpoint) or from an event thread (because it's a
 * single-shot breakpoint, e.g. "run to line").  We only get here as
 * the result of removing an entry from the event list, which is
 * synchronized, so it should not be possible for two threads to be
 * updating breakpoints at the same time.
 */
void dvmClearBreakAddr(Method* method, unsigned int instrOffset)
{
    BreakpointSet* pSet = gDvm.breakpointSet;
    dvmBreakpointSetLock(pSet);
    dvmBreakpointSetRemove(pSet, method, instrOffset);
    dvmBreakpointSetUnlock(pSet);
}

/*
 * Get the original opcode from under a breakpoint.
 *
 * On SMP hardware it's possible one core might try to execute a breakpoint
 * after another core has cleared it.  We need to handle the case where
 * there's no entry in the breakpoint set.  (The memory barriers in the
 * locks and in the breakpoint update code should ensure that, once we've
 * observed the absence of a breakpoint entry, we will also now observe
 * the restoration of the original opcode.  The fact that we're holding
 * the lock prevents other threads from confusing things further.)
 */
u1 dvmGetOriginalOpcode(const u2* addr)
{
    BreakpointSet* pSet = gDvm.breakpointSet;
    u1 orig = 0;

    dvmBreakpointSetLock(pSet);
    if (!dvmBreakpointSetOriginalOpcode(pSet, addr, &orig)) {
        orig = *(u1*)addr;
        if (orig == OP_BREAKPOINT) {
            ALOGE("GLITCH: can't find breakpoint, opcode is still set");
            dvmAbort();
        }
    }
    dvmBreakpointSetUnlock(pSet);

    return orig;
}

/*
 * Flush any breakpoints associated with methods in "clazz".
 *
 * We don't want to modify the bytecode of a method before the verifier
 * gets a chance to look at it, so we postpone opcode replacement until
 * after verification completes.
 */
void dvmFlushBreakpoints(ClassObject* clazz)
{
    BreakpointSet* pSet = gDvm.breakpointSet;

    if (pSet == NULL)
        return;

    assert(dvmIsClassVerified(clazz));
    dvmBreakpointSetLock(pSet);
    dvmBreakpointSetFlush(pSet, clazz);
    dvmBreakpointSetUnlock(pSet);
}

/*
 * Add a single step event.  Currently this is a global item.
 *
 * We set up some initial values based on the thread's current state.  This
 * won't work well if the thread is running, so it's up to the caller to
 * verify that it's suspended.
 *
 * This is only called from the JDWP thread.
 */
bool dvmAddSingleStep(Thread* thread, int size, int depth)
{
    StepControl* pCtrl = &gDvm.stepControl;

    if (pCtrl->active && thread != pCtrl->thread) {
        ALOGW("WARNING: single-step active for %p; adding %p",
            pCtrl->thread, thread);

        /*
         * Keep going, overwriting previous.  This can happen if you
         * suspend a thread in Object.wait, hit the single-step key, then
         * switch to another thread and do the same thing again.
         * The first thread's step is still pending.
         *
         * TODO: consider making single-step per-thread.  Adds to the
         * overhead, but could be useful in rare situations.
         */
    }

    pCtrl->size = static_cast<JdwpStepSize>(size);
    pCtrl->depth = static_cast<JdwpStepDepth>(depth);
    pCtrl->thread = thread;

    /*
     * We may be stepping into or over method calls, or running until we
     * return from the current method.  To make this work we need to track
     * the current line, current method, and current stack depth.  We need
     * to be checking these after most instructions, notably those that
     * call methods, return from methods, or are on a different line from the
     * previous instruction.
     *
     * We have to start with a snapshot of the current state.  If we're in
     * an interpreted method, everything we need is in the current frame.  If
     * we're in a native method, possibly with some extra JNI frames pushed
     * on by PushLocalFrame, we want to use the topmost native method.
     */
    const StackSaveArea* saveArea;
    u4* fp;
    u4* prevFp = NULL;

    for (fp = thread->interpSave.curFrame; fp != NULL;
         fp = saveArea->prevFrame) {
        const Method* method;

        saveArea = SAVEAREA_FROM_FP(fp);
        method = saveArea->method;

        if (!dvmIsBreakFrame((u4*)fp) && !dvmIsNativeMethod(method))
            break;
        prevFp = fp;
    }
    if (fp == NULL) {
        ALOGW("Unexpected: step req in native-only threadid=%d",
            thread->threadId);
        return false;
    }
    if (prevFp != NULL) {
        /*
         * First interpreted frame wasn't the one at the bottom.  Break
         * frames are only inserted when calling from native->interp, so we
         * don't need to worry about one being here.
         */
        ALOGV("##### init step while in native method");
        fp = prevFp;
        assert(!dvmIsBreakFrame((u4*)fp));
        assert(dvmIsNativeMethod(SAVEAREA_FROM_FP(fp)->method));
        saveArea = SAVEAREA_FROM_FP(fp);
    }

    /*
     * Pull the goodies out.  "xtra.currentPc" should be accurate since
     * we update it on every instruction while the debugger is connected.
     */
    pCtrl->method = saveArea->method;
    // Clear out any old address set
    if (pCtrl->pAddressSet != NULL) {
        // (discard const)
        free((void *)pCtrl->pAddressSet);
        pCtrl->pAddressSet = NULL;
    }
    if (dvmIsNativeMethod(pCtrl->method)) {
        pCtrl->line = -1;
    } else {
        pCtrl->line = dvmLineNumFromPC(saveArea->method,
                        saveArea->xtra.currentPc - saveArea->method->insns);
        pCtrl->pAddressSet
                = dvmAddressSetForLine(saveArea->method, pCtrl->line);
    }
    pCtrl->frameDepth =
        dvmComputeVagueFrameDepth(thread, thread->interpSave.curFrame);
    pCtrl->active = true;

    ALOGV("##### step init: thread=%p meth=%p '%s' line=%d frameDepth=%d depth=%s size=%s",
        pCtrl->thread, pCtrl->method, pCtrl->method->name,
        pCtrl->line, pCtrl->frameDepth,
        dvmJdwpStepDepthStr(pCtrl->depth),
        dvmJdwpStepSizeStr(pCtrl->size));

    return true;
}

/*
 * Disable a single step event.
 */
void dvmClearSingleStep(Thread* thread)
{
    UNUSED_PARAMETER(thread);

    gDvm.stepControl.active = false;
}

/*
 * The interpreter just threw.  Handle any special subMode requirements.
 * All interpSave state must be valid on entry.
 */
void dvmReportExceptionThrow(Thread* self, Object* exception)
{
    const Method* curMethod = self->interpSave.method;
#if defined(WITH_JIT)
    if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
        dvmJitEndTraceSelect(self, self->interpSave.pc);
    }
    if (self->interpBreak.ctl.breakFlags & kInterpSingleStep) {
        /* Discard any single-step native returns to translation */
        self->jitResumeNPC = NULL;
    }
#endif
    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
        void *catchFrame;
        int offset = self->interpSave.pc - curMethod->insns;
        int catchRelPc = dvmFindCatchBlock(self, offset, exception,
                                           true, &catchFrame);
        dvmDbgPostException(self->interpSave.curFrame, offset, catchFrame,
                            catchRelPc, exception);
    }
}

/*
 * The interpreter is preparing to do an invoke (both native & normal).
 * Handle any special subMode requirements.  All interpSave state
 * must be valid on entry.
 */
void dvmReportInvoke(Thread* self, const Method* methodToCall)
{
    TRACE_METHOD_ENTER(self, methodToCall);
}

/*
 * The interpreter is preparing to do a native invoke. Handle any
 * special subMode requirements.  NOTE: for a native invoke,
 * dvmReportInvoke() and dvmReportPreNativeInvoke() will both
 * be called prior to the invoke.  fp is the Dalvik FP of the calling
 * method.
 */
void dvmReportPreNativeInvoke(const Method* methodToCall, Thread* self, u4* fp)
{
#if defined(WITH_JIT)
    /*
     * Actively building a trace?  If so, end it now.   The trace
     * builder can't follow into or through a native method.
     */
    if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
        dvmCheckJit(self->interpSave.pc, self);
    }
#endif
    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
        Object* thisPtr = dvmGetThisPtr(self->interpSave.method, fp);
        assert(thisPtr == NULL || dvmIsHeapAddress(thisPtr));
        dvmDbgPostLocationEvent(methodToCall, -1, thisPtr, DBG_METHOD_ENTRY);
    }
}

/*
 * The interpreter has returned from a native invoke. Handle any
 * special subMode requirements.  fp is the Dalvik FP of the calling
 * method.
 */
void dvmReportPostNativeInvoke(const Method* methodToCall, Thread* self, u4* fp)
{
    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
        Object* thisPtr = dvmGetThisPtr(self->interpSave.method, fp);
        assert(thisPtr == NULL || dvmIsHeapAddress(thisPtr));
        dvmDbgPostLocationEvent(methodToCall, -1, thisPtr, DBG_METHOD_EXIT);
    }
    if (self->interpBreak.ctl.subMode & kSubModeMethodTrace) {
        dvmFastNativeMethodTraceExit(methodToCall, self);
    }
}

/*
 * The interpreter has returned from a normal method.  Handle any special
 * subMode requirements.  All interpSave state must be valid on entry.
 */
void dvmReportReturn(Thread* self)
{
    TRACE_METHOD_EXIT(self, self->interpSave.method);
#if defined(WITH_JIT)
    if (dvmIsBreakFrame(self->interpSave.curFrame) &&
        (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild)) {
        dvmCheckJit(self->interpSave.pc, self);
    }
#endif
}

/*
 * 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,
                           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().
     */
    dvmExportPC(pc, fp);

    if (self->debugIsMethodEntry) {
        eventFlags |= DBG_METHOD_ENTRY;
        self->debugIsMethodEntry = false;
    }

    /*
     * 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.
     */
    if (GET_OPCODE(*pc) == OP_BREAKPOINT) {
        ALOGV("+++ breakpoint hit at %p", pc);
        eventFlags |= DBG_BREAKPOINT;
    }

    /*
     * 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 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) {
            ALOGV("#####S %s", 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 opcode = GET_OPCODE(*pc);
    if (opcode == OP_RETURN_VOID || opcode == OP_RETURN ||
        opcode == OP_RETURN_WIDE ||opcode == 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 && !dvmIsHeapAddress(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);
            ALOGE("HEY: invalid 'this' ptr %p (%s.%s %s)", thisPtr,
                method->clazz->descriptor, method->name, desc);
            free(desc);
            dvmAbort();
        }
        dvmDbgPostLocationEvent(method, pc - method->insns, thisPtr,
            eventFlags);
    }
}

/*
 * Recover the "this" pointer from the current interpreted method.  "this"
 * is always in "in0" for non-static methods.
 *
 * The "ins" start at (#of registers - #of ins).  Note in0 != v0.
 *
 * This works because "dx" guarantees that it will work.  It's probably
 * fairly common to have a virtual method that doesn't use its "this"
 * pointer, in which case we're potentially wasting a register.  However,
 * the debugger doesn't treat "this" as just another argument.  For
 * example, events (such as breakpoints) can be enabled for specific
 * values of "this".  There is also a separate StackFrame.ThisObject call
 * in JDWP that is expected to work for any non-native non-static method.
 *
 * Because we need it when setting up debugger event filters, we want to
 * be able to do this quickly.
 */
Object* dvmGetThisPtr(const Method* method, const u4* fp)
{
    if (dvmIsStaticMethod(method))
        return NULL;
    return (Object*)fp[method->registersSize - method->insSize];
}


#if defined(WITH_TRACKREF_CHECKS)
/*
 * Verify that all internally-tracked references have been released.  If
 * they haven't, print them and abort the VM.
 *
 * "debugTrackedRefStart" indicates how many refs were on the list when
 * we were first invoked.
 */
void dvmInterpCheckTrackedRefs(Thread* self, const Method* method,
    int debugTrackedRefStart)
{
    if (dvmReferenceTableEntries(&self->internalLocalRefTable)
        != (size_t) debugTrackedRefStart)
    {
        char* desc;
        Object** top;
        int count;

        count = dvmReferenceTableEntries(&self->internalLocalRefTable);

        ALOGE("TRACK: unreleased internal reference (prev=%d total=%d)",
            debugTrackedRefStart, count);
        desc = dexProtoCopyMethodDescriptor(&method->prototype);
        ALOGE("       current method is %s.%s %s", method->clazz->descriptor,
            method->name, desc);
        free(desc);
        top = self->internalLocalRefTable.table + debugTrackedRefStart;
        while (top < self->internalLocalRefTable.nextEntry) {
            ALOGE("  %p (%s)",
                 *top,
                 ((*top)->clazz != NULL) ? (*top)->clazz->descriptor : "");
            top++;
        }
        dvmDumpThread(self, false);

        dvmAbort();
    }
    //ALOGI("TRACK OK");
}
#endif


#ifdef LOG_INSTR
/*
 * Dump the v-registers.  Sent to the ILOG log tag.
 */
void dvmDumpRegs(const Method* method, const u4* framePtr, bool inOnly)
{
    int i, localCount;

    localCount = method->registersSize - method->insSize;

    ALOG(LOG_VERBOSE, LOG_TAG"i", "Registers (fp=%p):", framePtr);
    for (i = method->registersSize-1; i >= 0; i--) {
        if (i >= localCount) {
            ALOG(LOG_VERBOSE, LOG_TAG"i", "  v%-2d in%-2d : 0x%08x",
                i, i-localCount, framePtr[i]);
        } else {
            if (inOnly) {
                ALOG(LOG_VERBOSE, LOG_TAG"i", "  [...]");
                break;
            }
            const char* name = "";
#if 0   // "locals" structure has changed -- need to rewrite this
            int j;
            DexFile* pDexFile = method->clazz->pDexFile;
            const DexCode* pDexCode = dvmGetMethodCode(method);
            int localsSize = dexGetLocalsSize(pDexFile, pDexCode);
            const DexLocal* locals = dvmDexGetLocals(pDexFile, pDexCode);
            for (j = 0; j < localsSize, j++) {
                if (locals[j].registerNum == (u4) i) {
                    name = dvmDexStringStr(locals[j].pName);
                    break;
                }
            }
#endif
            ALOG(LOG_VERBOSE, LOG_TAG"i", "  v%-2d      : 0x%08x %s",
                i, framePtr[i], name);
        }
    }
}
#endif


/*
 * ===========================================================================
 *      Entry point and general support functions
 * ===========================================================================
 */

/*
 * Construct an s4 from two consecutive half-words of switch data.
 * This needs to check endianness because the DEX optimizer only swaps
 * half-words in instruction stream.
 *
 * "switchData" must be 32-bit aligned.
 */
#if __BYTE_ORDER == __LITTLE_ENDIAN
static inline s4 s4FromSwitchData(const void* switchData) {
    return *(s4*) switchData;
}
#else
static inline s4 s4FromSwitchData(const void* switchData) {
    u2* data = switchData;
    return data[0] | (((s4) data[1]) << 16);
}
#endif

/*
 * Find the matching case.  Returns the offset to the handler instructions.
 *
 * Returns 3 if we don't find a match (it's the size of the packed-switch
 * instruction).
 */
s4 dvmInterpHandlePackedSwitch(const u2* switchData, s4 testVal)
{
    const int kInstrLen = 3;
    u2 size;
    s4 firstKey;
    const s4* entries;

    /*
     * Packed switch data format:
     *  ushort ident = 0x0100   magic value
     *  ushort size             number of entries in the table
     *  int first_key           first (and lowest) switch case value
     *  int targets[size]       branch targets, relative to switch opcode
     *
     * Total size is (4+size*2) 16-bit code units.
     */
    if (*switchData++ != kPackedSwitchSignature) {
        /* should have been caught by verifier */
        dvmThrowInternalError("bad packed switch magic");
        return kInstrLen;
    }

    size = *switchData++;
    assert(size > 0);

    firstKey = *switchData++;
    firstKey |= (*switchData++) << 16;

    if (testVal < firstKey || testVal >= firstKey + size) {
        LOGVV("Value %d not found in switch (%d-%d)",
            testVal, firstKey, firstKey+size-1);
        return kInstrLen;
    }

    /* The entries are guaranteed to be aligned on a 32-bit boundary;
     * we can treat them as a native int array.
     */
    entries = (const s4*) switchData;
    assert(((u4)entries & 0x3) == 0);

    assert(testVal - firstKey >= 0 && testVal - firstKey < size);
    LOGVV("Value %d found in slot %d (goto 0x%02x)",
        testVal, testVal - firstKey,
        s4FromSwitchData(&entries[testVal - firstKey]));
    return s4FromSwitchData(&entries[testVal - firstKey]);
}

/*
 * Find the matching case.  Returns the offset to the handler instructions.
 *
 * Returns 3 if we don't find a match (it's the size of the sparse-switch
 * instruction).
 */
s4 dvmInterpHandleSparseSwitch(const u2* switchData, s4 testVal)
{
    const int kInstrLen = 3;
    u2 size;
    const s4* keys;
    const s4* entries;

    /*
     * Sparse switch data format:
     *  ushort ident = 0x0200   magic value
     *  ushort size             number of entries in the table; > 0
     *  int keys[size]          keys, sorted low-to-high; 32-bit aligned
     *  int targets[size]       branch targets, relative to switch opcode
     *
     * Total size is (2+size*4) 16-bit code units.
     */

    if (*switchData++ != kSparseSwitchSignature) {
        /* should have been caught by verifier */
        dvmThrowInternalError("bad sparse switch magic");
        return kInstrLen;
    }

    size = *switchData++;
    assert(size > 0);

    /* The keys are guaranteed to be aligned on a 32-bit boundary;
     * we can treat them as a native int array.
     */
    keys = (const s4*) switchData;
    assert(((u4)keys & 0x3) == 0);

    /* The entries are guaranteed to be aligned on a 32-bit boundary;
     * we can treat them as a native int array.
     */
    entries = keys + size;
    assert(((u4)entries & 0x3) == 0);

    /*
     * Binary-search through the array of keys, which are guaranteed to
     * be sorted low-to-high.
     */
    int lo = 0;
    int hi = size - 1;
    while (lo <= hi) {
        int mid = (lo + hi) >> 1;

        s4 foundVal = s4FromSwitchData(&keys[mid]);
        if (testVal < foundVal) {
            hi = mid - 1;
        } else if (testVal > foundVal) {
            lo = mid + 1;
        } else {
            LOGVV("Value %d found in entry %d (goto 0x%02x)",
                testVal, mid, s4FromSwitchData(&entries[mid]));
            return s4FromSwitchData(&entries[mid]);
        }
    }

    LOGVV("Value %d not found in switch", testVal);
    return kInstrLen;
}

/*
 * Copy data for a fill-array-data instruction.  On a little-endian machine
 * we can just do a memcpy(), on a big-endian system we have work to do.
 *
 * The trick here is that dexopt has byte-swapped each code unit, which is
 * exactly what we want for short/char data.  For byte data we need to undo
 * the swap, and for 4- or 8-byte values we need to swap pieces within
 * each word.
 */
static void copySwappedArrayData(void* dest, const u2* src, u4 size, u2 width)
{
#if __BYTE_ORDER == __LITTLE_ENDIAN
    memcpy(dest, src, size*width);
#else
    int i;

    switch (width) {
    case 1:
        /* un-swap pairs of bytes as we go */
        for (i = (size-1) & ~1; i >= 0; i -= 2) {
            ((u1*)dest)[i] = ((u1*)src)[i+1];
            ((u1*)dest)[i+1] = ((u1*)src)[i];
        }
        /*
         * "src" is padded to end on a two-byte boundary, but we don't want to
         * assume "dest" is, so we handle odd length specially.
         */
        if ((size & 1) != 0) {
            ((u1*)dest)[size-1] = ((u1*)src)[size];
        }
        break;
    case 2:
        /* already swapped correctly */
        memcpy(dest, src, size*width);
        break;
    case 4:
        /* swap word halves */
        for (i = 0; i < (int) size; i++) {
            ((u4*)dest)[i] = (src[(i << 1) + 1] << 16) | src[i << 1];
        }
        break;
    case 8:
        /* swap word halves and words */
        for (i = 0; i < (int) (size << 1); i += 2) {
            ((int*)dest)[i] = (src[(i << 1) + 3] << 16) | src[(i << 1) + 2];
            ((int*)dest)[i+1] = (src[(i << 1) + 1] << 16) | src[i << 1];
        }
        break;
    default:
        ALOGE("Unexpected width %d in copySwappedArrayData", width);
        dvmAbort();
        break;
    }
#endif
}

/*
 * Fill the array with predefined constant values.
 *
 * Returns true if job is completed, otherwise false to indicate that
 * an exception has been thrown.
 */
bool dvmInterpHandleFillArrayData(ArrayObject* arrayObj, const u2* arrayData)
{
    u2 width;
    u4 size;

    if (arrayObj == NULL) {
        dvmThrowNullPointerException(NULL);
        return false;
    }
    assert (!IS_CLASS_FLAG_SET(((Object *)arrayObj)->clazz,
                               CLASS_ISOBJECTARRAY));

    /*
     * Array data table format:
     *  ushort ident = 0x0300   magic value
     *  ushort width            width of each element in the table
     *  uint   size             number of elements in the table
     *  ubyte  data[size*width] table of data values (may contain a single-byte
     *                          padding at the end)
     *
     * Total size is 4+(width * size + 1)/2 16-bit code units.
     */
    if (arrayData[0] != kArrayDataSignature) {
        dvmThrowInternalError("bad array data magic");
        return false;
    }

    width = arrayData[1];
    size = arrayData[2] | (((u4)arrayData[3]) << 16);

    if (size > arrayObj->length) {
        dvmThrowArrayIndexOutOfBoundsException(arrayObj->length, size);
        return false;
    }
    copySwappedArrayData(arrayObj->contents, &arrayData[4], size, width);
    return true;
}

/*
 * Find the concrete method that corresponds to "methodIdx".  The code in
 * "method" is executing invoke-method with "thisClass" as its first argument.
 *
 * Returns NULL with an exception raised on failure.
 */
Method* dvmInterpFindInterfaceMethod(ClassObject* thisClass, u4 methodIdx,
    const Method* method, DvmDex* methodClassDex)
{
    Method* absMethod;
    Method* methodToCall;
    int i, vtableIndex;

    /*
     * Resolve the method.  This gives us the abstract method from the
     * interface class declaration.
     */
    absMethod = dvmDexGetResolvedMethod(methodClassDex, methodIdx);
    if (absMethod == NULL) {
        absMethod = dvmResolveInterfaceMethod(method->clazz, methodIdx);
        if (absMethod == NULL) {
            ALOGV("+ unknown method");
            return NULL;
        }
    }

    /* make sure absMethod->methodIndex means what we think it means */
    assert(dvmIsAbstractMethod(absMethod));

    /*
     * Run through the "this" object's iftable.  Find the entry for
     * absMethod's class, then use absMethod->methodIndex to find
     * the method's entry.  The value there is the offset into our
     * vtable of the actual method to execute.
     *
     * The verifier does not guarantee that objects stored into
     * interface references actually implement the interface, so this
     * check cannot be eliminated.
     */
    for (i = 0; i < thisClass->iftableCount; i++) {
        if (thisClass->iftable[i].clazz == absMethod->clazz)
            break;
    }
    if (i == thisClass->iftableCount) {
        /* impossible in verified DEX, need to check for it in unverified */
        dvmThrowIncompatibleClassChangeError("interface not implemented");
        return NULL;
    }

    assert(absMethod->methodIndex <
        thisClass->iftable[i].clazz->virtualMethodCount);

    vtableIndex =
        thisClass->iftable[i].methodIndexArray[absMethod->methodIndex];
    assert(vtableIndex >= 0 && vtableIndex < thisClass->vtableCount);
    methodToCall = thisClass->vtable[vtableIndex];

#if 0
    /* this can happen when there's a stale class file */
    if (dvmIsAbstractMethod(methodToCall)) {
        dvmThrowAbstractMethodError("interface method not implemented");
        return NULL;
    }
#else
    assert(!dvmIsAbstractMethod(methodToCall) ||
        methodToCall->nativeFunc != NULL);
#endif

    LOGVV("+++ interface=%s.%s concrete=%s.%s",
        absMethod->clazz->descriptor, absMethod->name,
        methodToCall->clazz->descriptor, methodToCall->name);
    assert(methodToCall != NULL);

    return methodToCall;
}



/*
 * Helpers for dvmThrowVerificationError().
 *
 * Each returns a newly-allocated string.
 */
#define kThrowShow_accessFromClass     1
static std::string classNameFromIndex(const Method* method, int ref,
    VerifyErrorRefType refType, int flags)
{
    const DvmDex* pDvmDex = method->clazz->pDvmDex;
    if (refType == VERIFY_ERROR_REF_FIELD) {
        /* get class ID from field ID */
        const DexFieldId* pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
        ref = pFieldId->classIdx;
    } else if (refType == VERIFY_ERROR_REF_METHOD) {
        /* get class ID from method ID */
        const DexMethodId* pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
        ref = pMethodId->classIdx;
    }

    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, ref);
    std::string dotClassName(dvmHumanReadableDescriptor(className));
    if (flags == 0) {
        return dotClassName;
    }

    std::string result;
    if ((flags & kThrowShow_accessFromClass) != 0) {
        result += "tried to access class " + dotClassName;
        result += " from class " + dvmHumanReadableDescriptor(method->clazz->descriptor);
    } else {
        assert(false);      // should've been caught above
    }

    return result;
}
static std::string fieldNameFromIndex(const Method* method, int ref,
    VerifyErrorRefType refType, int flags)
{
    if (refType != VERIFY_ERROR_REF_FIELD) {
        ALOGW("Expected ref type %d, got %d", VERIFY_ERROR_REF_FIELD, refType);
        return NULL;    /* no message */
    }

    const DvmDex* pDvmDex = method->clazz->pDvmDex;
    const DexFieldId* pFieldId = dexGetFieldId(pDvmDex->pDexFile, ref);
    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, pFieldId->classIdx);
    const char* fieldName = dexStringById(pDvmDex->pDexFile, pFieldId->nameIdx);

    std::string dotName(dvmHumanReadableDescriptor(className));

    if ((flags & kThrowShow_accessFromClass) != 0) {
        std::string result;
        result += "tried to access field ";
        result += dotName + "." + fieldName;
        result += " from class ";
        result += dvmHumanReadableDescriptor(method->clazz->descriptor);
        return result;
    }
    return dotName + "." + fieldName;
}
static std::string methodNameFromIndex(const Method* method, int ref,
    VerifyErrorRefType refType, int flags)
{
    if (refType != VERIFY_ERROR_REF_METHOD) {
        ALOGW("Expected ref type %d, got %d", VERIFY_ERROR_REF_METHOD,refType);
        return NULL;    /* no message */
    }

    const DvmDex* pDvmDex = method->clazz->pDvmDex;
    const DexMethodId* pMethodId = dexGetMethodId(pDvmDex->pDexFile, ref);
    const char* className = dexStringByTypeIdx(pDvmDex->pDexFile, pMethodId->classIdx);
    const char* methodName = dexStringById(pDvmDex->pDexFile, pMethodId->nameIdx);

    std::string dotName(dvmHumanReadableDescriptor(className));

    if ((flags & kThrowShow_accessFromClass) != 0) {
        char* desc = dexProtoCopyMethodDescriptor(&method->prototype);
        std::string result;
        result += "tried to access method ";
        result += dotName + "." + methodName + ":" + desc;
        result += " from class " + dvmHumanReadableDescriptor(method->clazz->descriptor);
        free(desc);
        return result;
    }
    return dotName + "." + methodName;
}

/*
 * Throw an exception for a problem identified by the verifier.
 *
 * This is used by the invoke-verification-error instruction.  It always
 * throws an exception.
 *
 * "kind" indicates the kind of failure encountered by the verifier.  It
 * has two parts, an error code and an indication of the reference type.
 */
void dvmThrowVerificationError(const Method* method, int kind, int ref)
{
    int errorPart = kind & ~(0xff << kVerifyErrorRefTypeShift);
    int errorRefPart = kind >> kVerifyErrorRefTypeShift;
    VerifyError errorKind = static_cast<VerifyError>(errorPart);
    VerifyErrorRefType refType = static_cast<VerifyErrorRefType>(errorRefPart);
    ClassObject* exceptionClass = gDvm.exVerifyError;
    std::string msg;

    switch ((VerifyError) errorKind) {
    case VERIFY_ERROR_NO_CLASS:
        exceptionClass = gDvm.exNoClassDefFoundError;
        msg = classNameFromIndex(method, ref, refType, 0);
        break;
    case VERIFY_ERROR_NO_FIELD:
        exceptionClass = gDvm.exNoSuchFieldError;
        msg = fieldNameFromIndex(method, ref, refType, 0);
        break;
    case VERIFY_ERROR_NO_METHOD:
        exceptionClass = gDvm.exNoSuchMethodError;
        msg = methodNameFromIndex(method, ref, refType, 0);
        break;
    case VERIFY_ERROR_ACCESS_CLASS:
        exceptionClass = gDvm.exIllegalAccessError;
        msg = classNameFromIndex(method, ref, refType,
            kThrowShow_accessFromClass);
        break;
    case VERIFY_ERROR_ACCESS_FIELD:
        exceptionClass = gDvm.exIllegalAccessError;
        msg = fieldNameFromIndex(method, ref, refType,
            kThrowShow_accessFromClass);
        break;
    case VERIFY_ERROR_ACCESS_METHOD:
        exceptionClass = gDvm.exIllegalAccessError;
        msg = methodNameFromIndex(method, ref, refType,
            kThrowShow_accessFromClass);
        break;
    case VERIFY_ERROR_CLASS_CHANGE:
        exceptionClass = gDvm.exIncompatibleClassChangeError;
        msg = classNameFromIndex(method, ref, refType, 0);
        break;
    case VERIFY_ERROR_INSTANTIATION:
        exceptionClass = gDvm.exInstantiationError;
        msg = classNameFromIndex(method, ref, refType, 0);
        break;

    case VERIFY_ERROR_GENERIC:
        /* generic VerifyError; use default exception, no message */
        break;
    case VERIFY_ERROR_NONE:
        /* should never happen; use default exception */
        assert(false);
        msg = strdup("weird - no error specified");
        break;

    /* no default clause -- want warning if enum updated */
    }

    dvmThrowException(exceptionClass, msg.c_str());
}

/*
 * Update interpBreak for a single thread.
 */
void updateInterpBreak(Thread* thread, ExecutionSubModes subMode, bool enable)
{
    InterpBreak oldValue, newValue;
    do {
        oldValue = newValue = thread->interpBreak;
        newValue.ctl.breakFlags = kInterpNoBreak;  // Assume full reset
        if (enable)
            newValue.ctl.subMode |= subMode;
        else
            newValue.ctl.subMode &= ~subMode;
        if (newValue.ctl.subMode & SINGLESTEP_BREAK_MASK)
            newValue.ctl.breakFlags |= kInterpSingleStep;
        if (newValue.ctl.subMode & SAFEPOINT_BREAK_MASK)
            newValue.ctl.breakFlags |= kInterpSafePoint;
        newValue.ctl.curHandlerTable = (newValue.ctl.breakFlags) ?
            thread->altHandlerTable : thread->mainHandlerTable;
    } while (dvmQuasiAtomicCas64(oldValue.all, newValue.all,
             &thread->interpBreak.all) != 0);
}

/*
 * Update interpBreak for all threads.
 */
void updateAllInterpBreak(ExecutionSubModes subMode, bool enable)
{
    Thread* self = dvmThreadSelf();
    Thread* thread;

    dvmLockThreadList(self);
    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
        updateInterpBreak(thread, subMode, enable);
    }
    dvmUnlockThreadList();
}

/*
 * Update the normal and debugger suspend counts for a thread.
 * threadSuspendCount must be acquired before calling this to
 * ensure a clean update of suspendCount, dbgSuspendCount and
 * sumThreadSuspendCount.
 *
 * CLEANUP TODO: Currently only the JIT is using sumThreadSuspendCount.
 * Move under WITH_JIT ifdefs.
*/
void dvmAddToSuspendCounts(Thread* thread, int delta, int dbgDelta)
{
    thread->suspendCount += delta;
    thread->dbgSuspendCount += dbgDelta;
    updateInterpBreak(thread, kSubModeSuspendPending,
                      (thread->suspendCount != 0));
    // Update the global suspend count total
    gDvm.sumThreadSuspendCount += delta;
}


void dvmDisableSubMode(Thread* thread, ExecutionSubModes subMode)
{
    updateInterpBreak(thread, subMode, false);
}

void dvmEnableSubMode(Thread* thread, ExecutionSubModes subMode)
{
    updateInterpBreak(thread, subMode, true);
}

void dvmEnableAllSubMode(ExecutionSubModes subMode)
{
    updateAllInterpBreak(subMode, true);
}

void dvmDisableAllSubMode(ExecutionSubModes subMode)
{
    updateAllInterpBreak(subMode, false);
}

/*
 * Do a sanity check on interpreter state saved to Thread.
 * A failure here doesn't necessarily mean that something is wrong,
 * so this code should only be used during development to suggest
 * a possible problem.
 */
void dvmCheckInterpStateConsistency()
{
    Thread* self = dvmThreadSelf();
    Thread* thread;
    uint8_t breakFlags;
    uint8_t subMode;
    void* handlerTable;

    dvmLockThreadList(self);
    breakFlags = self->interpBreak.ctl.breakFlags;
    subMode = self->interpBreak.ctl.subMode;
    handlerTable = self->interpBreak.ctl.curHandlerTable;
    for (thread = gDvm.threadList; thread != NULL; thread = thread->next) {
        if (subMode != thread->interpBreak.ctl.subMode) {
            ALOGD("Warning: subMode mismatch - %#x:%#x, tid[%d]",
                subMode,thread->interpBreak.ctl.subMode,thread->threadId);
         }
        if (breakFlags != thread->interpBreak.ctl.breakFlags) {
            ALOGD("Warning: breakFlags mismatch - %#x:%#x, tid[%d]",
                breakFlags,thread->interpBreak.ctl.breakFlags,thread->threadId);
         }
        if (handlerTable != thread->interpBreak.ctl.curHandlerTable) {
            ALOGD("Warning: curHandlerTable mismatch - %#x:%#x, tid[%d]",
                (int)handlerTable,(int)thread->interpBreak.ctl.curHandlerTable,
                thread->threadId);
         }
#if defined(WITH_JIT)
         if (thread->pJitProfTable != gDvmJit.pProfTable) {
             ALOGD("Warning: pJitProfTable mismatch - %#x:%#x, tid[%d]",
                  (int)thread->pJitProfTable,(int)gDvmJit.pProfTable,
                  thread->threadId);
         }
         if (thread->jitThreshold != gDvmJit.threshold) {
             ALOGD("Warning: jitThreshold mismatch - %#x:%#x, tid[%d]",
                  (int)thread->jitThreshold,(int)gDvmJit.threshold,
                  thread->threadId);
         }
#endif
    }
    dvmUnlockThreadList();
}

/*
 * Arm a safepoint callback for a thread.  If funct is null,
 * clear any pending callback.
 * TODO: only gc is currently using this feature, and will have
 * at most a single outstanding callback request.  Until we need
 * something more capable and flexible, enforce this limit.
 */
void dvmArmSafePointCallback(Thread* thread, SafePointCallback funct,
                             void* arg)
{
    dvmLockMutex(&thread->callbackMutex);
    if ((funct == NULL) || (thread->callback == NULL)) {
        thread->callback = funct;
        thread->callbackArg = arg;
        if (funct != NULL) {
            dvmEnableSubMode(thread, kSubModeCallbackPending);
        } else {
            dvmDisableSubMode(thread, kSubModeCallbackPending);
        }
    } else {
        // Already armed.  Different?
        if ((funct != thread->callback) ||
            (arg != thread->callbackArg)) {
            // Yes - report failure and die
            ALOGE("ArmSafePointCallback failed, thread %d", thread->threadId);
            dvmUnlockMutex(&thread->callbackMutex);
            dvmAbort();
        }
    }
    dvmUnlockMutex(&thread->callbackMutex);
}

/*
 * One-time initialization at thread creation.  Here we initialize
 * useful constants.
 */
void dvmInitInterpreterState(Thread* self)
{
#if defined(WITH_JIT)
    /*
     * Reserve a static entity here to quickly setup runtime contents as
     * gcc will issue block copy instructions.
     */
    static struct JitToInterpEntries jitToInterpEntries = {
        dvmJitToInterpNormal,
        dvmJitToInterpNoChain,
        dvmJitToInterpPunt,
        dvmJitToInterpSingleStep,
        dvmJitToInterpTraceSelect,
#if defined(WITH_SELF_VERIFICATION)
        dvmJitToInterpBackwardBranch,
#else
        NULL,
#endif
    };
#endif

    // Begin initialization
    self->cardTable = gDvm.biasedCardTableBase;
#if defined(WITH_JIT)
    // One-time initializations
    self->jitToInterpEntries = jitToInterpEntries;
    self->icRechainCount = PREDICTED_CHAIN_COUNTER_RECHAIN;
    self->pProfileCountdown = &gDvmJit.profileCountdown;
    // Jit state that can change
    dvmJitUpdateThreadStateSingle(self);
#endif
    dvmInitializeInterpBreak(self);
}

/*
 * For a newly-created thread, we need to start off with interpBreak
 * set to any existing global modes.  The caller must hold the
 * thread list lock.
 */
void dvmInitializeInterpBreak(Thread* thread)
{
    if (gDvm.instructionCountEnableCount > 0) {
        dvmEnableSubMode(thread, kSubModeInstCounting);
    }
    if (dvmIsMethodTraceActive()) {
        dvmEnableSubMode(thread, kSubModeMethodTrace);
    }
    if (gDvm.emulatorTraceEnableCount > 0) {
        dvmEnableSubMode(thread, kSubModeEmulatorTrace);
    }
    if (gDvm.debuggerActive) {
        dvmEnableSubMode(thread, kSubModeDebuggerActive);
    }
#if 0
    // Debugging stress mode - force checkBefore
    dvmEnableSubMode(thread, kSubModeCheckAlways);
#endif
}

/*
 * Inter-instruction handler invoked in between instruction interpretations
 * to handle exceptional events such as debugging housekeeping, instruction
 * count profiling, JIT trace building, etc.  Dalvik PC has been exported
 * prior to call, but Thread copy of dPC & fp are not current.
 */
void dvmCheckBefore(const u2 *pc, u4 *fp, Thread* self)
{
    const Method* method = self->interpSave.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 (/*self->interpBreak.ctl.subMode & kSubModeDebuggerActive &&*/
            strcmp(method->clazz->descriptor, cd) == 0 &&
            strcmp(method->name, mn) == 0 &&
            strcmp(method->shorty, sg) == 0)
        {
            ALOGW("Reached %s.%s, enabling verbose mode",
                method->clazz->descriptor, method->name);
            android_setMinPriority(LOG_TAG"i", ANDROID_LOG_VERBOSE);
            dumpRegs(method, fp, true);
        }

        if (!gDvm.debuggerActive)
            *pIsMethodEntry = false;
    }
#endif

    /* Safe point handling */
    if (self->suspendCount ||
        (self->interpBreak.ctl.subMode & kSubModeCallbackPending)) {
        // Are we are a safe point?
        int flags;
        flags = dexGetFlagsFromOpcode(dexOpcodeFromCodeUnit(*pc));
        if (flags & (VERIFY_GC_INST_MASK & ~kInstrCanThrow)) {
            // Yes, at a safe point.  Pending callback?
            if (self->interpBreak.ctl.subMode & kSubModeCallbackPending) {
                SafePointCallback callback;
                void* arg;
                // Get consistent funct/arg pair
                dvmLockMutex(&self->callbackMutex);
                callback = self->callback;
                arg = self->callbackArg;
                dvmUnlockMutex(&self->callbackMutex);
                // Update Thread structure
                self->interpSave.pc = pc;
                self->interpSave.curFrame = fp;
                if (callback != NULL) {
                    // Do the callback
                    if (!callback(self,arg)) {
                        // disarm
                        dvmArmSafePointCallback(self, NULL, NULL);
                    }
                }
            }
            // Need to suspend?
            if (self->suspendCount) {
                dvmExportPC(pc, fp);
                dvmCheckSuspendPending(self);
            }
        }
    }

    if (self->interpBreak.ctl.subMode & kSubModeDebuggerActive) {
        updateDebugger(method, pc, fp, 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).
         */
        gDvm.executedInstrCounts[GET_OPCODE(*pc)]++;
    }


#if defined(WITH_TRACKREF_CHECKS)
    dvmInterpCheckTrackedRefs(self, method,
                              self->interpSave.debugTrackedRefStart);
#endif

#if defined(WITH_JIT)
    // Does the JIT need anything done now?
    if (self->interpBreak.ctl.subMode &
            (kSubModeJitTraceBuild | kSubModeJitSV)) {
        // Are we building a trace?
        if (self->interpBreak.ctl.subMode & kSubModeJitTraceBuild) {
            dvmCheckJit(pc, self);
        }

#if defined(WITH_SELF_VERIFICATION)
        // Are we replaying a trace?
        if (self->interpBreak.ctl.subMode & kSubModeJitSV) {
            dvmCheckSelfVerification(pc, self);
        }
#endif
    }
#endif

    /*
     * CountedStep processing.  NOTE: must be the last here to allow
     * preceeding special case handler to manipulate single-step count.
     */
    if (self->interpBreak.ctl.subMode & kSubModeCountedStep) {
        if (self->singleStepCount == 0) {
            // We've exhausted our single step count
            dvmDisableSubMode(self, kSubModeCountedStep);
#if defined(WITH_JIT)
#if 0
            /*
             * For debugging.  If jitResumeDPC is non-zero, then
             * we expect to return to a trace in progress.   There
             * are valid reasons why we wouldn't (such as an exception
             * throw), but here we can keep track.
             */
            if (self->jitResumeDPC != NULL) {
                if (self->jitResumeDPC == pc) {
                    if (self->jitResumeNPC != NULL) {
                        ALOGD("SS return to trace - pc:%#x to 0x:%x",
                             (int)pc, (int)self->jitResumeNPC);
                    } else {
                        ALOGD("SS return to interp - pc:%#x",(int)pc);
                    }
                } else {
                    ALOGD("SS failed to return.  Expected %#x, now at %#x",
                         (int)self->jitResumeDPC, (int)pc);
                }
            }
#endif
#if 0
            // TODO - fix JIT single-stepping resume mode (b/5551114)
            // self->jitResumeNPC needs to be cleared in callPrep

            // If we've got a native return and no other reasons to
            // remain in singlestep/break mode, do a long jump
            if (self->jitResumeNPC != NULL &&
                self->interpBreak.ctl.breakFlags == 0) {
                assert(self->jitResumeDPC == pc);
                self->jitResumeDPC = NULL;
                dvmJitResumeTranslation(self, pc, fp);
                // Doesn't return
                dvmAbort();
            }
            // In case resume is blocked by non-zero breakFlags, clear
            // jitResumeNPC here.
            self->jitResumeNPC = NULL;
            self->jitResumeDPC = NULL;
            self->inJitCodeCache = NULL;
#endif
#endif
        } else {
            self->singleStepCount--;
#if defined(WITH_JIT)
            if ((self->singleStepCount > 0) && (self->jitResumeNPC != NULL)) {
                /*
                 * Direct return to an existing translation following a
                 * single step is valid only if we step once.  If we're
                 * here, an additional step was added so we need to invalidate
                 * the return to translation.
                 */
                self->jitResumeNPC = NULL;
                self->inJitCodeCache = NULL;
            }
#endif
        }
    }
}

/*
 * Main interpreter loop entry point.
 *
 * This begins executing code at the start of "method".  On exit, "pResult"
 * holds the return value of the method (or, if "method" returns NULL, it
 * holds an undefined value).
 *
 * The interpreted stack frame, which holds the method arguments, has
 * already been set up.
 */
void dvmInterpret(Thread* self, const Method* method, JValue* pResult)
{
    InterpSaveState interpSaveState;
    ExecutionSubModes savedSubModes;

#if defined(WITH_JIT)
    /* Target-specific save/restore */
    double calleeSave[JIT_CALLEE_SAVE_DOUBLE_COUNT];
    /*
     * If the previous VM left the code cache through single-stepping the
     * inJitCodeCache flag will be set when the VM is re-entered (for example,
     * in self-verification mode we single-step NEW_INSTANCE which may re-enter
     * the VM through findClassFromLoaderNoInit). Because of that, we cannot
     * assert that self->inJitCodeCache is NULL here.
     */
#endif

    /*
     * Save interpreter state from previous activation, linking
     * new to last.
     */
    interpSaveState = self->interpSave;
    self->interpSave.prev = &interpSaveState;
    /*
     * Strip out and save any flags that should not be inherited by
     * nested interpreter activation.
     */
    savedSubModes = (ExecutionSubModes)(
              self->interpBreak.ctl.subMode & LOCAL_SUBMODE);
    if (savedSubModes != kSubModeNormal) {
        dvmDisableSubMode(self, savedSubModes);
    }
#if defined(WITH_JIT)
    dvmJitCalleeSave(calleeSave);
#endif


#if defined(WITH_TRACKREF_CHECKS)
    self->interpSave.debugTrackedRefStart =
        dvmReferenceTableEntries(&self->internalLocalRefTable);
#endif
    self->debugIsMethodEntry = true;
#if defined(WITH_JIT)
    dvmJitCalleeSave(calleeSave);
    /* Initialize the state to kJitNot */
    self->jitState = kJitNot;
#endif

    /*
     * Initialize working state.
     *
     * No need to initialize "retval".
     */
    self->interpSave.method = method;
    self->interpSave.curFrame = (u4*) self->interpSave.curFrame;
    self->interpSave.pc = method->insns;

    assert(!dvmIsNativeMethod(method));

    /*
     * Make sure the class is ready to go.  Shouldn't be possible to get
     * here otherwise.
     */
    if (method->clazz->status < CLASS_INITIALIZING ||
        method->clazz->status == CLASS_ERROR)
    {
        ALOGE("ERROR: tried to execute code in unprepared class '%s' (%d)",
            method->clazz->descriptor, method->clazz->status);
        dvmDumpThread(self, false);
        dvmAbort();
    }

    typedef void (*Interpreter)(Thread*);
    Interpreter stdInterp;
    if (gDvm.executionMode == kExecutionModeInterpFast)
        stdInterp = dvmMterpStd;
#if defined(WITH_JIT)
    else if (gDvm.executionMode == kExecutionModeJit)
        stdInterp = dvmMterpStd;
#endif
    else
        stdInterp = dvmInterpretPortable;

    // Call the interpreter
    (*stdInterp)(self);

    *pResult = self->interpSave.retval;

    /* Restore interpreter state from previous activation */
    self->interpSave = interpSaveState;
#if defined(WITH_JIT)
    dvmJitCalleeRestore(calleeSave);
#endif
    if (savedSubModes != kSubModeNormal) {
        dvmEnableSubMode(self, savedSubModes);
    }
}