Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 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 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 17 | #pragma once |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 18 | |
Andy Hung | f8ab093 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 19 | #include <chrono> |
Eric Laurent | 42896a0 | 2019-09-27 15:40:33 -0700 | [diff] [blame] | 20 | #include <vector> |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 21 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 22 | #include <mediautils/TimerThread.h> |
| 23 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 24 | namespace android::mediautils { |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 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 { |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 30 | public: |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 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). |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 43 | using OnTimerFunc = std::function<void(bool /* timeout */, float /* elapsedMs */ )>; |
| 44 | |
Andy Hung | cb89910 | 2024-08-06 15:17:16 -0700 | [diff] [blame] | 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(); |
Andy Hung | f8ab093 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 54 | |
Andy Hung | cb89910 | 2024-08-06 15:17:16 -0700 | [diff] [blame] | 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(); |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 68 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 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. |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 81 | * \param onTimer callback function with 2 parameters (described above in OnTimerFunc). |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 82 | * The callback when timeout is true will be called on a different thread. |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 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). |
Andy Hung | f8ab093 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 87 | * \param requestedTimeoutDuration timeout in milliseconds. |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 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. |
Andy Hung | f8ab093 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 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. |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 94 | * \param crashOnTimeout true if the object issues an abort on timeout. |
| 95 | */ |
Andy Hung | f8ab093 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 96 | explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer, |
| 97 | Duration requestedTimeoutDuration, Duration secondChanceDuration, |
| 98 | bool crashOnTimeout); |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 99 | |
| 100 | TimeCheck() = default; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 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 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 106 | ~TimeCheck(); |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 107 | static std::string toString(); |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 108 | static void setAudioHalPids(const std::vector<pid_t>& pids); |
| 109 | static std::vector<pid_t> getAudioHalPids(); |
Mikhail Naganov | 380cf5d | 2024-09-18 14:08:16 -0700 | [diff] [blame^] | 110 | static std::string signalAudioHals(); |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 111 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 112 | private: |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 113 | // Helper class for handling events. |
| 114 | // The usage here is const safe. |
| 115 | class TimeCheckHandler { |
| 116 | public: |
Andy Hung | 35f9615 | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 117 | template <typename S, typename F> |
| 118 | TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout, |
Andy Hung | f8ab093 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 119 | Duration _timeoutDuration, Duration _secondChanceDuration, |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 120 | std::chrono::system_clock::time_point _startSystemTime, |
Andy Hung | 35f9615 | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 121 | pid_t _tid) |
| 122 | : tag(std::forward<S>(_tag)) |
| 123 | , onTimer(std::forward<F>(_onTimer)) |
| 124 | , crashOnTimeout(_crashOnTimeout) |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 125 | , timeoutDuration(_timeoutDuration) |
Andy Hung | f8ab093 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 126 | , secondChanceDuration(_secondChanceDuration) |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 127 | , startSystemTime(_startSystemTime) |
Andy Hung | 35f9615 | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 128 | , tid(_tid) |
| 129 | {} |
| 130 | const FixedString62 tag; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 131 | const OnTimerFunc onTimer; |
| 132 | const bool crashOnTimeout; |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 133 | const Duration timeoutDuration; |
Andy Hung | f8ab093 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 134 | const Duration secondChanceDuration; |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 135 | const std::chrono::system_clock::time_point startSystemTime; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 136 | const pid_t tid; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 137 | void onCancel(TimerThread::Handle handle) const; |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 138 | void onTimeout(TimerThread::Handle handle) const; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 139 | }; |
| 140 | |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 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( |
Andy Hung | cb89910 | 2024-08-06 15:17:16 -0700 | [diff] [blame] | 144 | float timeoutMs, float secondChanceMs, |
| 145 | float elapsedSteadyMs, float elapsedSystemMs); |
Andy Hung | 2aa1510 | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 146 | |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 147 | static TimerThread& getTimeCheckThread(); |
| 148 | static void accessAudioHalPids(std::vector<pid_t>* pids, bool update); |
| 149 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 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; |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 154 | const TimerThread::Handle mTimerHandle = TimerThread::INVALID_HANDLE; |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 155 | }; |
| 156 | |
Andy Hung | 224f82f | 2022-03-22 00:00:49 -0700 | [diff] [blame] | 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 | |
Mikhail Naganov | 31d4665 | 2023-01-10 18:29:25 +0000 | [diff] [blame] | 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 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 167 | } // namespace android::mediautils |