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