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 | 741b3dd | 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 | df1ed5c | 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 | |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 45 | // The default timeout is chosen to be less than system server watchdog timeout |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 46 | // Note: kDefaultTimeOutMs should be no less than 2 seconds, otherwise spurious timeouts |
| 47 | // may occur with system suspend. |
Andy Hung | 741b3dd | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 48 | static constexpr TimeCheck::Duration kDefaultTimeoutDuration = std::chrono::milliseconds(3000); |
| 49 | |
| 50 | // Due to suspend abort not incrementing the monotonic clock, |
| 51 | // we allow another second chance timeout after the first timeout expires. |
| 52 | // |
| 53 | // The total timeout is therefore kDefaultTimeoutDuration + kDefaultSecondChanceDuration, |
| 54 | // and the result is more stable when the monotonic clock increments during suspend. |
| 55 | // |
| 56 | static constexpr TimeCheck::Duration kDefaultSecondChanceDuration = |
| 57 | std::chrono::milliseconds(2000); |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 58 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 59 | /** |
| 60 | * TimeCheck is a RAII object which will notify a callback |
| 61 | * on timer expiration or when the object is deallocated. |
| 62 | * |
| 63 | * TimeCheck is used as a watchdog and aborts by default on timer expiration. |
| 64 | * When it aborts, it will also send a debugger signal to pids passed in through |
| 65 | * setAudioHalPids(). |
| 66 | * |
| 67 | * If the callback function returns for timeout it will not be called again for |
| 68 | * the deallocation. |
| 69 | * |
| 70 | * \param tag string associated with the TimeCheck object. |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 71 | * \param onTimer callback function with 2 parameters (described above in OnTimerFunc). |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 72 | * 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] | 73 | * This will cancel the callback on the destructor but is not guaranteed |
| 74 | * to block for callback completion if it is already in progress |
| 75 | * (for maximum concurrency and reduced deadlock potential), so use proper |
| 76 | * lifetime analysis (e.g. shared or weak pointers). |
Andy Hung | 741b3dd | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 77 | * \param requestedTimeoutDuration timeout in milliseconds. |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 78 | * A zero timeout means no timeout is set - |
| 79 | * the callback is called only when |
| 80 | * the TimeCheck object is destroyed or leaves scope. |
Andy Hung | 741b3dd | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 81 | * \param secondChanceDuration additional milliseconds to wait if the first timeout expires. |
| 82 | * This is used to prevent false timeouts if the steady (monotonic) |
| 83 | * clock advances on aborted suspend. |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 84 | * \param crashOnTimeout true if the object issues an abort on timeout. |
| 85 | */ |
Andy Hung | 741b3dd | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 86 | explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer, |
| 87 | Duration requestedTimeoutDuration, Duration secondChanceDuration, |
| 88 | bool crashOnTimeout); |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 89 | |
| 90 | TimeCheck() = default; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 91 | // Remove copy constructors as there should only be one call to the destructor. |
| 92 | // Move is kept implicitly disabled, but would be logically consistent if enabled. |
| 93 | TimeCheck(const TimeCheck& other) = delete; |
| 94 | TimeCheck& operator=(const TimeCheck&) = delete; |
| 95 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 96 | ~TimeCheck(); |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 97 | static std::string toString(); |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 98 | static void setAudioHalPids(const std::vector<pid_t>& pids); |
| 99 | static std::vector<pid_t> getAudioHalPids(); |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 100 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 101 | private: |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 102 | // Helper class for handling events. |
| 103 | // The usage here is const safe. |
| 104 | class TimeCheckHandler { |
| 105 | public: |
Andy Hung | c8c2dde | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 106 | template <typename S, typename F> |
| 107 | TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout, |
Andy Hung | 741b3dd | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 108 | Duration _timeoutDuration, Duration _secondChanceDuration, |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 109 | std::chrono::system_clock::time_point _startSystemTime, |
Andy Hung | c8c2dde | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 110 | pid_t _tid) |
| 111 | : tag(std::forward<S>(_tag)) |
| 112 | , onTimer(std::forward<F>(_onTimer)) |
| 113 | , crashOnTimeout(_crashOnTimeout) |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 114 | , timeoutDuration(_timeoutDuration) |
Andy Hung | 741b3dd | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 115 | , secondChanceDuration(_secondChanceDuration) |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 116 | , startSystemTime(_startSystemTime) |
Andy Hung | c8c2dde | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 117 | , tid(_tid) |
| 118 | {} |
| 119 | const FixedString62 tag; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 120 | const OnTimerFunc onTimer; |
| 121 | const bool crashOnTimeout; |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 122 | const Duration timeoutDuration; |
Andy Hung | 741b3dd | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 123 | const Duration secondChanceDuration; |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 124 | const std::chrono::system_clock::time_point startSystemTime; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 125 | const pid_t tid; |
| 126 | |
| 127 | void onCancel(TimerThread::Handle handle) const; |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 128 | void onTimeout(TimerThread::Handle handle) const; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 129 | }; |
| 130 | |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame] | 131 | // Returns a string that represents the timeout vs elapsed time, |
| 132 | // and diagnostics if there are any potential issues. |
| 133 | static std::string analyzeTimeouts( |
| 134 | float timeoutMs, float elapsedSteadyMs, float elapsedSystemMs); |
| 135 | |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 136 | static TimerThread& getTimeCheckThread(); |
| 137 | static void accessAudioHalPids(std::vector<pid_t>* pids, bool update); |
| 138 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 139 | // mTimeCheckHandler is immutable, prefer to be first initialized, last destroyed. |
| 140 | // Technically speaking, we do not need a shared_ptr here because TimerThread::cancelTask() |
| 141 | // is mutually exclusive of the callback, but the price paid for lifetime safety is minimal. |
| 142 | const std::shared_ptr<const TimeCheckHandler> mTimeCheckHandler; |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 143 | const TimerThread::Handle mTimerHandle = TimerThread::INVALID_HANDLE; |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 144 | }; |
| 145 | |
Andy Hung | 224f82f | 2022-03-22 00:00:49 -0700 | [diff] [blame] | 146 | // Returns a TimeCheck object that sends info to MethodStatistics |
| 147 | // obtained from getStatisticsForClass(className). |
| 148 | TimeCheck makeTimeCheckStatsForClassMethod( |
| 149 | std::string_view className, std::string_view methodName); |
| 150 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 151 | } // namespace android::mediautils |