xref: /aosp_15_r20/frameworks/av/media/utils/include/mediautils/TimeCheck.h (revision ec779b8e0859a360c3d303172224686826e6e0e1)
1 /*
2  * Copyright (C) 2018 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #pragma once
18 
19 #include <chrono>
20 #include <vector>
21 
22 #include <mediautils/TimerThread.h>
23 
24 namespace android::mediautils {
25 
26 // A class monitoring execution time for a code block (scoped variable) and causing an assert
27 // if it exceeds a certain time
28 
29 class TimeCheck {
30   public:
31 
32     // Duration for TimeCheck is based on steady_clock, typically nanoseconds.
33     using Duration = std::chrono::steady_clock::duration;
34 
35     // Duration for printing is in milliseconds, using float for additional precision.
36     using FloatMs = std::chrono::duration<float, std::milli>;
37 
38     // OnTimerFunc is the callback function with 2 parameters.
39     //  bool timeout  (which is true when the TimeCheck object
40     //                 times out, false when the TimeCheck object is
41     //                 destroyed or leaves scope before the timer expires.)
42     //  float elapsedMs (the elapsed time to this event).
43     using OnTimerFunc = std::function<void(bool /* timeout */, float /* elapsedMs */ )>;
44 
45     /**
46      * Returns the default timeout to use for TimeCheck.
47      *
48      * The default timeout of 3000ms (kDefaultTimeoutDurationMs) is chosen to be less than
49      * the system server watchdog timeout, and can be changed by the sysprop
50      * audio.timecheck.timeout_duration_ms.
51      * A second chance wait may be set to extend the check.
52      */
53     static TimeCheck::Duration getDefaultTimeoutDuration();
54 
55     /**
56      * Returns the second chance timeout to use for TimeCheck.
57      *
58      * Due to suspend abort not incrementing the monotonic clock,
59      * we allow another second chance timeout after the first timeout expires.
60      * The second chance timeout default of 2000ms (kDefaultSecondChanceDurationMs)
61      * may be changed by the sysprop audio.timecheck.second_chance_duration_ms.
62      *
63      * The total timeout is therefore
64      * getDefaultTimeoutDuration() + getDefaultSecondChanceDuration(),
65      * and the result is more stable when the monotonic clock increments during suspend.
66      */
67     static TimeCheck::Duration getDefaultSecondChanceDuration();
68 
69     /**
70      * TimeCheck is a RAII object which will notify a callback
71      * on timer expiration or when the object is deallocated.
72      *
73      * TimeCheck is used as a watchdog and aborts by default on timer expiration.
74      * When it aborts, it will also send a debugger signal to pids passed in through
75      * setAudioHalPids().
76      *
77      * If the callback function returns for timeout it will not be called again for
78      * the deallocation.
79      *
80      * \param tag       string associated with the TimeCheck object.
81      * \param onTimer   callback function with 2 parameters (described above in OnTimerFunc).
82      *                  The callback when timeout is true will be called on a different thread.
83      *                  This will cancel the callback on the destructor but is not guaranteed
84      *                  to block for callback completion if it is already in progress
85      *                  (for maximum concurrency and reduced deadlock potential), so use proper
86      *                  lifetime analysis (e.g. shared or weak pointers).
87      * \param requestedTimeoutDuration timeout in milliseconds.
88      *                  A zero timeout means no timeout is set -
89      *                  the callback is called only when
90      *                  the TimeCheck object is destroyed or leaves scope.
91      * \param secondChanceDuration additional milliseconds to wait if the first timeout expires.
92      *                  This is used to prevent false timeouts if the steady (monotonic)
93      *                  clock advances on aborted suspend.
94      * \param crashOnTimeout true if the object issues an abort on timeout.
95      */
96     explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer,
97             Duration requestedTimeoutDuration, Duration secondChanceDuration,
98             bool crashOnTimeout);
99 
100     TimeCheck() = default;
101     // Remove copy constructors as there should only be one call to the destructor.
102     // Move is kept implicitly disabled, but would be logically consistent if enabled.
103     TimeCheck(const TimeCheck& other) = delete;
104     TimeCheck& operator=(const TimeCheck&) = delete;
105 
106     ~TimeCheck();
107     static std::string toString();
108     static void setAudioHalPids(const std::vector<pid_t>& pids);
109     static std::vector<pid_t> getAudioHalPids();
110     static std::string signalAudioHals();
111 
112   private:
113     // Helper class for handling events.
114     // The usage here is const safe.
115     class TimeCheckHandler {
116     public:
117         template <typename S, typename F>
TimeCheckHandler(S && _tag,F && _onTimer,bool _crashOnTimeout,Duration _timeoutDuration,Duration _secondChanceDuration,std::chrono::system_clock::time_point _startSystemTime,pid_t _tid)118         TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout,
119             Duration _timeoutDuration, Duration _secondChanceDuration,
120             std::chrono::system_clock::time_point _startSystemTime,
121             pid_t _tid)
122             : tag(std::forward<S>(_tag))
123             , onTimer(std::forward<F>(_onTimer))
124             , crashOnTimeout(_crashOnTimeout)
125             , timeoutDuration(_timeoutDuration)
126             , secondChanceDuration(_secondChanceDuration)
127             , startSystemTime(_startSystemTime)
128             , tid(_tid)
129             {}
130         const FixedString62 tag;
131         const OnTimerFunc onTimer;
132         const bool crashOnTimeout;
133         const Duration timeoutDuration;
134         const Duration secondChanceDuration;
135         const std::chrono::system_clock::time_point startSystemTime;
136         const pid_t tid;
137         void onCancel(TimerThread::Handle handle) const;
138         void onTimeout(TimerThread::Handle handle) const;
139     };
140 
141     // Returns a string that represents the timeout vs elapsed time,
142     // and diagnostics if there are any potential issues.
143     static std::string analyzeTimeouts(
144             float timeoutMs, float secondChanceMs,
145             float elapsedSteadyMs, float elapsedSystemMs);
146 
147     static TimerThread& getTimeCheckThread();
148     static void accessAudioHalPids(std::vector<pid_t>* pids, bool update);
149 
150     // mTimeCheckHandler is immutable, prefer to be first initialized, last destroyed.
151     // Technically speaking, we do not need a shared_ptr here because TimerThread::cancelTask()
152     // is mutually exclusive of the callback, but the price paid for lifetime safety is minimal.
153     const std::shared_ptr<const TimeCheckHandler> mTimeCheckHandler;
154     const TimerThread::Handle mTimerHandle = TimerThread::INVALID_HANDLE;
155 };
156 
157 // Returns a TimeCheck object that sends info to MethodStatistics
158 // obtained from getStatisticsForClass(className).
159 TimeCheck makeTimeCheckStatsForClassMethod(
160         std::string_view className, std::string_view methodName);
161 
162 // A handy statement-like macro to put at the beginning of almost every method
163 // which calls into HAL. Note that it requires the class to implement 'getClassName'.
164 #define TIME_CHECK() auto timeCheck = \
165             mediautils::makeTimeCheckStatsForClassMethod(getClassName(), __func__)
166 
167 }  // namespace android::mediautils
168