"External allocation" tracking for direct buffers.

The native memory allocated for ByteBuffer.allocateDirect was not being
tracked by the VM's "external allocation" mechanism.  This adds the
necessary calls to the Harmony OSMemory allocator, which is what
actually calls malloc().

The external alloc stuff just takes size values, so we tuck a copy of
the allocation size into the allocated block, and pull it out when it's
time to free the memory.

(Includes in a couple of other changes that I had to commit somewhere so
I could sync.)
diff --git a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp
index 5bd7907..3f7b0b1 100644
--- a/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp
+++ b/libcore/luni/src/main/native/org_apache_harmony_luni_platform_OSMemory.cpp
@@ -14,14 +14,26 @@
  * limitations under the License.
  */
 
+#define LOG_TAG "OSMemory"
 #include "JNIHelp.h"
 #include "AndroidSystemNatives.h"
 #include "utils/misc.h"
+#include "utils/Log.h"
 #include <sys/mman.h>
 #include <stdlib.h>
 #include <string.h>
 #include <errno.h>
 
+/*
+ * Cached dalvik.system.VMRuntime pieces.
+ */
+static struct {
+    jmethodID method_trackExternalAllocation;
+    jmethodID method_trackExternalFree;
+
+    jobject runtimeInstance;
+} gIDCache;
+
 #undef MMAP_READ_ONLY
 #define MMAP_READ_ONLY 1L
 #undef MMAP_READ_WRITE
@@ -55,11 +67,28 @@
  * Signature: (I)I
  */
 static jint harmony_nio_mallocImpl(JNIEnv *_env, jobject _this, jint size) {
-    void *returnValue = malloc(size);
-    if(returnValue == NULL) {
-        jniThrowException(_env, "java.lang.OutOfMemoryError", "");
+    jboolean allowed = _env->CallBooleanMethod(gIDCache.runtimeInstance,
+        gIDCache.method_trackExternalAllocation, (jlong) size);
+    if (!allowed) {
+        LOGW("External allocation of %d bytes was rejected\n", size);
+        jniThrowException(_env, "java/lang/OutOfMemoryError", NULL);
+        return 0;
     }
-    return (jint)returnValue;
+
+    LOGV("OSMemory alloc %d\n", size);
+    void *returnValue = malloc(size + sizeof(jlong));
+    if (returnValue == NULL) {
+        jniThrowException(_env, "java/lang/OutOfMemoryError", NULL);
+        return 0;
+    }
+
+    /*
+     * Tuck a copy of the size at the head of the buffer.  We need this
+     * so harmony_nio_freeImpl() knows how much memory is being freed.
+     */
+    jlong* adjptr = (jlong*) returnValue;
+    *adjptr++ = size;
+    return (jint)adjptr;
 }
 
 /*
@@ -68,7 +97,12 @@
  * Signature: (I)V
  */
 static void harmony_nio_freeImpl(JNIEnv *_env, jobject _this, jint pointer) {
-    free((void *)pointer);
+    jlong* adjptr = (jlong*) pointer;
+    jint size = *--adjptr;
+    LOGV("OSMemory free %d\n", size);
+    _env->CallVoidMethod(gIDCache.runtimeInstance,
+        gIDCache.method_trackExternalFree, (jlong) size);
+    free((void *)adjptr);
 }
 
 /*
@@ -577,6 +611,46 @@
     { "flushImpl",          "(IJ)I",   (void*) harmony_nio_flushImpl }
 };
 int register_org_apache_harmony_luni_platform_OSMemory(JNIEnv *_env) {
-    return jniRegisterNativeMethods(_env, "org/apache/harmony/luni/platform/OSMemory",
+    /*
+     * We need to call VMRuntime.trackExternal{Allocation,Free}.  Cache
+     * method IDs and a reference to the singleton.
+     */
+    static const char* kVMRuntimeName = "dalvik/system/VMRuntime";
+    jmethodID method_getRuntime;
+    jclass clazz;
+
+    clazz = _env->FindClass(kVMRuntimeName);
+    if (clazz == NULL) {
+        LOGE("Unable to find class %s\n", kVMRuntimeName);
+        return -1;
+    }
+    gIDCache.method_trackExternalAllocation = _env->GetMethodID(clazz,
+        "trackExternalAllocation", "(J)Z");
+    gIDCache.method_trackExternalFree = _env->GetMethodID(clazz,
+        "trackExternalFree", "(J)V");
+    method_getRuntime = _env->GetStaticMethodID(clazz,
+        "getRuntime", "()Ldalvik/system/VMRuntime;");
+
+    if (gIDCache.method_trackExternalAllocation == NULL ||
+        gIDCache.method_trackExternalFree == NULL ||
+        method_getRuntime == NULL)
+    {
+        LOGE("Unable to find VMRuntime methods\n");
+        return -1;
+    }
+
+    jobject instance = _env->CallStaticObjectMethod(clazz, method_getRuntime);
+    if (instance == NULL) {
+        LOGE("Unable to obtain VMRuntime instance\n");
+        return -1;
+    }
+    gIDCache.runtimeInstance = _env->NewGlobalRef(instance);
+
+    /*
+     * Register methods.
+     */
+    return jniRegisterNativeMethods(_env,
+                "org/apache/harmony/luni/platform/OSMemory",
                 gMethods, NELEM(gMethods));
 }
+
diff --git a/libnativehelper/include/nativehelper/JNIHelp.h b/libnativehelper/include/nativehelper/JNIHelp.h
index eec7af8..3982797 100644
--- a/libnativehelper/include/nativehelper/JNIHelp.h
+++ b/libnativehelper/include/nativehelper/JNIHelp.h
@@ -42,6 +42,8 @@
 
 /*
  * Throw an exception with the specified class and an optional message.
+ * The "className" argument will be passed directly to FindClass, which
+ * takes strings with slashes (e.g. "java/lang/Object").
  *
  * Returns 0 on success, nonzero if something failed (e.g. the exception
  * class couldn't be found).
diff --git a/vm/Thread.c b/vm/Thread.c
index ae81230..ad7c4b6 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -530,8 +530,8 @@
                  * Could be that a resume-all is in progress, and something
                  * grabbed the CPU when the wakeup was broadcast.  The thread
                  * performing the resume hasn't had a chance to release the
-                 * thread suspend lock.  (Should no longer be an issue --
-                 * we now release before broadcast.)
+                 * thread suspend lock.  (We release before the broadcast,
+                 * so this should be a narrow window.)
                  *
                  * Could be we hit the window as a suspend was started,
                  * and the lock has been grabbed but the suspend counts
@@ -622,6 +622,7 @@
 
         LOGI("threadid=%d: killing leftover daemon threadid=%d [TODO]\n",
             self->threadId, target->threadId);
+        LOGI("             name='%s'\n", dvmGetThreadName(target));
         // TODO: suspend and/or kill the thread
         // (at the very least, we can "rescind their JNI privileges")