1 2 /** 3 * Copyright (C) 2018-present MongoDB, Inc. 4 * 5 * This program is free software: you can redistribute it and/or modify 6 * it under the terms of the Server Side Public License, version 1, 7 * as published by MongoDB, Inc. 8 * 9 * This program is distributed in the hope that it will be useful, 10 * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 * Server Side Public License for more details. 13 * 14 * You should have received a copy of the Server Side Public License 15 * along with this program. If not, see 16 * <http://www.mongodb.com/licensing/server-side-public-license>. 17 * 18 * As a special exception, the copyright holders give permission to link the 19 * code of portions of this program with the OpenSSL library under certain 20 * conditions as described in each individual source file and distribute 21 * linked combinations including the program with the OpenSSL library. You 22 * must comply with the Server Side Public License in all respects for 23 * all of the code used other than as permitted herein. If you modify file(s) 24 * with this exception, you may extend this exception to your version of the 25 * file(s), but you are not obligated to do so. If you do not wish to do so, 26 * delete this exception statement from your version. If you delete this 27 * exception statement from all source files in the program, then also delete 28 * it in the license file. 29 */ 30 31 #pragma once 32 33 #include "mongo/db/concurrency/lock_manager_defs.h" 34 #include "mongo/platform/atomic_word.h" 35 36 namespace mongo { 37 38 class BSONObjBuilder; 39 40 41 /** 42 * Operations for manipulating the lock statistics abstracting whether they are atomic or not. 43 */ 44 struct CounterOps { getCounterOps45 static int64_t get(const int64_t& counter) { 46 return counter; 47 } 48 getCounterOps49 static int64_t get(const AtomicInt64& counter) { 50 return counter.load(); 51 } 52 setCounterOps53 static void set(int64_t& counter, int64_t value) { 54 counter = value; 55 } 56 setCounterOps57 static void set(AtomicInt64& counter, int64_t value) { 58 counter.store(value); 59 } 60 addCounterOps61 static void add(int64_t& counter, int64_t value) { 62 counter += value; 63 } 64 addCounterOps65 static void add(int64_t& counter, const AtomicInt64& value) { 66 counter += value.load(); 67 } 68 addCounterOps69 static void add(AtomicInt64& counter, int64_t value) { 70 counter.addAndFetch(value); 71 } 72 }; 73 74 75 /** 76 * Bundle of locking statistics values. 77 */ 78 template <typename CounterType> 79 struct LockStatCounters { 80 template <typename OtherType> appendLockStatCounters81 void append(const LockStatCounters<OtherType>& other) { 82 CounterOps::add(numAcquisitions, other.numAcquisitions); 83 CounterOps::add(numWaits, other.numWaits); 84 CounterOps::add(combinedWaitTimeMicros, other.combinedWaitTimeMicros); 85 CounterOps::add(numDeadlocks, other.numDeadlocks); 86 } 87 88 template <typename OtherType> subtractLockStatCounters89 void subtract(const LockStatCounters<OtherType>& other) { 90 CounterOps::add(numAcquisitions, -other.numAcquisitions); 91 CounterOps::add(numWaits, -other.numWaits); 92 CounterOps::add(combinedWaitTimeMicros, -other.combinedWaitTimeMicros); 93 CounterOps::add(numDeadlocks, -other.numDeadlocks); 94 } 95 resetLockStatCounters96 void reset() { 97 CounterOps::set(numAcquisitions, 0); 98 CounterOps::set(numWaits, 0); 99 CounterOps::set(combinedWaitTimeMicros, 0); 100 CounterOps::set(numDeadlocks, 0); 101 } 102 103 104 CounterType numAcquisitions; 105 CounterType numWaits; 106 CounterType combinedWaitTimeMicros; 107 CounterType numDeadlocks; 108 }; 109 110 111 /** 112 * Templatized lock statistics management class, which can be specialized with atomic integers 113 * for the global stats and with regular integers for the per-locker stats. 114 */ 115 template <typename CounterType> 116 class LockStats { 117 public: 118 // Declare the type for the lock counters bundle 119 typedef LockStatCounters<CounterType> LockStatCountersType; 120 121 /** 122 * Initializes the locking statistics with zeroes (calls reset). 123 */ 124 LockStats(); 125 recordAcquisition(ResourceId resId,LockMode mode)126 void recordAcquisition(ResourceId resId, LockMode mode) { 127 CounterOps::add(get(resId, mode).numAcquisitions, 1); 128 } 129 recordWait(ResourceId resId,LockMode mode)130 void recordWait(ResourceId resId, LockMode mode) { 131 CounterOps::add(get(resId, mode).numWaits, 1); 132 } 133 recordWaitTime(ResourceId resId,LockMode mode,int64_t waitMicros)134 void recordWaitTime(ResourceId resId, LockMode mode, int64_t waitMicros) { 135 CounterOps::add(get(resId, mode).combinedWaitTimeMicros, waitMicros); 136 } 137 recordDeadlock(ResourceId resId,LockMode mode)138 void recordDeadlock(ResourceId resId, LockMode mode) { 139 CounterOps::add(get(resId, mode).numDeadlocks, 1); 140 } 141 get(ResourceId resId,LockMode mode)142 LockStatCountersType& get(ResourceId resId, LockMode mode) { 143 if (resId == resourceIdOplog) { 144 return _oplogStats.modeStats[mode]; 145 } 146 147 return _stats[resId.getType()].modeStats[mode]; 148 } 149 150 template <typename OtherType> append(const LockStats<OtherType> & other)151 void append(const LockStats<OtherType>& other) { 152 typedef LockStatCounters<OtherType> OtherLockStatCountersType; 153 154 // Append all lock stats 155 for (int i = 0; i < ResourceTypesCount; i++) { 156 for (int mode = 0; mode < LockModesCount; mode++) { 157 const OtherLockStatCountersType& otherStats = other._stats[i].modeStats[mode]; 158 LockStatCountersType& thisStats = _stats[i].modeStats[mode]; 159 thisStats.append(otherStats); 160 } 161 } 162 163 // Append the oplog stats 164 for (int mode = 0; mode < LockModesCount; mode++) { 165 const OtherLockStatCountersType& otherStats = other._oplogStats.modeStats[mode]; 166 LockStatCountersType& thisStats = _oplogStats.modeStats[mode]; 167 thisStats.append(otherStats); 168 } 169 } 170 171 template <typename OtherType> subtract(const LockStats<OtherType> & other)172 void subtract(const LockStats<OtherType>& other) { 173 typedef LockStatCounters<OtherType> OtherLockStatCountersType; 174 175 for (int i = 0; i < ResourceTypesCount; i++) { 176 for (int mode = 0; mode < LockModesCount; mode++) { 177 const OtherLockStatCountersType& otherStats = other._stats[i].modeStats[mode]; 178 LockStatCountersType& thisStats = _stats[i].modeStats[mode]; 179 thisStats.subtract(otherStats); 180 } 181 } 182 183 for (int mode = 0; mode < LockModesCount; mode++) { 184 const OtherLockStatCountersType& otherStats = other._oplogStats.modeStats[mode]; 185 LockStatCountersType& thisStats = _oplogStats.modeStats[mode]; 186 thisStats.subtract(otherStats); 187 } 188 } 189 190 void report(BSONObjBuilder* builder) const; 191 void reset(); 192 193 private: 194 // Necessary for the append call, which accepts argument of type different than our 195 // template parameter. 196 template <typename T> 197 friend class LockStats; 198 199 200 // Keep the per-mode lock stats next to each other in case we want to do fancy operations 201 // such as atomic operations on 128-bit values. 202 struct PerModeLockStatCounters { 203 LockStatCountersType modeStats[LockModesCount]; 204 }; 205 206 207 void _report(BSONObjBuilder* builder, 208 const char* sectionName, 209 const PerModeLockStatCounters& stat) const; 210 211 212 // Split the lock stats per resource type and special-case the oplog so we can collect 213 // more detailed stats for it. 214 PerModeLockStatCounters _stats[ResourceTypesCount]; 215 PerModeLockStatCounters _oplogStats; 216 }; 217 218 typedef LockStats<int64_t> SingleThreadedLockStats; 219 typedef LockStats<AtomicInt64> AtomicLockStats; 220 221 222 /** 223 * Reports instance-wide locking statistics, which can then be converted to BSON or logged. 224 */ 225 void reportGlobalLockingStats(SingleThreadedLockStats* outStats); 226 227 /** 228 * Currently used for testing only. 229 */ 230 void resetGlobalLockStats(); 231 232 } // namespace mongo 233