Dump more debug info for b/33006388.
Bug: 33006388
Bug: 12687968
Test: test-art-host with CC.
Change-Id: Id9d67bc603c6ff7bc8e346e181e3e09ffbda43b3
diff --git a/runtime/barrier.cc b/runtime/barrier.cc
index 0d842cc..9bcda35 100644
--- a/runtime/barrier.cc
+++ b/runtime/barrier.cc
@@ -80,6 +80,11 @@
return timed_out;
}
+int Barrier::GetCount(Thread* self) {
+ MutexLock mu(self, lock_);
+ return count_;
+}
+
void Barrier::SetCountLocked(Thread* self, int count) {
count_ = count;
if (count == 0) {
diff --git a/runtime/barrier.h b/runtime/barrier.h
index 94977fb..d7c4661 100644
--- a/runtime/barrier.h
+++ b/runtime/barrier.h
@@ -61,6 +61,8 @@
// another thread is still in Wait(). See above.
void Init(Thread* self, int count) REQUIRES(!lock_);
+ int GetCount(Thread* self) REQUIRES(!lock_);
+
private:
void SetCountLocked(Thread* self, int count) REQUIRES(lock_);
diff --git a/runtime/gc/collector/concurrent_copying.cc b/runtime/gc/collector/concurrent_copying.cc
index 19ee0fb..fbab73f 100644
--- a/runtime/gc/collector/concurrent_copying.cc
+++ b/runtime/gc/collector/concurrent_copying.cc
@@ -812,13 +812,13 @@
false_gray_stack_.clear();
}
-
void ConcurrentCopying::IssueEmptyCheckpoint() {
Thread* self = Thread::Current();
ThreadList* thread_list = Runtime::Current()->GetThreadList();
Barrier* barrier = thread_list->EmptyCheckpointBarrier();
barrier->Init(self, 0);
- size_t barrier_count = thread_list->RunEmptyCheckpoint();
+ std::vector<uint32_t> runnable_thread_ids; // Used in debug build only
+ size_t barrier_count = thread_list->RunEmptyCheckpoint(runnable_thread_ids);
// If there are no threads to wait which implys that all the checkpoint functions are finished,
// then no need to release the mutator lock.
if (barrier_count == 0) {
@@ -828,7 +828,27 @@
Locks::mutator_lock_->SharedUnlock(self);
{
ScopedThreadStateChange tsc(self, kWaitingForCheckPointsToRun);
- barrier->Increment(self, barrier_count);
+ if (kIsDebugBuild) {
+ static constexpr uint64_t kEmptyCheckpointTimeoutMs = 600 * 1000; // 10 minutes.
+ bool timed_out = barrier->Increment(self, barrier_count, kEmptyCheckpointTimeoutMs);
+ if (timed_out) {
+ Runtime* runtime = Runtime::Current();
+ std::ostringstream ss;
+ ss << "Empty checkpoint timeout\n";
+ ss << "Barrier count " << barrier->GetCount(self) << "\n";
+ ss << "Runnable thread IDs";
+ for (uint32_t tid : runnable_thread_ids) {
+ ss << " " << tid;
+ }
+ ss << "\n";
+ Locks::mutator_lock_->Dump(ss);
+ ss << "\n";
+ runtime->GetThreadList()->Dump(ss);
+ LOG(FATAL) << ss.str();
+ }
+ } else {
+ barrier->Increment(self, barrier_count);
+ }
}
Locks::mutator_lock_->SharedLock(self);
}
diff --git a/runtime/thread.cc b/runtime/thread.cc
index 1283cf0..0bcb2f7 100644
--- a/runtime/thread.cc
+++ b/runtime/thread.cc
@@ -1394,6 +1394,7 @@
os << " | group=\"" << group_name << "\""
<< " sCount=" << thread->tls32_.suspend_count
<< " dsCount=" << thread->tls32_.debug_suspend_count
+ << " flags=" << thread->tls32_.state_and_flags.as_struct.flags
<< " obj=" << reinterpret_cast<void*>(thread->tlsPtr_.opeer)
<< " self=" << reinterpret_cast<const void*>(thread) << "\n";
}
diff --git a/runtime/thread_list.cc b/runtime/thread_list.cc
index 27fb37a..a6bd83d 100644
--- a/runtime/thread_list.cc
+++ b/runtime/thread_list.cc
@@ -375,7 +375,7 @@
return count;
}
-size_t ThreadList::RunEmptyCheckpoint() {
+size_t ThreadList::RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids) {
Thread* self = Thread::Current();
Locks::mutator_lock_->AssertNotExclusiveHeld(self);
Locks::thread_list_lock_->AssertNotHeld(self);
@@ -392,6 +392,9 @@
// This thread will run an empty checkpoint (decrement the empty checkpoint barrier)
// some time in the near future.
++count;
+ if (kIsDebugBuild) {
+ runnable_thread_ids.push_back(thread->GetThreadId());
+ }
break;
}
if (thread->GetState() != kRunnable) {
diff --git a/runtime/thread_list.h b/runtime/thread_list.h
index 133d430..1acabcb 100644
--- a/runtime/thread_list.h
+++ b/runtime/thread_list.h
@@ -27,6 +27,7 @@
#include <bitset>
#include <list>
+#include <vector>
namespace art {
namespace gc {
@@ -106,7 +107,9 @@
// in-flight mutator heap access (eg. a read barrier.) Runnable threads will respond by
// decrementing the empty checkpoint barrier count. This works even when the weak ref access is
// disabled. Only one concurrent use is currently supported.
- size_t RunEmptyCheckpoint()
+ // In debug build, runnable_thread_ids will be populated with the thread IDS of the runnable
+ // thread to wait for.
+ size_t RunEmptyCheckpoint(std::vector<uint32_t>& runnable_thread_ids)
REQUIRES(!Locks::thread_list_lock_, !Locks::thread_suspend_count_lock_);
size_t RunCheckpointOnRunnableThreads(Closure* checkpoint_function)