summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorMark Benvenuto <mark.benvenuto@mongodb.com>2018-02-20 13:23:59 -0500
committerMark Benvenuto <mark.benvenuto@mongodb.com>2018-02-20 13:27:24 -0500
commitbcf597ce00b63a04703e6a96414b5fab85e86d34 (patch)
tree30ecf4041e2062553925a8ace4433be8d6951a00 /src
parent15179bd0a5e9bbe5e48a603c08e38bdb1a08a6fa (diff)
downloadmongo-bcf597ce00b63a04703e6a96414b5fab85e86d34.tar.gz
SERVER-30114 Add simple slow spinlock monitoring
(cherry picked from commit ba957823288d674f34c8345059f35f34cb4dd055)
Diffstat (limited to 'src')
-rw-r--r--src/mongo/util/tcmalloc_server_status_section.cpp2
-rw-r--r--src/third_party/gperftools-2.2/src/base/spinlock.cc77
-rw-r--r--src/third_party/gperftools-2.2/src/base/spinlock.h6
-rw-r--r--src/third_party/gperftools-2.2/src/tcmalloc.cc5
4 files changed, 90 insertions, 0 deletions
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<double>(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<base::subtle::Atomic64>(
+ 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;
}