am 770379e1: am 6efd4463: Fix a deadlock in the breakpoint code.

Merge commit '770379e17e694ec6e08f1edb690a5e33ab81c684' into kraken

* commit '770379e17e694ec6e08f1edb690a5e33ab81c684':
  Fix a deadlock in the breakpoint code.
diff --git a/vm/analysis/DexVerify.c b/vm/analysis/DexVerify.c
index c490691..088309c 100644
--- a/vm/analysis/DexVerify.c
+++ b/vm/analysis/DexVerify.c
@@ -150,66 +150,6 @@
     return true;
 }
 
-/*
- * Temporarily "undo" any breakpoints found in this method.  There is no risk
- * of confusing the interpreter, because unverified code cannot be executed.
- *
- * Breakpoints can be set after a class is loaded but before it has been
- * verified.
- *
- * The "breakpoint" opcode can replace any other opcode, leaving no
- * indication of the original instruction's width or purpose in the
- * instruction stream.  We either have to quietly undo the breakpoints
- * before verification, or look up the original opcode whenever we need it.
- * The latter is more efficient since we only slow down on code that
- * actually has breakpoints, but it requires explicit handling in every
- * function that examines the instruction stream.
- *
- * We need to ensure that the debugger doesn't insert any additional
- * breakpoints while we work.  This either requires holding a lock on the
- * breakpoint set throughout the verification of this method, or adding a
- * "do not touch anything on these pages" list to the set.  Either way,
- * the caller of this method must ensure that it calls "redo" to release
- * state.
- *
- * A debugger could connect while we work, so we return without doing
- * anything if a debugger doesn't happen to be connected now.  We can only
- * avoid doing work if the debugger thread isn't running (dexopt, zygote,
- * or debugging not configured).
- *
- * Returns "false" if we did nothing, "true" if we did stuff (and, hence,
- * need to call "redo" at some point).
- */
-static bool undoBreakpoints(Method* meth)
-{
-#ifdef WITH_DEBUGGER
-    if (gDvm.optimizing || gDvm.zygote || !gDvm.jdwpConfigured)
-        return false;
-    dvmUndoBreakpoints(meth);
-    return true;
-#else
-    return false;
-#endif
-}
-
-/*
- * Restore any breakpoints we undid previously.  Also has to update the
- * stored "original opcode" value for any instruction that we replaced
- * with a throw-verification-error op.
- */
-static void redoBreakpoints(Method* meth)
-{
-#ifdef WITH_DEBUGGER
-    if (gDvm.optimizing || gDvm.zygote || !gDvm.jdwpConfigured) {
-        /* should not be here */
-        assert(false);
-        return;
-    }
-    dvmRedoBreakpoints(meth);
-#else
-    assert(false);
-#endif
-}
 
 /*
  * Perform verification on a single method.
@@ -238,9 +178,6 @@
     UninitInstanceMap* uninitMap = NULL;
     InsnFlags* insnFlags = NULL;
     int i, newInstanceCount;
-    bool undidBreakpoints;
-
-    undidBreakpoints = undoBreakpoints(meth);
 
     /*
      * If there aren't any instructions, make sure that's expected, then
@@ -322,8 +259,6 @@
     result = true;
 
 bail:
-    if (undidBreakpoints)
-        redoBreakpoints(meth);
     dvmFreeUninitInstanceMap(uninitMap);
     free(insnFlags);
     return result;
diff --git a/vm/interp/Interp.c b/vm/interp/Interp.c
index 4401520..002dfb1 100644
--- a/vm/interp/Interp.c
+++ b/vm/interp/Interp.c
@@ -71,6 +71,7 @@
  * We only remove the breakpoint when the last instance is cleared.
  */
 typedef struct {
+    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 */
@@ -116,10 +117,20 @@
 
 /*
  * 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)
 {
-    dvmLockMutex(&pSet->lock);
+    if (dvmTryLockMutex(&pSet->lock) != 0) {
+        Thread* self = dvmThreadSelf();
+        int oldStatus = dvmChangeStatus(self, THREAD_VMWAIT);
+        dvmLockMutex(&pSet->lock);
+        dvmChangeStatus(self, oldStatus);
+    }
 }
 
 /*
@@ -212,6 +223,7 @@
         }
 
         pBreak = &pSet->breakpoints[pSet->count++];
+        pBreak->method = method;
         pBreak->addr = (u2*)addr;
         pBreak->originalOpCode = *(u1*)addr;
         pBreak->setCount = 1;
@@ -219,16 +231,35 @@
         /*
          * 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 relevant breakpoints when
+         * verification completes.
          */
         MEM_BARRIER();
         assert(*(u1*)addr != OP_BREAKPOINT);
-        dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr, OP_BREAKPOINT);
+        if (dvmIsClassVerified(method->clazz)) {
+            LOGV("Class %s verified, adding breakpoint at %p\n",
+                method->clazz->descriptor, addr);
+            dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)addr, OP_BREAKPOINT);
+        } else {
+            LOGV("Class %s NOT verified, deferring breakpoint at %p\n",
+                method->clazz->descriptor, addr);
+        }
     } else {
         pBreak = &pSet->breakpoints[idx];
         pBreak->setCount++;
 
-        /* verify instruction stream has break op */
-        assert(*(u1*)addr == OP_BREAKPOINT);
+        /*
+         * Instruction stream may not have breakpoint opcode yet -- flush
+         * may be pending during verification of class.
+         */
+        //assert(*(u1*)addr == OP_BREAKPOINT);
     }
 
     return true;
@@ -262,6 +293,11 @@
         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);
