Merge "Add the "- waiting on"/"- waiting to lock" lines to the SIGQUIT output." into dalvik-dev
diff --git a/src/asm_support.h b/src/asm_support.h
index 3a8a8ce..1ef10b8 100644
--- a/src/asm_support.h
+++ b/src/asm_support.h
@@ -9,13 +9,13 @@
#define rLR r14
#define SUSPEND_CHECK_INTERVAL (1000)
// Offset of field Thread::top_of_managed_stack_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_OFFSET 341
+#define THREAD_TOP_OF_MANAGED_STACK_OFFSET 276
// Offset of field Thread::top_of_managed_stack_pc_ verified in InitCpu
-#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 345
+#define THREAD_TOP_OF_MANAGED_STACK_PC_OFFSET 280
#elif defined(__i386__)
// Offset of field Thread::self_ verified in InitCpu
-#define THREAD_SELF_OFFSET 365
+#define THREAD_SELF_OFFSET 372
#endif
#endif // ART_SRC_ASM_SUPPORT_H_
diff --git a/src/dex_file.cc b/src/dex_file.cc
index 924953f..519cdc2 100644
--- a/src/dex_file.cc
+++ b/src/dex_file.cc
@@ -287,17 +287,15 @@
// lock. If somebody else is working on it, we'll block here until
// they complete. Because we're waiting on an external resource,
// we go into native mode.
- // Note that current_thread can be NULL if we're parsing the bootclasspath
+ // Note that self can be NULL if we're parsing the bootclasspath
// during JNI_CreateJavaVM.
- Thread* current_thread = Thread::Current();
- Thread::State old(Thread::kUnknown);
- if (current_thread != NULL) {
- old = current_thread->SetState(Thread::kNative);
+ Thread* self = Thread::Current();
+ UniquePtr<ScopedThreadStateChange> state_changer;
+ if (self != NULL) {
+ state_changer.reset(new ScopedThreadStateChange(self, Thread::kNative));
}
UniquePtr<LockedFd> fd(LockedFd::CreateAndLock(cache_path_tmp, 0644));
- if (current_thread != NULL) {
- current_thread->SetState(old);
- }
+ state_changer.reset(NULL);
if (fd.get() == NULL) {
return NULL;
}
diff --git a/src/java_lang_Thread.cc b/src/java_lang_Thread.cc
index b1da60d..9a91f77 100644
--- a/src/java_lang_Thread.cc
+++ b/src/java_lang_Thread.cc
@@ -48,8 +48,10 @@
jint Thread_nativeGetStatus(JNIEnv* env, jobject javaThread) {
ThreadListLock lock;
Thread* thread = Thread::FromManagedThread(env, javaThread);
- Thread::State state = (thread != NULL) ? thread->GetState() : Thread::kUnknown;
- return static_cast<jint>(state);
+ if (thread == NULL) {
+ return -1;
+ }
+ return static_cast<jint>(thread->GetState());
}
jboolean Thread_nativeHoldsLock(JNIEnv* env, jobject javaThread, jobject javaObject) {
diff --git a/src/monitor.cc b/src/monitor.cc
index 7d6cde4..4e71e45 100644
--- a/src/monitor.cc
+++ b/src/monitor.cc
@@ -27,6 +27,7 @@
#include "mutex.h"
#include "object.h"
#include "thread.h"
+#include "thread_list.h"
namespace art {
@@ -638,6 +639,7 @@
// Allocate and acquire a new monitor.
Monitor* m = new Monitor(obj);
+ LOG(INFO) << "created monitor " << m << " for object " << obj;
// Replace the head of the list with the new monitor.
do {
m->next_ = gMonitorList;
@@ -699,6 +701,7 @@
} else {
LOG(INFO) << StringPrintf("(%d) spin on lock %p: %#x (%#x) %#x", threadId, thinp, 0, *thinp, thin);
// The lock is owned by another thread. Notify the VM that we are about to wait.
+ self->monitor_enter_object_ = obj;
Thread::State oldStatus = self->SetState(Thread::kBlocked);
// Spin until the thin lock is released or inflated.
sleepDelayNs = 0;
@@ -736,12 +739,14 @@
// The thin lock was inflated by another thread. Let the VM know we are no longer
// waiting and try again.
LOG(INFO) << "(" << threadId << ") lock " << (void*) thinp << " surprise-fattened";
+ self->monitor_enter_object_ = NULL;
self->SetState(oldStatus);
goto retry;
}
}
LOG(INFO) << StringPrintf("(%d) spin on lock done %p: %#x (%#x) %#x", threadId, thinp, 0, *thinp, thin);
// We have acquired the thin lock. Let the VM know that we are no longer waiting.
+ self->monitor_enter_object_ = NULL;
self->SetState(oldStatus);
// Fatten the lock.
Inflate(self, obj);
@@ -885,4 +890,39 @@
}
}
+void Monitor::DescribeWait(std::ostream& os, const Thread* thread) {
+ Thread::State state = thread->GetState();
+
+ Object* object = NULL;
+ uint32_t lock_owner = ThreadList::kInvalidId;
+ if (state == Thread::kWaiting || state == Thread::kTimedWaiting) {
+ os << " - waiting on ";
+ Monitor* monitor = thread->wait_monitor_;
+ if (monitor != NULL) {
+ object = monitor->obj_;
+ }
+ lock_owner = Thread::LockOwnerFromThreadLock(object);
+ } else if (state == Thread::kBlocked) {
+ os << " - waiting to lock ";
+ object = thread->monitor_enter_object_;
+ if (object != NULL) {
+ lock_owner = object->GetLockOwner();
+ }
+ } else {
+ // We're not waiting on anything.
+ return;
+ }
+ os << "<" << object << ">";
+
+ // - waiting on <0x613f83d8> (a java.lang.ThreadLock) held by thread 5
+ // - waiting on <0x6008c468> (a java.lang.Class<java.lang.ref.ReferenceQueue>)
+ os << " (a " << PrettyTypeOf(object) << ")";
+
+ if (lock_owner != ThreadList::kInvalidId) {
+ os << " held by thread " << lock_owner;
+ }
+
+ os << "\n";
+}
+
} // namespace art
diff --git a/src/monitor.h b/src/monitor.h
index 8c22fb1..fefc43e 100644
--- a/src/monitor.h
+++ b/src/monitor.h
@@ -20,6 +20,8 @@
#include <pthread.h>
#include <stdint.h>
+#include <iosfwd>
+
#include "mutex.h"
namespace art {
@@ -71,6 +73,8 @@
static void FreeMonitorList();
+ static void DescribeWait(std::ostream& os, const Thread* thread);
+
private:
Monitor(Object* obj);
diff --git a/src/thread.cc b/src/thread.cc
index fb2ba8d..4e3e8d1 100644
--- a/src/thread.cc
+++ b/src/thread.cc
@@ -30,6 +30,7 @@
#include "context.h"
#include "heap.h"
#include "jni_internal.h"
+#include "monitor.h"
#include "object.h"
#include "runtime.h"
#include "runtime_support.h"
@@ -41,6 +42,7 @@
pthread_key_t Thread::pthread_key_self_;
+static Class* gThreadLock = NULL;
static Class* gThrowable = NULL;
static Field* gThread_daemon = NULL;
static Field* gThread_group = NULL;
@@ -50,6 +52,7 @@
static Field* gThread_uncaughtHandler = NULL;
static Field* gThread_vmData = NULL;
static Field* gThreadGroup_name = NULL;
+static Field* gThreadLock_thread = NULL;
static Method* gThread_run = NULL;
static Method* gThreadGroup_removeThread = NULL;
static Method* gUncaughtExceptionHandler_uncaughtException = NULL;
@@ -818,7 +821,8 @@
}
struct StackDumpVisitor : public Thread::StackVisitor {
- StackDumpVisitor(std::ostream& os) : os(os) {
+ StackDumpVisitor(std::ostream& os, const Thread* thread)
+ : os(os), thread(thread), frame_count(0) {
}
virtual ~StackDumpVisitor() {
@@ -828,10 +832,10 @@
if (!frame.HasMethod()) {
return;
}
- ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
Method* m = frame.GetMethod();
Class* c = m->GetDeclaringClass();
+ ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
const DexFile& dex_file = class_linker->FindDexFile(c->GetDexCache());
os << " at " << PrettyMethod(m, false);
@@ -842,13 +846,19 @@
os << "(" << c->GetSourceFile()->ToModifiedUtf8() << ":" << line_number << ")";
}
os << "\n";
+
+ if (frame_count++ == 0) {
+ Monitor::DescribeWait(os, thread);
+ }
}
std::ostream& os;
+ const Thread* thread;
+ int frame_count;
};
void Thread::DumpStack(std::ostream& os) const {
- StackDumpVisitor dumper(os);
+ StackDumpVisitor dumper(os, this);
WalkStack(&dumper);
}
@@ -962,29 +972,61 @@
}
}
+// TODO: make more accessible?
+Class* FindPrimitiveClassOrDie(ClassLinker* class_linker, char descriptor) {
+ Class* c = class_linker->FindPrimitiveClass(descriptor);
+ CHECK(c != NULL) << descriptor;
+ return c;
+}
+
+// TODO: make more accessible?
+Class* FindClassOrDie(ClassLinker* class_linker, const char* descriptor) {
+ Class* c = class_linker->FindSystemClass(descriptor);
+ CHECK(c != NULL) << descriptor;
+ return c;
+}
+
+// TODO: make more accessible?
+Field* FindFieldOrDie(Class* c, const char* name, Class* type) {
+ Field* f = c->FindDeclaredInstanceField(name, type);
+ CHECK(f != NULL) << PrettyClass(c) << " " << name << " " << PrettyClass(type);
+ return f;
+}
+
+// TODO: make more accessible?
+Method* FindMethodOrDie(Class* c, const char* name, const char* signature) {
+ Method* m = c->FindVirtualMethod(name, signature);
+ CHECK(m != NULL) << PrettyClass(c) << " " << name << " " << signature;
+ return m;
+}
+
void Thread::FinishStartup() {
// Now the ClassLinker is ready, we can find the various Class*, Field*, and Method*s we need.
ClassLinker* class_linker = Runtime::Current()->GetClassLinker();
- Class* boolean_class = class_linker->FindPrimitiveClass('Z');
- Class* int_class = class_linker->FindPrimitiveClass('I');
- Class* String_class = class_linker->FindSystemClass("Ljava/lang/String;");
- Class* Thread_class = class_linker->FindSystemClass("Ljava/lang/Thread;");
- Class* ThreadGroup_class = class_linker->FindSystemClass("Ljava/lang/ThreadGroup;");
- Class* ThreadLock_class = class_linker->FindSystemClass("Ljava/lang/ThreadLock;");
- Class* UncaughtExceptionHandler_class = class_linker->FindSystemClass("Ljava/lang/Thread$UncaughtExceptionHandler;");
- gThrowable = class_linker->FindSystemClass("Ljava/lang/Throwable;");
- gThread_daemon = Thread_class->FindDeclaredInstanceField("daemon", boolean_class);
- gThread_group = Thread_class->FindDeclaredInstanceField("group", ThreadGroup_class);
- gThread_lock = Thread_class->FindDeclaredInstanceField("lock", ThreadLock_class);
- gThread_name = Thread_class->FindDeclaredInstanceField("name", String_class);
- gThread_priority = Thread_class->FindDeclaredInstanceField("priority", int_class);
- gThread_run = Thread_class->FindVirtualMethod("run", "()V");
- gThread_uncaughtHandler = Thread_class->FindDeclaredInstanceField("uncaughtHandler", UncaughtExceptionHandler_class);
- gThread_vmData = Thread_class->FindDeclaredInstanceField("vmData", int_class);
- gThreadGroup_name = ThreadGroup_class->FindDeclaredInstanceField("name", String_class);
- gThreadGroup_removeThread = ThreadGroup_class->FindVirtualMethod("removeThread", "(Ljava/lang/Thread;)V");
- gUncaughtExceptionHandler_uncaughtException =
- UncaughtExceptionHandler_class->FindVirtualMethod("uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
+
+ Class* boolean_class = FindPrimitiveClassOrDie(class_linker, 'Z');
+ Class* int_class = FindPrimitiveClassOrDie(class_linker, 'I');
+ Class* String_class = FindClassOrDie(class_linker, "Ljava/lang/String;");
+ Class* Thread_class = FindClassOrDie(class_linker, "Ljava/lang/Thread;");
+ Class* ThreadGroup_class = FindClassOrDie(class_linker, "Ljava/lang/ThreadGroup;");
+ Class* UncaughtExceptionHandler_class = FindClassOrDie(class_linker, "Ljava/lang/Thread$UncaughtExceptionHandler;");
+ gThreadLock = FindClassOrDie(class_linker, "Ljava/lang/ThreadLock;");
+ gThrowable = FindClassOrDie(class_linker, "Ljava/lang/Throwable;");
+
+ gThread_daemon = FindFieldOrDie(Thread_class, "daemon", boolean_class);
+ gThread_group = FindFieldOrDie(Thread_class, "group", ThreadGroup_class);
+ gThread_lock = FindFieldOrDie(Thread_class, "lock", gThreadLock);
+ gThread_name = FindFieldOrDie(Thread_class, "name", String_class);
+ gThread_priority = FindFieldOrDie(Thread_class, "priority", int_class);
+ gThread_uncaughtHandler = FindFieldOrDie(Thread_class, "uncaughtHandler", UncaughtExceptionHandler_class);
+ gThread_vmData = FindFieldOrDie(Thread_class, "vmData", int_class);
+ gThreadGroup_name = FindFieldOrDie(ThreadGroup_class, "name", String_class);
+ gThreadLock_thread = FindFieldOrDie(gThreadLock, "thread", Thread_class);
+
+ gThread_run = FindMethodOrDie(Thread_class, "run", "()V");
+ gThreadGroup_removeThread = FindMethodOrDie(ThreadGroup_class, "removeThread", "(Ljava/lang/Thread;)V");
+ gUncaughtExceptionHandler_uncaughtException = FindMethodOrDie(UncaughtExceptionHandler_class,
+ "uncaughtException", "(Ljava/lang/Thread;Ljava/lang/Throwable;)V");
// Finish attaching the main thread.
Thread::Current()->CreatePeer("main", false);
@@ -994,27 +1036,45 @@
CHECK_PTHREAD_CALL(pthread_key_delete, (Thread::pthread_key_self_), "self key");
}
+uint32_t Thread::LockOwnerFromThreadLock(Object* thread_lock) {
+ if (thread_lock == NULL || thread_lock->GetClass() != gThreadLock) {
+ return ThreadList::kInvalidId;
+ }
+ Object* managed_thread = gThreadLock_thread->GetObject(thread_lock);
+ if (managed_thread == NULL) {
+ return ThreadList::kInvalidId;
+ }
+ uintptr_t vmData = static_cast<uintptr_t>(gThread_vmData->GetInt(managed_thread));
+ Thread* thread = reinterpret_cast<Thread*>(vmData);
+ if (thread == NULL) {
+ return ThreadList::kInvalidId;
+ }
+ return thread->GetThinLockId();
+}
+
Thread::Thread()
: peer_(NULL),
+ top_of_managed_stack_(),
+ top_of_managed_stack_pc_(0),
wait_mutex_(new Mutex("Thread wait mutex")),
wait_cond_(new ConditionVariable("Thread wait condition variable")),
wait_monitor_(NULL),
interrupted_(false),
wait_next_(NULL),
+ monitor_enter_object_(NULL),
card_table_(0),
stack_end_(NULL),
- top_of_managed_stack_(),
- top_of_managed_stack_pc_(0),
native_to_managed_record_(NULL),
top_sirt_(NULL),
jni_env_(NULL),
- state_(Thread::kUnknown),
+ state_(Thread::kNative),
self_(NULL),
runtime_(NULL),
exception_(NULL),
suspend_count_(0),
class_loader_override_(NULL),
long_jump_context_(NULL) {
+ CHECK((sizeof(Thread) % 4) == 0) << sizeof(Thread);
}
void MonitorExitVisitor(const Object* object, void*) {
@@ -1537,7 +1597,7 @@
"Suspended",
};
std::ostream& operator<<(std::ostream& os, const Thread::State& state) {
- int int_state = static_cast<int>(state);
+ int32_t int_state = static_cast<int32_t>(state);
if (state >= Thread::kTerminated && state <= Thread::kSuspended) {
os << kStateNames[int_state];
} else {
diff --git a/src/thread.h b/src/thread.h
index a9cc78f..01cc1cc 100644
--- a/src/thread.h
+++ b/src/thread.h
@@ -151,8 +151,6 @@
kMaxPriority = 10,
};
enum State {
- kUnknown = -1,
-
// These match up with JDWP values.
kTerminated = 0, // TERMINATED
kRunnable = 1, // RUNNABLE or running now
@@ -257,6 +255,7 @@
}
static Thread* FromManagedThread(JNIEnv* env, jobject thread);
+ static uint32_t LockOwnerFromThreadLock(Object* thread_lock);
void Dump(std::ostream& os) const;
@@ -577,15 +576,28 @@
// Our managed peer (an instance of java.lang.Thread).
Object* peer_;
+ // The top_of_managed_stack_ and top_of_managed_stack_pc_ fields are accessed from
+ // compiled code, so we keep them early in the structure to (a) avoid having to keep
+ // fixing the assembler offsets and (b) improve the chances that these will still be aligned.
+
+ // Top of the managed stack, written out prior to the state transition from
+ // kRunnable to kNative. Uses include to give the starting point for scanning
+ // a managed stack when a thread is in native code.
+ Frame top_of_managed_stack_;
+ // PC corresponding to the call out of the top_of_managed_stack_ frame
+ uintptr_t top_of_managed_stack_pc_;
+
// Guards the 'interrupted_' and 'wait_monitor_' members.
mutable Mutex* wait_mutex_;
ConditionVariable* wait_cond_;
// Pointer to the monitor lock we're currently waiting on (or NULL), guarded by wait_mutex_.
Monitor* wait_monitor_;
// Thread "interrupted" status; stays raised until queried or thrown, guarded by wait_mutex_.
- bool interrupted_;
+ uint32_t interrupted_;
// The next thread in the wait set this thread is part of.
Thread* wait_next_;
+ // If we're blocked in MonitorEnter, this is the object we're trying to lock.
+ Object* monitor_enter_object_;
friend class Monitor;
@@ -604,14 +616,6 @@
// The "lowest addressable byte" of the stack
byte* stack_base_;
- // Top of the managed stack, written out prior to the state transition from
- // kRunnable to kNative. Uses include to give the starting point for scanning
- // a managed stack when a thread is in native code.
- Frame top_of_managed_stack_;
-
- // PC corresponding to the call out of the top_of_managed_stack_ frame
- uintptr_t top_of_managed_stack_pc_;
-
// A linked list (of stack allocated records) recording transitions from
// native to managed code.
NativeToManagedRecord* native_to_managed_record_;
diff --git a/src/thread_list.h b/src/thread_list.h
index e5d9eab..35b23e7 100644
--- a/src/thread_list.h
+++ b/src/thread_list.h
@@ -87,12 +87,10 @@
// Try to get it from TLS.
self = Thread::Current();
}
+ // self may still be NULL during VM shutdown.
Thread::State old_state;
if (self != NULL) {
old_state = self->SetState(Thread::kVmWait);
- } else {
- // This happens during VM shutdown.
- old_state = Thread::kUnknown;
}
Runtime::Current()->GetThreadList()->thread_list_lock_.Lock();
if (self != NULL) {