resolved conflicts for merge of 9b5264c8 to master
diff --git a/vm/Thread.c b/vm/Thread.c
index edfb49c..c9db9bc 100644
--- a/vm/Thread.c
+++ b/vm/Thread.c
@@ -1314,10 +1314,18 @@
     assert(threadObj != NULL);
 
     if(gDvm.zygote) {
-        dvmThrowException("Ljava/lang/IllegalStateException;",
-            "No new threads in -Xzygote mode");
+        // Allow the sampling profiler thread. We shut it down before forking.
+        StringObject* nameStr = (StringObject*) dvmGetFieldObject(threadObj,
+                    gDvm.offJavaLangThread_name);
+        char* threadName = dvmCreateCstrFromString(nameStr);
+        bool profilerThread = strcmp(threadName, "SamplingProfiler") == 0;
+        free(threadName);
+        if (!profilerThread) {
+            dvmThrowException("Ljava/lang/IllegalStateException;",
+                "No new threads in -Xzygote mode");
 
-        goto fail;
+            goto fail;
+        }
     }
 
     self = dvmThreadSelf();
@@ -2251,6 +2259,11 @@
     dvmUnlockThreadList();
 
     setThreadSelf(NULL);
+
+    if (self->statFile > 0) {
+        close(self->statFile);
+    }
+
     freeThread(self);
 }
 
@@ -3869,3 +3882,115 @@
      */
     gcScanAllThreads();
 }
+
+/* Converts a Linux thread state to a ThreadStatus. */
+static ThreadStatus stateToStatus(char state) {
+    switch (state) {
+        case 'R': return THREAD_RUNNING; // running
+        case 'S': return THREAD_WAIT;    // sleeping in interruptible wait
+        case 'D': return THREAD_WAIT;    // uninterruptible disk sleep
+        case 'Z': return THREAD_ZOMBIE;  // zombie
+        case 'T': return THREAD_WAIT;    // traced or stopped on a signal
+        case 'W': return THREAD_PAGING;  // paging memory
+        default:
+            LOGE("Unexpected state: %c", state);
+            return THREAD_NATIVE;
+    }
+}
+
+/* Reads the state char starting from the beginning of the file. */
+static char readStateFromBeginning(Thread* thread) {
+    char buffer[256];
+    int size = read(thread->statFile, buffer, sizeof(buffer));
+    if (size == 0) {
+        LOGE("EOF trying to read stat file.");
+        return 0;
+    }
+    char* endOfName = (char*) memchr(buffer, ')', sizeof(buffer));
+    if (endOfName == NULL) {
+        LOGE("End of executable name not found.");
+        return 0;
+    }
+    char* state = endOfName + 2;
+    thread->stateOffset = state - buffer;
+    if (*state == 0) {
+        LOGE("State char is 0.");
+    }
+    return *state;
+}
+
+/*
+ * Looks for the state char at the last place we found it. Read from the
+ * beginning if necessary.
+ */
+static char readStateRelatively(Thread* thread) {
+    char buffer[3];
+    // Position file offset at end of executable name.
+    int result = lseek(thread->statFile, thread->stateOffset - 2, SEEK_SET);
+    if (result < 0) {
+        LOGE("lseek() error.");
+        return 0;
+    }
+    int size = read(thread->statFile, buffer, sizeof(buffer));
+    if (size < (int) sizeof(buffer)) {
+        LOGE("EOF trying to read stat file.");
+        return 0;
+    }
+    if (buffer[0] != ')') {
+        // The executable name must have changed.
+        result = lseek(thread->statFile, 0, SEEK_SET);
+        if (result < 0) {
+            LOGE("lseek() error.");
+            return 0;
+        }
+        return readStateFromBeginning(thread);
+    }
+    char state = buffer[2];
+    if (state == 0) {
+        LOGE("State char is 0.");
+    }
+    return state;
+}
+
+ThreadStatus dvmGetNativeThreadStatus(Thread* thread) {
+    // TODO: Add a custom hook so this just reads a char from memory.
+
+    ThreadStatus status = thread->status;
+    if (status != THREAD_NATIVE) {
+        // Return cached status so we don't accidentally return THREAD_NATIVE.
+        return status;
+    }
+
+    if (thread->statFile == -1) {
+        // We tried and failed to open the file earlier. Return current status.
+        return thread->status;
+    }
+
+    // Note: see "man proc" for the format of stat.
+    char state;
+    if (thread->statFile == 0) {
+        // We haven't tried to open the file yet. Do so.
+        char fileName[256];
+        sprintf(fileName, "/proc/self/task/%d/stat", thread->systemTid);
+        thread->statFile = open(fileName, O_RDONLY);
+        if (thread->statFile == NULL) {
+            LOGE("Error opening %s: %s", fileName, strerror(errno));
+            thread->statFile = -1;
+            return thread->status;
+        }
+        state = readStateFromBeginning(thread);
+    } else {
+        state = readStateRelatively(thread);
+    }
+
+    if (state == 0) {
+        close(thread->statFile);
+        thread->statFile = -1;
+        return thread->status;
+    }
+    ThreadStatus nativeStatus = stateToStatus(state);
+
+    // The thread status could have changed from NATIVE.
+    status = thread->status;
+    return status == THREAD_NATIVE ? nativeStatus : status;
+}
diff --git a/vm/Thread.h b/vm/Thread.h
index 669b1a1..402d4c8 100644
--- a/vm/Thread.h
+++ b/vm/Thread.h
@@ -51,6 +51,7 @@
     THREAD_STARTING     = 6,        /* started, not yet on thread list */
     THREAD_NATIVE       = 7,        /* off in a JNI native method */
     THREAD_VMWAIT       = 8,        /* waiting on a VM resource */