@@ -282,58 +318,30 @@
 }
 
 /*
- * Restore the original opcode on any breakpoints that are in the specified
- * method.  The breakpoints are NOT removed from the set.
+ * 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 dvmBreakpointSetUndo(BreakpointSet* pSet, Method* method)
+static void dvmBreakpointSetFlush(BreakpointSet* pSet, ClassObject* clazz)
 {
-    const u2* start = method->insns;
-    const u2* end = method->insns + dvmGetMethodInsnsSize(method);
-
     int i;
     for (i = 0; i < pSet->count; i++) {
         Breakpoint* pBreak = &pSet->breakpoints[i];
-        if (pBreak->addr >= start && pBreak->addr < end) {
-            LOGV("UNDO %s.%s [%d]\n",
-                method->clazz->descriptor, method->name, i);
-            dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)pBreak->addr,
-                pBreak->originalOpCode);
+        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.
+             */
+            LOGV("Flushing breakpoint at %p for %s\n",
+                pBreak->addr, clazz->descriptor);
+            dvmDexChangeDex1(clazz->pDvmDex, (u1*)pBreak->addr, OP_BREAKPOINT);
         }
     }
 }
-
-/*
- * Put the breakpoint opcode back into the instruction stream, and check
- * to see if the original opcode has changed.
- *
- * The BreakpointSet's lock must be acquired before calling here.
- */
-static void dvmBreakpointSetRedo(BreakpointSet* pSet, Method* method)
-{
-    const u2* start = method->insns;
-    const u2* end = method->insns + dvmGetMethodInsnsSize(method);
-
-    int i;
-    for (i = 0; i < pSet->count; i++) {
-        Breakpoint* pBreak = &pSet->breakpoints[i];
-        if (pBreak->addr >= start && pBreak->addr < end) {
-            LOGV("REDO %s.%s [%d]\n",
-                method->clazz->descriptor, method->name, i);
-            u1 currentOpCode = *(u1*)pBreak->addr;
-            if (pBreak->originalOpCode != currentOpCode) {
-                /* verifier can drop in a throw-verification-error */
-                LOGD("NOTE: updating originalOpCode from 0x%02x to 0x%02x\n",
-                    pBreak->originalOpCode, currentOpCode);
-                pBreak->originalOpCode = currentOpCode;
-            }
-            dvmDexChangeDex1(method->clazz->pDvmDex, (u1*)pBreak->addr,
-                OP_BREAKPOINT);
-        }
-    }
-}
-
 #endif /*WITH_DEBUGGER*/
 
 
@@ -428,32 +436,22 @@
 }
 
 /*
- * Temporarily "undo" any breakpoints set in a specific method.  Used
- * during verification.
+ * Flush any breakpoints associated with methods in "clazz".
  *
- * Locks the breakpoint set, and leaves it locked.
+ * 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 dvmUndoBreakpoints(Method* method)
+void dvmFlushBreakpoints(ClassObject* clazz)
 {
     BreakpointSet* pSet = gDvm.breakpointSet;
 
+    if (pSet == NULL)
+        return;
+
+    assert(dvmIsClassVerified(clazz));
     dvmBreakpointSetLock(pSet);
-    dvmBreakpointSetUndo(pSet, method);
-    /* lock remains held */
-}
-
-/*
- * "Redo" the breakpoints cleared by a previous "undo", re-inserting the
- * breakpoint opcodes and updating the "original opcode" values.
- *
- * Unlocks the breakpoint set, which must be held by a previous "undo".
- */
-void dvmRedoBreakpoints(Method* method)
-{
-    BreakpointSet* pSet = gDvm.breakpointSet;
-
-    /* lock already held */
-    dvmBreakpointSetRedo(pSet, method);
+    dvmBreakpointSetFlush(pSet, clazz);
     dvmBreakpointSetUnlock(pSet);
 }
 #endif
diff --git a/vm/interp/Interp.h b/vm/interp/Interp.h
index 015c3db..1964b57 100644
--- a/vm/interp/Interp.h
+++ b/vm/interp/Interp.h
@@ -56,20 +56,9 @@
 u1 dvmGetOriginalOpCode(const u2* addr);
 
 /*
- * Temporarily "undo" any breakpoints set in a specific method.  Used
- * during verification.
- *
- * Locks the breakpoint set, and leaves it locked.
+ * Flush any breakpoints associated with methods in "clazz".
  */
-void dvmUndoBreakpoints(Method* method);
-
-/*
- * "Redo" the breakpoints cleared by a previous "undo", re-inserting the
- * breakpoint opcodes and updating the "original opcode" values.
- *
- * Unlocks the breakpoint set, which must be held by a previous "undo".
- */
-void dvmRedoBreakpoints(Method* method);
+void dvmFlushBreakpoints(ClassObject* clazz);
 #endif
 
 #endif /*_DALVIK_INTERP_INTERP*/
diff --git a/vm/oo/Class.c b/vm/oo/Class.c
index 0b03d94..625a572 100644
--- a/vm/oo/Class.c
+++ b/vm/oo/Class.c
@@ -4286,8 +4286,10 @@
             (gDvm.classVerifyMode == VERIFY_MODE_REMOTE &&
              clazz->classLoader == NULL))
         {
+            /* advance to "verified" state */
             LOGV("+++ not verifying class %s (cl=%p)\n",
                 clazz->descriptor, clazz->classLoader);
+            clazz->status = CLASS_VERIFIED;
             goto noverify;
         }
 
@@ -4321,6 +4323,11 @@
     }
 noverify:
 
+#ifdef WITH_DEBUGGER
+    /* update instruction stream now that the verifier is done */
+    dvmFlushBreakpoints(clazz);
+#endif
+
     if (clazz->status == CLASS_INITIALIZED)
         goto bail_unlock;