From bcf597ce00b63a04703e6a96414b5fab85e86d34 Mon Sep 17 00:00:00 2001 From: Mark Benvenuto Date: Tue, 20 Feb 2018 13:23:59 -0500 Subject: SERVER-30114 Add simple slow spinlock monitoring (cherry picked from commit ba957823288d674f34c8345059f35f34cb4dd055) --- src/mongo/util/tcmalloc_server_status_section.cpp | 2 + .../gperftools-2.2/src/base/spinlock.cc | 77 ++++++++++++++++++++++ src/third_party/gperftools-2.2/src/base/spinlock.h | 6 ++ src/third_party/gperftools-2.2/src/tcmalloc.cc | 5 ++ 4 files changed, 90 insertions(+) diff --git a/src/mongo/util/tcmalloc_server_status_section.cpp b/src/mongo/util/tcmalloc_server_status_section.cpp index c2404e4f94c..c689b66eaf9 100644 --- a/src/mongo/util/tcmalloc_server_status_section.cpp +++ b/src/mongo/util/tcmalloc_server_status_section.cpp @@ -176,6 +176,8 @@ public: sub, "pageheap_reserve_count", "tcmalloc.pageheap_reserve_count"); appendNumericPropertyIfAvailable( sub, "pageheap_total_reserve_bytes", "tcmalloc.pageheap_total_reserve_bytes"); + appendNumericPropertyIfAvailable( + sub, "spinlock_total_delay_ns", "tcmalloc.spinlock_total_delay_ns"); #if MONGO_HAVE_GPERFTOOLS_SIZE_CLASS_STATS if (verbosity >= 2) { diff --git a/src/third_party/gperftools-2.2/src/base/spinlock.cc b/src/third_party/gperftools-2.2/src/base/spinlock.cc index 2021fecaabf..534634c424c 100644 --- a/src/third_party/gperftools-2.2/src/base/spinlock.cc +++ b/src/third_party/gperftools-2.2/src/base/spinlock.cc @@ -56,6 +56,47 @@ const base::LinkerInitialized SpinLock::LINKER_INITIALIZED = base::LINKER_INITIALIZED; namespace { + +const int64_t kNanoPerSec = uint64_t(1000) * 1000 * 1000; + +#ifdef _WIN32 +int64_t frequency; + +void InitTimer() { + LARGE_INTEGER large_freq; + QueryPerformanceFrequency(&large_freq); + frequency = large_freq.QuadPart; +} + +int64_t NowMonotonic() { + LARGE_INTEGER time_value; + QueryPerformanceCounter(&time_value); + return time_value.QuadPart; +} + +int64_t TicksToNanos(int64_t timer_value) { + return timer_value * kNanoPerSec / frequency; +} + +#else // _WIN32 +void InitTimer() {} + +int64_t NowMonotonic() { + struct timespec t; + + if (clock_gettime(CLOCK_MONOTONIC, &t)) { + return 0; + } + + return (static_cast(t.tv_sec) * kNanoPerSec) + t.tv_nsec; +} + +int64_t TicksToNanos(int64_t timer_value) { + return timer_value; +} + +#endif // _WIN32 + struct SpinLock_InitHelper { SpinLock_InitHelper() { // On multi-cpu machines, spin for longer before yielding @@ -63,6 +104,8 @@ struct SpinLock_InitHelper { if (NumCPUs() > 1) { adaptive_spin_count = 1000; } + + InitTimer(); } }; @@ -71,6 +114,31 @@ struct SpinLock_InitHelper { // but nothing lock-intensive should be going on at that time. static SpinLock_InitHelper init_helper; +// The current version of atomic_ops.h lacks base::subtle::Barrier_AtomicIncrement +// so a CAS loop is used instead. +void NoBarrier_AtomicAdd(volatile base::subtle::Atomic64* ptr, + base::subtle::Atomic64 increment) { + base::subtle::Atomic64 base_value = base::subtle::NoBarrier_Load(ptr); + while (true) { + base::subtle::Atomic64 new_value; + base::subtle::NoBarrier_Store(&new_value, base::subtle::NoBarrier_Load(&base_value) + + base::subtle::NoBarrier_Load(&increment)); + + // Swap in the new incremented value. + base::subtle::Atomic64 cas_result = base::subtle::Acquire_CompareAndSwap( + ptr, base_value, new_value); + + // Check if the increment succeeded. + if (cas_result == base_value) { + return; + } + + // If the increment failed, just use the previous value as the value to + // add our increment to. + base_value = cas_result; + }; +} + } // unnamed namespace // Monitor the lock to see if its value changes within some time period @@ -92,6 +160,8 @@ Atomic32 SpinLock::SpinLoop(int64 initial_wait_timestamp, return lock_value; } +base::subtle::Atomic64 SpinLock::totalDelayNanos_ = 0; + void SpinLock::SlowLock() { // The lock was not obtained initially, so this thread needs to wait for // it. Record the current timestamp in the local variable wait_start_time @@ -102,6 +172,7 @@ void SpinLock::SlowLock() { Atomic32 lock_value = SpinLoop(wait_start_time, &wait_cycles); int lock_wait_call_count = 0; + int64_t start = 0; while (lock_value != kSpinLockFree) { // If the lock is currently held, but not marked as having a sleeper, mark // it as having a sleeper. @@ -129,12 +200,18 @@ void SpinLock::SlowLock() { } // Wait for an OS specific delay. + start = NowMonotonic(); base::internal::SpinLockDelay(&lockword_, lock_value, ++lock_wait_call_count); // Spin again after returning from the wait routine to give this thread // some chance of obtaining the lock. lock_value = SpinLoop(wait_start_time, &wait_cycles); } + + if (start) { + NoBarrier_AtomicAdd(&totalDelayNanos_, static_cast( + TicksToNanos(NowMonotonic() - start))); + } } // The wait time for contentionz lock profiling must fit into 32 bits. diff --git a/src/third_party/gperftools-2.2/src/base/spinlock.h b/src/third_party/gperftools-2.2/src/base/spinlock.h index 033a75e79f4..c9213bb21b9 100644 --- a/src/third_party/gperftools-2.2/src/base/spinlock.h +++ b/src/third_party/gperftools-2.2/src/base/spinlock.h @@ -109,6 +109,10 @@ class LOCKABLE SpinLock { return base::subtle::NoBarrier_Load(&lockword_) != kSpinLockFree; } + static base::subtle::Atomic64 GetTotalDelayNanos() { + return base::subtle::NoBarrier_Load(&totalDelayNanos_); + } + static const base::LinkerInitialized LINKER_INITIALIZED; // backwards compat private: enum { kSpinLockFree = 0 }; @@ -117,6 +121,8 @@ class LOCKABLE SpinLock { volatile Atomic32 lockword_; + static base::subtle::Atomic64 totalDelayNanos_; + void SlowLock(); void SlowUnlock(uint64 wait_cycles); Atomic32 SpinLoop(int64 initial_wait_timestamp, Atomic32* wait_cycles); diff --git a/src/third_party/gperftools-2.2/src/tcmalloc.cc b/src/third_party/gperftools-2.2/src/tcmalloc.cc index 688dd92abfb..32121943bea 100644 --- a/src/third_party/gperftools-2.2/src/tcmalloc.cc +++ b/src/third_party/gperftools-2.2/src/tcmalloc.cc @@ -778,6 +778,11 @@ class TCMallocImplementation : public MallocExtension { return true; } + if (strcmp(name, "tcmalloc.spinlock_total_delay_ns") == 0) { + *value = SpinLock::GetTotalDelayNanos(); + return true; + } + return false; } -- cgit v1.2.1