| // Copyright 2020 The Cobalt Authors. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| #include "starboard/common/experimental/concurrency_debug.h" |
| |
| #if SB_ENABLE_CONCURRENTY_DEBUG |
| |
| #include <algorithm> |
| #include <string> |
| #include <vector> |
| |
| #include "starboard/atomic.h" |
| #include "starboard/common/log.h" |
| #include "starboard/common/mutex.h" |
| #include "starboard/shared/posix/time_internal.h" |
| #include "starboard/system.h" |
| |
| namespace starboard { |
| namespace experimental { |
| namespace { |
| |
| const int kMaxSymbolNameLength = 1024; |
| // Usually there are quite a few contentions at app startup, and it could also |
| // be unsafe to log as the log system may not be ready. So ignore the first |
| // few contentions at app startup set by the folloing constant. |
| const int kNumberOfInitialContentionsToIgnore = 50; |
| |
| const SbTime kMinimumWaitToLog = 5 * kSbTimeMillisecond; |
| const SbTime kLoggingInterval = 5 * kSbTimeSecond; |
| const int kStackTraceDepth = 0; |
| |
| volatile SbAtomic32 s_mutex_acquire_call_counter = 0; |
| volatile SbAtomic32 s_mutex_acquire_contention_counter = 0; |
| volatile SbAtomic32 s_mutex_max_contention_time = 0; |
| |
| } // namespace |
| |
| ScopedMutexWaitTracker::ScopedMutexWaitTracker(SbMutex* mutex) |
| : acquired_(SbMutexAcquireTry(mutex) == kSbMutexAcquired) { |
| SbAtomicNoBarrier_Increment(&s_mutex_acquire_call_counter, 1); |
| if (!acquired_) { |
| wait_start_ = SbTimeGetMonotonicNow(); |
| } |
| } |
| |
| ScopedMutexWaitTracker::~ScopedMutexWaitTracker() { |
| if (kMinimumWaitToLog == 0 || acquired_) { |
| return; |
| } |
| if (SbAtomicNoBarrier_Increment(&s_mutex_acquire_contention_counter, 1) < |
| kNumberOfInitialContentionsToIgnore) { |
| return; |
| } |
| |
| auto elapsed = SbTimeGetMonotonicNow() - wait_start_; |
| |
| for (;;) { |
| SbAtomic32 old_value = s_mutex_max_contention_time; |
| if (elapsed <= old_value) { |
| break; |
| } |
| if (SbAtomicNoBarrier_CompareAndSwap(&s_mutex_max_contention_time, |
| old_value, elapsed) == old_value) { |
| break; |
| } |
| } |
| |
| if (elapsed < kMinimumWaitToLog) { |
| return; |
| } |
| |
| SB_LOG(INFO) << "SbMutexAcquire() takes " << elapsed; |
| |
| if (kStackTraceDepth > 0) { |
| void* stack[kStackTraceDepth]; |
| int num_stack = SbSystemGetStack(stack, kStackTraceDepth); |
| |
| for (int i = 2; i < std::min(num_stack, 5); ++i) { |
| char name[kMaxSymbolNameLength + 1]; |
| if (SbSystemSymbolize(stack[i], name, kMaxSymbolNameLength)) { |
| SB_LOG(INFO) << " - " << name; |
| } else { |
| SB_LOG(INFO) << " - 0x" << stack[i]; |
| } |
| } |
| } |
| } |
| |
| } // namespace experimental |
| } // namespace starboard |
| |
| #endif // SB_ENABLE_CONCURRENTY_DEBUG |