diff options
Diffstat (limited to 'runtime/base/mutex.cc')
| -rw-r--r-- | runtime/base/mutex.cc | 82 |
1 files changed, 80 insertions, 2 deletions
diff --git a/runtime/base/mutex.cc b/runtime/base/mutex.cc index 0b8c781858..6574ec0db6 100644 --- a/runtime/base/mutex.cc +++ b/runtime/base/mutex.cc @@ -31,11 +31,18 @@ #include "mutex-inl.h" #include "scoped_thread_state_change-inl.h" #include "thread-inl.h" +#include "thread.h" +#include "thread_list.h" namespace art { using android::base::StringPrintf; +static constexpr uint64_t kIntervalMillis = 50; +static constexpr int kMonitorTimeoutTryMax = 5; + +static const char* kLastDumpStackTime = "LastDumpStackTime"; + struct AllMutexData { // A guard for all_mutexes_ that's not a mutex (Mutexes must CAS to acquire and busy wait). Atomic<const BaseMutex*> all_mutexes_guard; @@ -45,6 +52,13 @@ struct AllMutexData { }; static struct AllMutexData gAllMutexData[kAllMutexDataSize]; +struct DumpStackLastTimeTLSData : public art::TLSData { + explicit DumpStackLastTimeTLSData(uint64_t last_dump_time_ms) { + last_dump_time_ms_ = last_dump_time_ms; + } + uint64_t last_dump_time_ms_; +}; + #if ART_USE_FUTEXES static bool ComputeRelativeTimeSpec(timespec* result_ts, const timespec& lhs, const timespec& rhs) { const int32_t one_sec = 1000 * 1000 * 1000; // one second in nanoseconds. @@ -443,15 +457,28 @@ void Mutex::ExclusiveLock(Thread* self) { if (UNLIKELY(should_respond_to_empty_checkpoint_request_)) { self->CheckEmptyCheckpointFromMutex(); } + + uint64_t wait_start_ms = enable_monitor_timeout_ ? MilliTime() : 0; + uint64_t try_times = 0; do { + timespec timeout_ts; + timeout_ts.tv_sec = 0; + timeout_ts.tv_nsec = Runtime::Current()->GetMonitorTimeoutNs(); if (futex(state_and_contenders_.Address(), FUTEX_WAIT_PRIVATE, cur_state, - nullptr, nullptr, 0) != 0) { + enable_monitor_timeout_ ? &timeout_ts : nullptr , nullptr, 0) != 0) { // We only went to sleep after incrementing and contenders and checking that the // lock is still held by someone else. EAGAIN and EINTR both indicate a spurious // failure, try again from the beginning. We don't use TEMP_FAILURE_RETRY so we can // intentionally retry to acquire the lock. if ((errno != EAGAIN) && (errno != EINTR)) { - PLOG(FATAL) << "futex wait failed for " << name_; + if (errno == ETIMEDOUT) { + try_times++; + if (try_times <= kMonitorTimeoutTryMax) { + DumpStack(self, wait_start_ms, try_times); + } + } else { + PLOG(FATAL) << "futex wait failed for " << name_; + } } } SleepIfRuntimeDeleted(self); @@ -481,6 +508,57 @@ void Mutex::ExclusiveLock(Thread* self) { } } +void Mutex::DumpStack(Thread* self, uint64_t wait_start_ms, uint64_t try_times) { + ScopedObjectAccess soa(self); + Locks::thread_list_lock_->ExclusiveLock(self); + std::string owner_stack_dump; + pid_t owner_tid = GetExclusiveOwnerTid(); + Thread *owner = Runtime::Current()->GetThreadList()->FindThreadByTid(owner_tid); + if (owner != nullptr) { + if (IsDumpFrequent(owner, try_times)) { + Locks::thread_list_lock_->ExclusiveUnlock(self); + LOG(WARNING) << "Contention with tid " << owner_tid << ", monitor id " << monitor_id_; + return; + } + struct CollectStackTrace : public Closure { + void Run(art::Thread* thread) override + REQUIRES_SHARED(art::Locks::mutator_lock_) { + if (IsDumpFrequent(thread)) { + return; + } + thread->SetCustomTLS(kLastDumpStackTime, new DumpStackLastTimeTLSData(MilliTime())); + thread->DumpJavaStack(oss); + } + std::ostringstream oss; + }; + CollectStackTrace owner_trace; + owner->RequestSynchronousCheckpoint(&owner_trace); + owner_stack_dump = owner_trace.oss.str(); + uint64_t wait_ms = MilliTime() - wait_start_ms; + LOG(WARNING) << "Monitor contention with tid " << owner_tid << ", wait time: " << wait_ms + << "ms, monitor id: " << monitor_id_ + << "\nPerfMonitor owner thread(" << owner_tid << ") stack is:\n" + << owner_stack_dump; + } else { + Locks::thread_list_lock_->ExclusiveUnlock(self); + } +} + +bool Mutex::IsDumpFrequent(Thread* thread, uint64_t try_times) { + uint64_t last_dump_time_ms = 0; + DumpStackLastTimeTLSData* tls_data = + reinterpret_cast<DumpStackLastTimeTLSData*>(thread->GetCustomTLS(kLastDumpStackTime)); + if (tls_data != nullptr) { + last_dump_time_ms = tls_data->last_dump_time_ms_; + } + uint64_t interval = MilliTime() - last_dump_time_ms; + if (interval < kIntervalMillis * try_times) { + return true; + } else { + return false; + } +} + bool Mutex::ExclusiveTryLock(Thread* self) { DCHECK(self == nullptr || self == Thread::Current()); if (kDebugLocking && !recursive_) { |