More debugger support.
This gets us as far as the first DDMS-specific message, which means
it's time to bring in a bunch more code.
Change-Id: I3f9d75706d5ddde0aa21fcca558132282b94eff4
diff --git a/src/thread_list.cc b/src/thread_list.cc
index dfce2a2..f386547 100644
--- a/src/thread_list.cc
+++ b/src/thread_list.cc
@@ -18,6 +18,8 @@
#include <unistd.h>
+#include "debugger.h"
+
namespace art {
// TODO: merge with ThreadListLock?
@@ -113,23 +115,24 @@
}
}
-void ThreadList::SuspendAll() {
+void ThreadList::SuspendAll(bool for_debugger) {
Thread* self = Thread::Current();
// TODO: add another thread_suspend_lock_ to avoid GC/debugger races.
if (verbose_) {
- LOG(INFO) << *self << " SuspendAll starting...";
+ LOG(INFO) << *self << " SuspendAll starting..." << (for_debugger ? " (debugger)" : "");
}
ThreadListLocker locker(this);
+ Thread* debug_thread = Dbg::GetDebugThread();
{
// Increment everybody's suspend count (except our own).
MutexLock mu(thread_suspend_count_lock_);
for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
Thread* thread = *it;
- if (thread != self) {
+ if (thread != self && thread != debug_thread) {
if (verbose_) {
LOG(INFO) << "requesting thread suspend: " << *thread;
}
@@ -154,7 +157,7 @@
*/
for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
Thread* thread = *it;
- if (thread != self) {
+ if (thread != self && thread != debug_thread) {
thread->WaitUntilSuspended();
if (verbose_) {
LOG(INFO) << "thread suspended: " << *thread;
@@ -193,12 +196,56 @@
}
}
+void ThreadList::SuspendSelfForDebugger() {
+ Thread* self = Thread::Current();
-void ThreadList::ResumeAll() {
+ // The debugger thread must not suspend itself due to debugger activity!
+ Thread* debug_thread = Dbg::GetDebugThread();
+ CHECK(debug_thread != NULL);
+ CHECK(self != debug_thread);
+
+ // Collisions with other suspends aren't really interesting. We want
+ // to ensure that we're the only one fiddling with the suspend count
+ // though.
+ ThreadListLocker locker(this);
+ MutexLock mu(thread_suspend_count_lock_);
+ ++self->suspend_count_;
+
+ // Suspend ourselves.
+ CHECK_GT(self->suspend_count_, 0);
+ self->SetState(Thread::kSuspended);
+ if (verbose_) {
+ LOG(INFO) << *self << " self-suspending (dbg)";
+ }
+
+ // Tell JDWP that we've completed suspension. The JDWP thread can't
+ // tell us to resume before we're fully asleep because we hold the
+ // suspend count lock.
+ Dbg::ClearWaitForEventThread();
+
+ while (self->suspend_count_ != 0) {
+ thread_suspend_count_cond_.Wait(thread_suspend_count_lock_);
+ if (self->suspend_count_ != 0) {
+ // The condition was signaled but we're still suspended. This
+ // can happen if the debugger lets go while a SIGQUIT thread
+ // dump event is pending (assuming SignalCatcher was resumed for
+ // just long enough to try to grab the thread-suspend lock).
+ LOG(DEBUG) << *self << " still suspended after undo "
+ << "(suspend count=" << self->suspend_count_ << ")";
+ }
+ }
+ CHECK_EQ(self->suspend_count_, 0);
+ self->SetState(Thread::kRunnable);
+ if (verbose_) {
+ LOG(INFO) << *self << " self-reviving (dbg)";
+ }
+}
+
+void ThreadList::ResumeAll(bool for_debugger) {
Thread* self = Thread::Current();
if (verbose_) {
- LOG(INFO) << *self << " ResumeAll starting";
+ LOG(INFO) << *self << " ResumeAll starting" << (for_debugger ? " (debugger)" : "");
}
// Decrement the suspend counts for all threads. No need for atomic
@@ -206,10 +253,11 @@
// We do need to hold the thread list because of JNI attaches.
{
ThreadListLocker locker(this);
+ Thread* debug_thread = Dbg::GetDebugThread();
MutexLock mu(thread_suspend_count_lock_);
for (It it = list_.begin(), end = list_.end(); it != end; ++it) {
Thread* thread = *it;
- if (thread != self) {
+ if (thread != self && thread != debug_thread) {
if (thread->suspend_count_ > 0) {
--thread->suspend_count_;
} else {