Merge pi-platform-release to stage-aosp-master - DO NOT MERGE

Change-Id: Ie7e1c8de0bdc6b28d19ed92570216ce2d18488c3
diff --git a/src/share/back/VirtualMachineImpl.c b/src/share/back/VirtualMachineImpl.c
index dfcdfb2..fde53c3 100644
--- a/src/share/back/VirtualMachineImpl.c
+++ b/src/share/back/VirtualMachineImpl.c
@@ -55,6 +55,12 @@
     if (vmVersion == NULL) {
         vmVersion = "<unknown>";
     }
+    // ANDROID-CHANGED: The runtime value of the java.version property has always been "0" on
+    //                  android but the old debugger just sent a different value. Simply sending "0"
+    //                  can confuse some JDWP clients so we will simply say that we are version "8".
+    if (strcmp(gdata->property_java_vm_name, "Dalvik") == 0 && strcmp(vmVersion, "0") == 0) {
+      vmVersion = "8";
+    }
     vmName = gdata->property_java_vm_name;
     if (vmName == NULL) {
         vmName = "<unknown>";
diff --git a/src/share/back/classTrack.c b/src/share/back/classTrack.c
index aea139a..64a0733 100644
--- a/src/share/back/classTrack.c
+++ b/src/share/back/classTrack.c
@@ -113,7 +113,7 @@
 static void JNICALL
 cbTrackingObjectFree(jvmtiEnv* jvmti_env, jlong tag)
 {
-    debugMonitorEnter(deletedTagLock);
+    debugMonitorEnterNoSuspend(deletedTagLock);
     *(jlong*)bagAdd(deletedTagBag) = tag;
     debugMonitorExit(deletedTagLock);
 }
diff --git a/src/share/back/commonRef.c b/src/share/back/commonRef.c
index 259d4d8..f00766f 100644
--- a/src/share/back/commonRef.c
+++ b/src/share/back/commonRef.c
@@ -30,23 +30,31 @@
 #include "util.h"
 #include "commonRef.h"
 
-#define ALL_REFS -1
-
 /*
+ * ANDROID-CHANGED: This was modified for android to avoid any use of weak
+ * global (jweak) references. On Android hosts the number of jweak
+ * references active at any one time is limited. By using jweaks to keep
+ * track of objects here we could hit the jweak limit on some very large
+ * apps. The implementation is compatible with any JVMTI implementation
+ * that provides the 'can_tag_objects' and
+ * 'can_generate_object_free_events' capabilities. This works by watching
+ * for the ObjectFree events on tagged objects and storing them in a list
+ * of things that have been deleted.
+ *
  * Each object sent to the front end is tracked with the RefNode struct
  * (see util.h).
  * External to this module, objects are identified by a jlong id which is
- * simply the sequence number. A weak reference is usually used so that
+ * simply the sequence number. A JVMTI tag is usually used so that
  * the presence of a debugger-tracked object will not prevent
  * its collection. Once an object is collected, its RefNode may be
- * deleted and the weak ref inside may be reused (these may happen in
- * either order). Using the sequence number
+ * deleted (these may happen in * either order). Using the sequence number
  * as the object id prevents ambiguity in the object id when the weak ref
  * is reused. The RefNode* is stored with the object as it's JVMTI Tag.
+ * This tag also provides the weak-reference behavior.
  *
- * The ref member is changed from weak to strong when
- * gc of the object is to be prevented.
- * Whether or not it is strong, it is never exported from this module.
+ * The ref member is changed from weak to strong when gc of the object is
+ * to be prevented. Whether or not it is strong, it is never exported
+ * from this module.
  *
  * A reference count of each jobject is also maintained here. It tracks
  * the number times an object has been referenced through
@@ -54,9 +62,9 @@
  * count is decremented to 0 (with commonRef_release*), even if the
  * corresponding object has not been collected.
  *
- * One hash table is maintained. The mapping of ID to jobject (or RefNode*)
- * is handled with one hash table that will re-size itself as the number
- * of RefNode's grow.
+ * One hash table is maintained. The mapping of ID to RefNode* is handled
+ * with one hash table that will re-size itself as the number of RefNode's
+ * grow.
  */
 
 /* Initial hash table size (must be power of 2) */
@@ -82,38 +90,97 @@
     return gdata->nextSeqNum++;
 }
 
