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