+    THREAD_PAGING       = 9,        /* paging memory */
 } ThreadStatus;
 
 /* thread priorities, from java.lang.Thread */
@@ -90,7 +91,7 @@
      * Thread's current status.  Can only be changed by the thread itself
      * (i.e. don't mess with this from other threads).
      */
-    ThreadStatus status;
+    volatile ThreadStatus status;
 
     /*
      * This is the number of times the thread has been suspended.  When the
@@ -219,6 +220,11 @@
     /* Buffer for register state during self verification */
     struct ShadowSpace* shadowSpace;
 #endif
+
+    /* /proc/PID/task/TID/stat */
+    int statFile;
+    /* offset of state char in stat file, last we checked */
+    int stateOffset;
 } Thread;
 
 /* start point for an internal thread; mimics pthread args */
@@ -439,6 +445,13 @@
 void dvmDumpAllThreads(bool grabLock);
 void dvmDumpAllThreadsEx(const DebugOutputTarget* target, bool grabLock);
 
+/*
+ * Reads the native thread status. If the thread is in native code, this
+ * function queries the native thread state and converts it to an equivalent
+ * ThreadStatus. If the native status can't be read, this function returns
+ * THREAD_NATIVE.
+ */
+ThreadStatus dvmGetNativeThreadStatus(Thread* thread);
 
 #ifdef WITH_MONITOR_TRACKING
 /*
diff --git a/vm/native/dalvik_system_SamplingProfiler.c b/vm/native/dalvik_system_SamplingProfiler.c
index 2381ade..71ef490 100644
--- a/vm/native/dalvik_system_SamplingProfiler.c
+++ b/vm/native/dalvik_system_SamplingProfiler.c
@@ -25,10 +25,10 @@
 #include "Dalvik.h"
 #include "native/InternalNativePriv.h"
 
-// ~16k
+// ~20k
 #define INITIAL_CAPACITY 1024
 
-// ~64k
+// ~80k
 #define MAX_CAPACITY 4096
 
 typedef enum {
@@ -43,20 +43,27 @@
 typedef enum {
     /** Executing bytecode. */
     RUNNING_THREAD,
-    /** In a native method. */
-    NATIVE_THREAD,
     /** Waiting on a lock or VM resource. */
     SUSPENDED_THREAD
 } ThreadState;
 
 #define THREAD_STATE_SIZE (SUSPENDED_THREAD + 1)
 