-/* Create a fresh RefNode structure, create a weak ref and tag the object */
+/* ANDROID-CHANGED: This helper function is unique to android.
+ * This function gets a local-ref to object the node is pointing to. If the node's object has been
+ * collected it will return NULL. The caller is responsible for calling env->DeleteLocalRef or
+ * env->PopLocalFrame to clean up the reference. This function makes no changes to the passed in
+ * node.
+ */
+static jobject
+getLocalRef(JNIEnv *env, const RefNode* node) {
+    if (node->isStrong) {
+        return JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
+    }
+    jint count = -1;
+    jobject *objects = NULL;
+    jlong tag = ptr_to_jlong(node);
+    jvmtiError error = JVMTI_FUNC_PTR(gdata->jvmti,GetObjectsWithTags)
+            (gdata->jvmti, 1, &tag, &count, &objects, NULL);
+    if (error != JVMTI_ERROR_NONE) {
+        EXIT_ERROR(error,"GetObjectsWithTags");
+    }
+    if (count != 1 && count != 0) {
+        EXIT_ERROR(AGENT_ERROR_INTERNAL,
+                   "GetObjectsWithTags returned multiple objects unexpectedly");
+    }
+    jobject res = (count == 0) ? NULL : objects[0];
+    JVMTI_FUNC_PTR(gdata->jvmti,Deallocate)(gdata->jvmti,(unsigned char*)objects);
+    return res;
+}
+
+/* ANDROID-CHANGED: Handler function for objects being freed. */
+void commonRef_handleFreedObject(jlong tag) {
+    RefNode* node = (RefNode*)jlong_to_ptr(tag);
+    debugMonitorEnterNoSuspend(gdata->refLock); {
+        // Delete the node and remove it from the hashmap.
+        // If we raced with a deleteNode call and lost the next and prev will be null but we will
+        // not be at the start of the bucket. This is fine.
+        jint slot = hashBucket(node->seqNum);
+        if (node->next != NULL ||
+                node->prev != NULL ||
+                gdata->objectsByID[slot] == node) {
+            /* Detach from id hash table */
+            if (node->prev == NULL) {
+                gdata->objectsByID[slot] = node->next;
+            } else {
+                node->prev->next = node->next;
+            }
+            /* Also fixup back links. */
+            if (node->next != NULL) {
+                node->next->prev = node->prev;
+            }
+            gdata->objectsByIDcount--;
+        }
+        jvmtiDeallocate(node);
+    } debugMonitorExit(gdata->refLock);
+}
+
+/* Create a fresh RefNode structure, and tag the object (creating a weak-ref to it).
+ * ANDROID-CHANGED: The definition of RefNode was changed slightly so that node->ref is only for
+ * a strong reference. For weak references we use the node as a tag on the object to keep track if
+ * it.
+ * ANDROID-CHANGED: ref must be a local-reference held live for the duration of this method until it
+ * is fully in the objectByID map.
+ */
 static RefNode *
 createNode(JNIEnv *env, jobject ref)
 {
     RefNode   *node;
-    jobject    weakRef;
     jvmtiError error;
 
+    if (ref == NULL) {
+        return NULL;
+    }
+
     /* Could allocate RefNode's in blocks, not sure it would help much */
     node = (RefNode*)jvmtiAllocate((int)sizeof(RefNode));
     if (node == NULL) {
         return NULL;
     }
 
-    /* Create weak reference to make sure we have a reference */
-    weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, ref);
-    if (weakRef == NULL) {
-        jvmtiDeallocate(node);
-        return NULL;
-    }
-
-    /* Set tag on weakRef */
-    error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)
-                          (gdata->jvmti, weakRef, ptr_to_jlong(node));
+    /* ANDROID-CHANGED: Use local reference to make sure we have a reference. We will use this
+     * reference to set a tag to the node to use as a weak-reference and keep track of the ref.
+     * ANDROID-CHANGED: Set node tag on the ref. This tag now functions as the weak-reference to the
+     * object.
+     */
+    error = JVMTI_FUNC_PTR(gdata->jvmti, SetTag)(gdata->jvmti, ref, ptr_to_jlong(node));
     if ( error != JVMTI_ERROR_NONE ) {
-        JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, weakRef);
         jvmtiDeallocate(node);
         return NULL;
     }
 
     /* Fill in RefNode */
-    node->ref      = weakRef;
+    node->ref      = NULL;
     node->isStrong = JNI_FALSE;
     node->count    = 1;
     node->seqNum   = newSeqNum();
