1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2018 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   You may obtain a copy of the License at
8  *
9  *       http://www.apache.org/licenses/LICENSE-2.0
10  *
11  *   Unless required by applicable law or agreed to in writing, software
12  *   distributed under the License is distributed on an "AS IS" BASIS,
13  *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  *   See the License for the specific language governing permissions and
15  *   limitations under the License.
16  */
17 
18 #include <chrono>
19 
20 #include "phosphor.h"
21 #include "trace_log.h"
22 
23 namespace phosphor {
24 struct tracepoint_info;
25 
26 /**
27  * RAII-style object which captures the arguments for a scoped event.
28  *
29  * If enabled==true, saves the time of object creation; upon destruction
30  * records end time and logs an event.
31  * If !enabled; then no times are recorded and no event logged.
32  */
33 template <typename T, typename U>
34 struct ScopedEventGuard {
ScopedEventGuardphosphor::ScopedEventGuard35     ScopedEventGuard(const tracepoint_info* tpi_,
36                      bool enabled_,
37                      T arg1_,
38                      U arg2_)
39         : tpi(tpi_), enabled(enabled_), arg1(arg1_), arg2(arg2_) {
40         if (enabled) {
41             start = std::chrono::steady_clock::now();
42         }
43     }
44 
~ScopedEventGuardphosphor::ScopedEventGuard45     ~ScopedEventGuard() {
46         if (enabled) {
47             const auto end = std::chrono::steady_clock::now();
48             TraceLog::getInstance().logEvent(
49                     tpi, start, end - start, arg1, arg2);
50         }
51     }
52 
53     const tracepoint_info* tpi;
54     const bool enabled;
55     const T arg1;
56     const U arg2;
57     std::chrono::steady_clock::time_point start;
58 };
59 
60 /**
61  * RAII-style object to record events for acquiring and locking a mutex.
62  * Locks the underlying mutex on construction, unlocks when it goes out of
63  * scope; measuring two timespans:
64  * 1. Time taken to acquire the mutex.
65  * 2. Time the mutex is held for.
66  *
67  * @tparam Mutex Type of mutex to guard.
68  */
69 template <class Mutex>
70 class MutexEventGuard {
71 public:
72     /**
73      * Acquires ownership of the specified mutex.
74      *
75      * If enabled is true, records the time spent waiting for the lock. Upon
76      * destruction, events will be logged with the specified details.
77      * @threshold Minimum duration that either the lock or acquire span must
78      * take for events to be logged.
79      */
MutexEventGuard(const tracepoint_info* tpiWait_, const tracepoint_info* tpiHeld_, bool enabled_, Mutex& mutex_, std::chrono::steady_clock::duration threshold_ = std::chrono::steady_clock::duration::zero())80     MutexEventGuard(const tracepoint_info* tpiWait_,
81                     const tracepoint_info* tpiHeld_,
82                     bool enabled_,
83                     Mutex& mutex_,
84                     std::chrono::steady_clock::duration threshold_ =
85                             std::chrono::steady_clock::duration::zero())
86         : tpiWait(tpiWait_),
87           tpiHeld(tpiHeld_),
88           enabled(enabled_),
89           mutex(mutex_),
90           threshold(threshold_) {
91         if (enabled) {
92             start = std::chrono::steady_clock::now();
93             mutex.lock();
94             lockedAt = std::chrono::steady_clock::now();
95         } else {
96             mutex.lock();
97         }
98     }
99 
100     /// Unlocks the mutex, and records trace events if enabled.
~MutexEventGuard()101     ~MutexEventGuard() {
102         mutex.unlock();
103         if (enabled) {
104             releasedAt = std::chrono::steady_clock::now();
105             const auto waitTime = lockedAt - start;
106             const auto heldTime = releasedAt - lockedAt;
107             if (waitTime > threshold || heldTime > threshold) {
108                 auto& traceLog = TraceLog::getInstance();
109                 traceLog.logEvent(tpiWait,
110                                   start,
111                                   waitTime,
112                                   reinterpret_cast<void*>(&mutex),
113                                   NoneType());
114                 traceLog.logEvent(tpiHeld,
115                                   lockedAt,
116                                   heldTime,
117                                   reinterpret_cast<void*>(&mutex),
118                                   NoneType());
119             }
120         }
121     }
122 
123 private:
124     const tracepoint_info* tpiWait;
125     const tracepoint_info* tpiHeld;
126     const bool enabled;
127     Mutex& mutex;
128     const std::chrono::steady_clock::duration threshold;
129     std::chrono::steady_clock::time_point start;
130     std::chrono::steady_clock::time_point lockedAt;
131     std::chrono::steady_clock::time_point releasedAt;
132 };
133 
134 } // namespace phosphor
135