+typedef enum {
+    /** This method is in the call stack. */
+    CALLING_METHOD,
+    /** VM is in this method. */
+    LEAF_METHOD
+} MethodState;
+
+#define METHOD_STATE_SIZE (LEAF_METHOD + 1)
+
 /** SampleSet entry. */
 typedef struct {
     /** Entry key. */
     const Method* method; // 4 bytes
     /** Sample counts for method divided by thread type and state. */
-    u2 counts[THREAD_TYPE_SIZE][THREAD_STATE_SIZE]; // 12 bytes
+    u2 counts[THREAD_TYPE_SIZE][THREAD_STATE_SIZE][METHOD_STATE_SIZE]; // 16B
 } MethodCount;
 
 /**
@@ -143,7 +150,8 @@
 
 /** Increments counter for method in set. */
 static void countMethod(SampleSet* set, const Method* method,
-        ThreadType threadType, ThreadState threadState) {
+        ThreadType threadType, ThreadState threadState,
+        MethodState methodState) {
     MethodCount* entries = set->entries;
     int start = hash(method) & set->mask;
     int i;
@@ -152,7 +160,7 @@
 
         if (entry->method == method) {
             // We found an existing entry.
-            entry->counts[threadType][threadState]++;
+            entry->counts[threadType][threadState][methodState]++;
             return;
         }
 
@@ -160,14 +168,15 @@
             // Add a new entry.
             if (set->size < set->maxSize) {
                 entry->method = method;
-                entry->counts[threadType][threadState] = 1;
+                entry->counts[threadType][threadState][methodState] = 1;
                 set->collisions += (i != start);
                 set->size++;
             } else {
                 if (set->capacity < MAX_CAPACITY) {
                     // The set is 3/4 full. Expand it, and then add the entry.
                     expand(set);
-                    countMethod(set, method, threadType, threadState);
+                    countMethod(set, method, threadType, threadState,
+                            methodState);
                 } else {
                     // Don't add any more entries.
                     // TODO: Should we replace the LRU entry?
@@ -193,10 +202,10 @@
         ? EVENT_THREAD : OTHER_THREAD;
 
     ThreadState threadState;
-    switch (thread->status) {
+    switch (dvmGetNativeThreadStatus(thread)) {
         case THREAD_RUNNING: threadState = RUNNING_THREAD; break;
-        case THREAD_NATIVE: threadState = NATIVE_THREAD; break;
-        default: threadState = SUSPENDED_THREAD;
+        case THREAD_NATIVE: dvmAbort();
+        default: threadState = SUSPENDED_THREAD; // includes PAGING
     }
 
     /*
@@ -213,13 +222,15 @@
         return;
     }
 
+    MethodState methodState = LEAF_METHOD;
     while (true) {
         StackSaveArea* saveArea = SAVEAREA_FROM_FP(currentFrame);
 
         const Method* method = saveArea->method;
         // Count the method now. We'll validate later that it's a real Method*.
         if (method != NULL) {
-            countMethod(set, method, threadType, threadState);
+            countMethod(set, method, threadType, threadState, methodState);
+            methodState = CALLING_METHOD;
         }
 
         void* callerFrame = saveArea->prevFrame;
@@ -398,7 +409,8 @@
 
         size += 2; // method name size
         size += wrapper->methodNameLength;
-        size += THREAD_TYPE_SIZE * THREAD_STATE_SIZE * 2; // sample counts
+        // sample counts
+        size += THREAD_TYPE_SIZE * THREAD_STATE_SIZE * METHOD_STATE_SIZE * 2;
         wrapper = wrapper->next;
     } while (wrapper != NULL);
 
@@ -451,11 +463,15 @@
                 wrapper->methodNameLength);
 
         // Sample counts.
-        u2 (*counts)[THREAD_STATE_SIZE] = wrapper->methodCount->counts;
-        int type, state;
+        u2 (*counts)[THREAD_STATE_SIZE][METHOD_STATE_SIZE]
+                = wrapper->methodCount->counts;
+        int type, threadState, methodState;
         for (type = 0; type < THREAD_TYPE_SIZE; type++)
-            for (state = 0; state < THREAD_STATE_SIZE; state++)
-                writeShort(offset, counts[type][state]);
+            for (threadState = 0; threadState < THREAD_STATE_SIZE;
+                    threadState++)
+                for (methodState = 0; methodState < METHOD_STATE_SIZE;
+                        methodState++)
+                    writeShort(offset, counts[type][threadState][methodState]);
 
         methodCount++;
         wrapper = wrapper->next;
@@ -489,13 +505,16 @@
      *  MethodEntry:
      *    method name length (2 bytes)
      *    UTF-8 method name
-     *    Counts (for event thread)
-     *    Counts (for other threads)
+     *    CountsByThreadState (for event thread)
+     *    CountsByThreadState (for other threads)
      *
-     *  Counts:
-     *    running count (2 bytes)
-     *    native count (2 bytes)
-     *    suspended count (2 bytes)
+     *  CountsByThreadState:
+     *    CountsByMethodState (for running threads)
+     *    CountsByMethodState (for suspended threads)
+     *
+     *  CountsByMethodState:
+     *    as calling method (2 bytes)
+     *    as leaf method (2 bytes)
      */
 
     SampleSet* set = (SampleSet*) args[0];
@@ -591,6 +610,17 @@
 }
 
 /**
+ * Frees native memory.
+ */
+static void Dalvik_dalvik_system_SamplingProfiler_free(const u4* args,
+        JValue* pResult) {
+    SampleSet* set = (SampleSet*) args[0];
+    free(set->entries);
+    free(set);
+    RETURN_VOID();
+}
+
+/**
  * Identifies the event thread.
  */
 static void Dalvik_dalvik_system_SamplingProfiler_setEventThread(const u4* args,
@@ -608,8 +638,9 @@
     { "size", "(I)I", Dalvik_dalvik_system_SamplingProfiler_size },
     { "sample", "(I)I", Dalvik_dalvik_system_SamplingProfiler_sample },
     { "snapshot", "(I)[B", Dalvik_dalvik_system_SamplingProfiler_snapshot },
+    { "free", "(I)V", Dalvik_dalvik_system_SamplingProfiler_free },
     { "allocate", "()I", Dalvik_dalvik_system_SamplingProfiler_allocate },
     { "setEventThread", "(ILjava/lang/Thread;)V",
             Dalvik_dalvik_system_SamplingProfiler_setEventThread },
     { NULL, NULL, NULL },
-};
+};
\ No newline at end of file