@@ -127,20 +194,41 @@
 static void
 deleteNode(JNIEnv *env, RefNode *node)
 {
-    LOG_MISC(("Freeing %d (%x)\n", (int)node->seqNum, node->ref));
+    /* ANDROID-CHANGED: use getLocalRef to get a local reference to the node. */
+    WITH_LOCAL_REFS(env, 1) {
+        jobject localRef = getLocalRef(env, node);
+        LOG_MISC(("Freeing %d\n", (int)node->seqNum));
 
-    if ( node->ref != NULL ) {
-        /* Clear tag */
-        (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
-                            (gdata->jvmti, node->ref, NULL_OBJECT_ID);
-        if (node->isStrong) {
-            JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
+        /* Detach from id hash table */
+        if (node->prev == NULL) {
+            gdata->objectsByID[hashBucket(node->seqNum)] = node->next;
         } else {
-            JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
+            node->prev->next = node->next;
         }
-    }
-    gdata->objectsByIDcount--;
-    jvmtiDeallocate(node);
+        /* Also fixup back links. */
+        if (node->next != NULL) {
+            node->next->prev = node->prev;
+        }
+
+        // If we don't get the localref that means the ObjectFree event is being called and the
+        // node will be deleted there.
+        if ( localRef != NULL ) {
+            /* Clear tag */
+            (void)JVMTI_FUNC_PTR(gdata->jvmti,SetTag)
+                                (gdata->jvmti, localRef, NULL_OBJECT_ID);
+            if (node->isStrong) {
+                JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
+            }
+
+            jvmtiDeallocate(node);
+        } else {
+            // We are going to let the object-free do the final work. Mark this node as not in the
+            // list with both null links but not in the bucket.
+            node->prev = NULL;
+            node->next = NULL;
+        }
+        gdata->objectsByIDcount--;
+    } END_WITH_LOCAL_REFS(env);
 }
 
 /* Change a RefNode to have a strong reference */
@@ -148,44 +236,35 @@
 strengthenNode(JNIEnv *env, RefNode *node)
 {
     if (!node->isStrong) {
-        jobject strongRef;
-
-        strongRef = JNI_FUNC_PTR(env,NewGlobalRef)(env, node->ref);
-        /*
-         * NewGlobalRef on a weak ref will return NULL if the weak
-         * reference has been collected or if out of memory.
-         * We need to distinguish those two occurrences.
-         */
-        if ((strongRef == NULL) && !isSameObject(env, node->ref, NULL)) {
-            EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
-        }
-        if (strongRef != NULL) {
-            JNI_FUNC_PTR(env,DeleteWeakGlobalRef)(env, node->ref);
-            node->ref      = strongRef;
-            node->isStrong = JNI_TRUE;
-        }
-        return strongRef;
-    } else {
-        return node->ref;
+        /* ANDROID-CHANGED: We need to find and fill in the node->ref when we strengthen a node. */
+        WITH_LOCAL_REFS(env, 1) {
+            /* getLocalRef will return NULL if the referent has been collected. */
+            jobject localRef = getLocalRef(env, node);
+            if (localRef != NULL) {
+                node->ref = JNI_FUNC_PTR(env,NewGlobalRef)(env, localRef);
+                if (node->ref == NULL) {
+                    EXIT_ERROR(AGENT_ERROR_NULL_POINTER,"NewGlobalRef");
+                }
+                node->isStrong = JNI_TRUE;
+            }
+        } END_WITH_LOCAL_REFS(env);
     }
+    return node->ref;
 }
 
-/* Change a RefNode to have a weak reference */
-static jweak
+/* Change a RefNode to have a weak reference
+ * ANDROID-CHANGED: This is done by deleting the strong reference. We already have a tag in
+ * to the node from when we created the node. Since this is never removed we can simply delete the
+ * global ref, reset node->isStrong & node->ref, and return. Since no part of this can fail we can
+ * change this function to be void too.
+ */
+static void
 weakenNode(JNIEnv *env, RefNode *node)
 {
     if (node->isStrong) {
-        jweak weakRef;
-
-        weakRef = JNI_FUNC_PTR(env,NewWeakGlobalRef)(env, node->ref);
-        if (weakRef != NULL) {
-            JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
-            node->ref      = weakRef;
-            node->isStrong = JNI_FALSE;
-        }
-        return weakRef;
-    } else {
-        return node->ref;
+        JNI_FUNC_PTR(env,DeleteGlobalRef)(env, node->ref);
+        node->ref      = NULL;
+        node->isStrong = JNI_FALSE;
     }
 }
 
@@ -216,36 +295,25 @@
 static void
 deleteNodeByID(JNIEnv *env, jlong id, jint refCount)
 {
+    /* ANDROID-CHANGED: Rewrite for double-linked list. Also remove ALL_REFS since it's not needed
+     * since the free-callback will do the work of cleaning up when an object gets collected. */
     jint     slot;
     RefNode *node;
-    RefNode *prev;
 
     slot = hashBucket(id);
     node = gdata->objectsByID[slot];
-    prev = NULL;
 
     while (node != NULL) {
         if (id == node->seqNum) {
-            if (refCount != ALL_REFS) {
-                node->count -= refCount;
-            } else {
-                node->count = 0;
-            }
+            node->count -= refCount;
             if (node->count <= 0) {
                 if ( node->count < 0 ) {
                     EXIT_ERROR(AGENT_ERROR_INTERNAL,"RefNode count < 0");
                 }
-                /* Detach from id hash table */
-                if (prev == NULL) {
-                    gdata->objectsByID[slot] = node->next;
-                } else {
-                    prev->next = node->next;
-                }
                 deleteNode(env, node);
             }
             break;
         }
-        prev = node;
         node = node->next;
     }
 }
@@ -263,20 +331,22 @@
 static RefNode *
 findNodeByID(JNIEnv *env, jlong id)
 {
+    /* ANDROID-CHANGED: Rewrite for double-linked list */
     jint     slot;
     RefNode *node;
-    RefNode *prev;
 
     slot = hashBucket(id);
     node = gdata->objectsByID[slot];
-    prev = NULL;
 
     while (node != NULL) {
         if ( id == node->seqNum ) {
-            if ( prev != NULL ) {
+            if ( node->prev != NULL ) {
                 /* Re-order hash list so this one is up front */
-                prev->next = node->next;
+                node->prev->next = node->next;
+                node->prev->prev = node->prev;
                 node->next = gdata->objectsByID[slot];
+                node->next->prev = node;
+                node->prev = NULL;
                 gdata->objectsByID[slot] = node;
             }
             break;
@@ -302,15 +372,21 @@
 static void
 hashIn(RefNode *node)
 {
+    /* ANDROID-CHANGED: Modify for double-linked list */
     jint     slot;
 
     /* Add to id hashtable */
     slot                     = hashBucket(node->seqNum);
     node->next               = gdata->objectsByID[slot];
+    node->prev               = NULL;
+    if (node->next != NULL) {
+        node->next->prev     = node;
+    }
     gdata->objectsByID[slot] = node;
 }
 
-/* Allocate and add RefNode to hash table */
+/* Allocate and add RefNode to hash table
+ * ANDROID-CHANGED: Requires that ref be a held-live local ref.*/
 static RefNode *
 newCommonRef(JNIEnv *env, jobject ref)
 {
@@ -378,13 +454,8 @@
         for (i = 0; i < gdata->objectsByIDsize; i++) {
             RefNode *node;
 
-            node = gdata->objectsByID[i];
-            while (node != NULL) {
-                RefNode *next;
-
-                next = node->next;
+            for (node = gdata->objectsByID[i]; node != NULL; node = gdata->objectsByID[i]) {
                 deleteNode(env, node);
-                node = next;
             }
             gdata->objectsByID[i] = NULL;
         }
@@ -417,10 +488,12 @@
 
         node = findNodeByRef(env, ref);
         if (node == NULL) {
-            node = newCommonRef(env, ref);
-            if ( node != NULL ) {
-                id = node->seqNum;
-            }
+            WITH_LOCAL_REFS(env, 1) {
+                node = newCommonRef(env, JNI_FUNC_PTR(env,NewLocalRef)(env, ref));
+                if ( node != NULL ) {
+                    id = node->seqNum;
+                }
+            } END_WITH_LOCAL_REFS(env);
         } else {
             id = node->seqNum;
             node->count++;
@@ -451,14 +524,20 @@
             } else {
                 jobject lref;
 
-                lref = JNI_FUNC_PTR(env,NewLocalRef)(env, node->ref);
-                if ( lref == NULL ) {
-                    /* Object was GC'd shortly after we found the node */
-                    deleteNodeByID(env, node->seqNum, ALL_REFS);
-                } else {
-                    saveGlobalRef(env, node->ref, &ref);
+                /* ANDROID-CHANGED: Use getLocalRef helper to get a local-reference to the object
+                 * this node weakly points to. It will return NULL if the object has been GCd
+                 */
+                lref = getLocalRef(env, node);
+                if ( lref != NULL ) {
+                    /* ANDROID-CHANGED: Use lref to save the global ref since that is the only real
+                     * jobject we have.
+                     */
+                    saveGlobalRef(env, lref, &ref);
                     JNI_FUNC_PTR(env,DeleteLocalRef)(env, lref);
                 }
+                /* ANDROID-CHANGED: Otherwise the object was GC'd shortly after we found the node.
+                 * The free callback will deal with cleanup once we return.
+                 */
             }
         }
     } debugMonitorExit(gdata->refLock);
@@ -501,9 +580,9 @@
             if (strongRef == NULL) {
                 /*
                  * Referent has been collected, clean up now.
+                 * ANDROID-CHANGED: The node will be cleaned up by the object-free callback.
                  */
                 error = AGENT_ERROR_INVALID_OBJECT;
-                deleteNodeByID(env, id, ALL_REFS);
             }
         }
     } debugMonitorExit(gdata->refLock);
@@ -524,12 +603,8 @@
         env  = getEnv();
         node = findNodeByID(env, id);
         if (node != NULL) {
-            jweak weakRef;
-
-            weakRef = weakenNode(env, node);
-            if (weakRef == NULL) {
-                error = AGENT_ERROR_OUT_OF_MEMORY;
-            }
+            // ANDROID-CHANGED: weakenNode was changed to never fail.
+            weakenNode(env, node);
         }
     } debugMonitorExit(gdata->refLock);
     return error;
@@ -556,44 +631,7 @@
 void
 commonRef_compact(void)
 {
-    JNIEnv  *env;
-    RefNode *node;
-    RefNode *prev;
-    int      i;
-
-    env = getEnv();
-    debugMonitorEnter(gdata->refLock); {
-        if ( gdata->objectsByIDsize > 0 ) {
-            /*
-             * Walk through the id-based hash table. Detach any nodes
-             * for which the ref has been collected.
-             */
-            for (i = 0; i < gdata->objectsByIDsize; i++) {
-                node = gdata->objectsByID[i];
-                prev = NULL;
-                while (node != NULL) {
-                    /* Has the object been collected? */
-                    if ( (!node->isStrong) &&
-                          isSameObject(env, node->ref, NULL)) {
-                        RefNode *freed;
-
-                        /* Detach from the ID list */
-                        if (prev == NULL) {
-                            gdata->objectsByID[i] = node->next;
-                        } else {
-                            prev->next = node->next;
-                        }
-                        freed = node;
-                        node = node->next;
-                        deleteNode(env, freed);
-                    } else {
-                        prev = node;
-                        node = node->next;
-                    }
-                }
-            }
-        }
-    } debugMonitorExit(gdata->refLock);
+    // NO-OP.
 }
 
 /* Lock the commonRef tables */
diff --git a/src/share/back/commonRef.h b/src/share/back/commonRef.h
index 7c76667..b84ca96 100644
--- a/src/share/back/commonRef.h
+++ b/src/share/back/commonRef.h
@@ -38,6 +38,9 @@
 void commonRef_release(JNIEnv *env, jlong id);
 void commonRef_compact(void);
 
+/* ANDROID-CHANGED: Called when an object is freed. This is called without any synchronization. */
+void commonRef_handleFreedObject(jlong tag);
+
 void commonRef_lock(void);
 void commonRef_unlock(void);
 
diff --git a/src/share/back/debugInit.c b/src/share/back/debugInit.c
index a11a9bc..66d5282 100644
--- a/src/share/back/debugInit.c
+++ b/src/share/back/debugInit.c
@@ -89,6 +89,8 @@
 static jboolean suspendOnInit = JNI_TRUE;   /* suspend all app threads after init */
 static jboolean dopause = JNI_FALSE;        /* pause for debugger attach */
 static jboolean docoredump = JNI_FALSE;     /* core dump on exit */
+/* ANDROID-CHANGED: Added directlog option */
+static jboolean directlog = JNI_FALSE;      /* Don't add pid to logfile. */
 static char *logfile = NULL;                /* Name of logfile (if logging) */
 static unsigned logflags = 0;               /* Log flags */
 
@@ -202,10 +204,11 @@
            minor_runtime >= minor_compiletime;
 }
 
-// ANDROID-CHANGED: Function to get and set the com.android.art.internal.ddm.process_chunk extension
-// function. This returns JNI_ERR if something went wrong with searching. If the extension is not
-// found we return JNI_OK and don't bother updating the gdata pointer.
-static jint find_ddm_process_chunk()
+// ANDROID-CHANGED: Function to get and set the com.android.art.internal.ddm.process_chunk and
+// com.android.art.concurrent.raw_monitor_enter_no_suspend extension functions. This returns JNI_ERR
+// if something went wrong with searching. If the extension is not found we return JNI_OK and don't
+// bother updating the gdata pointer.
+static jint find_extension_functions()
 {
     jvmtiError error;
     jvmtiExtensionFunctionInfo* extension_info;
@@ -228,6 +231,10 @@
         if (strcmp("com.android.art.internal.ddm.process_chunk", extension_info[i].id) == 0) {
             gdata->ddm_process_chunk = (DdmProcessChunk) extension_info[i].func;
         }
+        if (strcmp("com.android.art.concurrent.raw_monitor_enter_no_suspend",
+                   extension_info[i].id) == 0) {
+            gdata->raw_monitor_enter_no_suspend = (RawMonitorEnterNoSuspend) extension_info[i].func;
+        }
         jvmtiDeallocate(extension_info[i].id);
         jvmtiDeallocate(extension_info[i].short_description);
         for (j = 0; j < extension_info[i].param_count; j++) {
@@ -382,6 +389,8 @@
     needed_capabilities.can_maintain_original_method_order      = 1;
     needed_capabilities.can_generate_monitor_events             = 1;
     needed_capabilities.can_tag_objects                         = 1;
+    /* ANDROID-CHANGED: Needed for how we implement commonRef tracking */
+    needed_capabilities.can_generate_object_free_events         = 1;
 
     /* And what potential ones that would be nice to have */
     needed_capabilities.can_force_early_return
@@ -459,7 +468,7 @@
     }
 
     // ANDROID-CHANGED: Find com.android.art.internal.ddm.process_chunk function if it exists.
-    if (find_ddm_process_chunk() != JNI_OK) {
+    if (find_extension_functions() != JNI_OK || gdata->raw_monitor_enter_no_suspend == NULL) {
         ERROR_MESSAGE(("Fatal error while attempting to find the "
                        "com.android.art.internal.ddm.process_chunk extension function"));
         return JNI_ERR;
@@ -1003,6 +1012,8 @@
  "pause=y|n                    pause to debug PID                n\n"
  "coredump=y|n                 coredump at exit                  n\n"
  "errorexit=y|n                exit on any error                 n\n"
+ /* ANDROID-CHANGED: Added directlog */
+ "directlog                    do not add pid to name of logfile n\n"
  "logfile=filename             name of log file                  none\n"
  "logflags=flags               log flags (bitmask)               none\n"
  "                               JVM calls     = 0x001\n"
@@ -1108,6 +1119,8 @@
     /* Set defaults */
     gdata->assertOn     = DEFAULT_ASSERT_ON;
     gdata->assertFatal  = DEFAULT_ASSERT_FATAL;
+    /* ANDROID-CHANGED: Add directlog */
+    directlog           = JNI_FALSE;
     logfile             = DEFAULT_LOGFILE;
     // ANDROID-CHANGED: By default we assume ddms is off initially.
     gdata->ddmInitiallyActive = JNI_FALSE;
@@ -1263,6 +1276,12 @@
         } else if (strcmp(buf, "precrash") == 0) {
             errmsg = "The precrash option removed, use -XX:OnError";
             goto bad_option_with_errmsg;
+        } else if (strcmp(buf, "directlog") == 0) {
+            /* ANDROID-CHANGED: Added directlog */
+            /*LINTED*/
+            if ( !get_boolean(&str, &directlog) ) {
+                goto syntax_error;
+            }
         } else if (strcmp(buf, "logfile") == 0) {
             /*LINTED*/
             if (!get_tok(&str, current, (int)(end - current), ',')) {
@@ -1324,7 +1343,8 @@
 
     /* Setup logging now */
     if ( logfile!=NULL ) {
-        setup_logging(logfile, logflags);
+        /* ANDROID-CHANGED: Add directlog */
+        setup_logging(logfile, logflags, directlog);
         (void)atexit(&atexit_finish_logging);
     }
 
diff --git a/src/share/back/eventHandler.c b/src/share/back/eventHandler.c
index 5f5bf24..25da37c 100644
--- a/src/share/back/eventHandler.c
+++ b/src/share/back/eventHandler.c
@@ -743,6 +743,16 @@
     return clazz;
 }
 
+/* ANDROID-CHANGED: Android keeps track of object unloads by watching this event instead of looking
+ * through jweaks since there are a limited number of those. This does not cause any corresponding
+ * jdwp event and is merely passed on to the commonRef system.
+ */
+static void JNICALL
+cbObjectFree(jvmtiEnv* jvmti_env, jlong tag)
+{
+  commonRef_handleFreedObject(tag);
+}
+
 /* Event callback for JVMTI_EVENT_SINGLE_STEP */
 static void JNICALL
 cbSingleStep(jvmtiEnv *jvmti_env, JNIEnv *env,
@@ -883,6 +893,13 @@
 cbClassPrepare(jvmtiEnv *jvmti_env, JNIEnv *env,
                         jthread thread, jclass klass)
 {
+    /* ANDROID-CHANGED: b/111394423 Android sends ClassPrepare events for arrays too. We don't
+     * really care about these though and they can cause deadlocks since they may be sent on jit
+     * threads so just ignore them.
+     */
+    if (isArrayClass(klass)) {
+      return;
+    }
     EventInfo info;
 
     LOG_CB(("cbClassPrepare: thread=%p", thread));
@@ -912,6 +929,13 @@
 cbClassLoad(jvmtiEnv *jvmti_env, JNIEnv *env,
                         jthread thread, jclass klass)
 {
+    /* ANDROID-CHANGED: b/111394423 Android sends ClassLoad events for arrays too. We don't really
+     * care about these though and they can cause deadlocks since they may be sent on jit threads so
+     * just ignore them.
+     */
+    if (isArrayClass(klass)) {
+      return;
+    }
     EventInfo info;
 
     LOG_CB(("cbClassLoad: thread=%p", thread));
@@ -1485,8 +1509,16 @@
     if (error != JVMTI_ERROR_NONE) {
         EXIT_ERROR(error,"Can't enable garbage collection finish events");
     }
+    /* ANDROID-CHANGED: Permanently enable object free for common-ref tracking */
+    error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventNotificationMode)
+                (gdata->jvmti, JVMTI_ENABLE, JVMTI_EVENT_OBJECT_FREE, NULL);
+    if (error != JVMTI_ERROR_NONE) {
+        EXIT_ERROR(error,"Can't enable object free events");
+    }
 
     (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks));
+    /* ANDROID-CHANGED: Event callback for common-ref tracking */
+    gdata->callbacks.ObjectFree                 = &cbObjectFree;
     /* Event callback for JVMTI_EVENT_SINGLE_STEP */
     gdata->callbacks.SingleStep                 = &cbSingleStep;
     /* Event callback for JVMTI_EVENT_BREAKPOINT */
diff --git a/src/share/back/log_messages.c b/src/share/back/log_messages.c
index 32bea29..78b21c2 100644
--- a/src/share/back/log_messages.c
+++ b/src/share/back/log_messages.c
@@ -202,8 +202,9 @@
 #endif
 
 /* Set up the logging with the name of a logging file. */
+/* ANDROID-CHANGED: Added directlog */
 void
-setup_logging(const char *filename, unsigned flags)
+setup_logging(const char *filename, unsigned flags, int directlog)
 {
 #ifdef JDWP_LOGGING
     FILE *fp = NULL;
@@ -217,9 +218,14 @@
         return;
 
     /* Create potential filename for logging */
-    processPid = GETPID();
-    (void)snprintf(logging_filename, sizeof(logging_filename),
-                    "%s.%d", filename, (int)processPid);
+    /* ANDROID-CHANGED: Add directlog */
+    if (directlog) {
+      strncpy(logging_filename, filename, sizeof(logging_filename));
+    } else {
+      processPid = GETPID();
+      (void)snprintf(logging_filename, sizeof(logging_filename),
+                      "%s.%d", filename, (int)processPid);
+    }
 
     /* Turn on logging (do this last) */
     logging = 1;
diff --git a/src/share/back/log_messages.h b/src/share/back/log_messages.h
index 129fab6..23833bf 100644
--- a/src/share/back/log_messages.h
+++ b/src/share/back/log_messages.h
@@ -28,7 +28,8 @@
 
 /* LOG: Must be called like:  LOG_category(("anything")) or LOG_category((format,args)) */
 
-void setup_logging(const char *, unsigned);
+/* ANDROID-CHANGED: Added directlog argument */
+void setup_logging(const char *, unsigned, int);
 void finish_logging();
 
 #define LOG_NULL ((void)0)
diff --git a/src/share/back/util.c b/src/share/back/util.c
index 060c452..dcd195d 100644
--- a/src/share/back/util.c
+++ b/src/share/back/util.c
@@ -40,9 +40,11 @@
 
 /* Forward declarations */
 static jboolean isInterface(jclass clazz);
-static jboolean isArrayClass(jclass clazz);
 static char * getPropertyUTF8(JNIEnv *env, char *propertyName);
 
+static jvmtiError (JNICALL *ext_RawMonitorEnterNoSuspend) (jvmtiEnv* env, jrawMonitorID monitor);
+static jvmtiError (JNICALL *ext_RawMonitorExitNoSuspend) (jvmtiEnv* env, jrawMonitorID monitor);
+
 // ANDROID-CHANGED: Implement a helper to get the current time in milliseconds according to
 // CLOCK_MONOTONIC.
 jlong
@@ -1093,6 +1095,24 @@
     }
 }
 
+/* ANDROID-CHANGED: Add suspension ignoring raw-monitor enter. */
+void debugMonitorEnterNoSuspend(jrawMonitorID monitor)
+{
+    jvmtiError error;
+    while (JNI_TRUE) {
+        error = FUNC_PTR(&gdata,raw_monitor_enter_no_suspend)(gdata->jvmti, monitor);
+        error = ignore_vm_death(error);
+        if (error == JVMTI_ERROR_INTERRUPT) {
+            handleInterrupt();
+        } else {
+            break;
+        }
+    }
+    if (error != JVMTI_ERROR_NONE) {
+        EXIT_ERROR(error, "on raw monitor enter no suspend");
+    }
+}
+
 void
 debugMonitorWait(jrawMonitorID monitor)
 {
@@ -1312,7 +1332,8 @@
     return status;
 }
 
-static jboolean
+/* ANDROID-CHANGED: Make isArrayClass public */
+jboolean
 isArrayClass(jclass clazz)
 {
     jboolean isArray = JNI_FALSE;
diff --git a/src/share/back/util.h b/src/share/back/util.h
index dde7495..b02ad7b 100644
--- a/src/share/back/util.h
+++ b/src/share/back/util.h
@@ -56,13 +56,20 @@
 /* Get access to Native Platform Toolkit functions */
 #include "npt.h"
 
+/* ANDROID-CHANGED: We want to avoid allocating jweaks on android so if !isStrong we will use the
+ * node-pointer tag as the weak-reference.
+ */
 /* Definition of a CommonRef tracked by the backend for the frontend */
 typedef struct RefNode {
     jlong        seqNum;        /* ID of reference, also key for hash table */
-    jobject      ref;           /* could be strong or weak */
+    jobject      ref;           /* ANDROID-CHANGED: Always the strong reference if isStrong, NULL
+                                 * otherwise.
+                                 */
     struct RefNode *next;       /* next RefNode* in bucket chain */
+    struct RefNode *prev;       /* ANDROID-CHANGED: Previous RefNode* in bucket chain. Used to allow
+                                 * us to remove arbitrary elements. */
     jint         count;         /* count of references */
-    unsigned     isStrong : 1;  /* 1 means this is a string reference */
+    unsigned     isStrong : 1;  /* 1 means this is a strong reference */
 } RefNode;
 
 /* Value of a NULL ID */
@@ -82,6 +89,7 @@
                                       jint* type_out,
                                       jint* length_out,
                                       jbyte** data_out);
+typedef jvmtiError (*RawMonitorEnterNoSuspend)(jvmtiEnv* env, jrawMonitorID mon);
 
 typedef struct {
     jvmtiEnv *jvmti;
@@ -147,6 +155,7 @@
 
      /* ANDROID-CHANGED: com.android.art.internal.ddm.process_chunk extension function */
      DdmProcessChunk ddm_process_chunk;
+     RawMonitorEnterNoSuspend raw_monitor_enter_no_suspend;
 
      /* ANDROID-CHANGED: Need to keep track of if ddm is initially active. */
      jboolean ddmInitiallyActive;
@@ -380,6 +389,12 @@
 jrawMonitorID debugMonitorCreate(char *name);
 void debugMonitorEnter(jrawMonitorID theLock);
 void debugMonitorExit(jrawMonitorID theLock);
+
+/* ANDROID-CHANGED: extension functions that will enter and exit a mutex without allowing suspension
+ * to occur. Caller must not use monitor-wait.
+ */
+void debugMonitorEnterNoSuspend(jrawMonitorID theLock);
+
 void debugMonitorWait(jrawMonitorID theLock);
 void debugMonitorTimedWait(jrawMonitorID theLock, jlong millis);
 void debugMonitorNotify(jrawMonitorID theLock);
@@ -390,6 +405,9 @@
 
 void threadGroupInfo(jthreadGroup, jvmtiThreadGroupInfo *info);
 
+/* ANDROID-CHANGED: Add isArrayClass */
+jboolean isArrayClass(jclass);
+
 char *getClassname(jclass);
 jvmtiError classSignature(jclass, char**, char**);
 jint classStatus(jclass);