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 | |
Eric Laurent | 42896a0 | 2019-09-27 15:40:33 -0700 | [diff] [blame] | 19 | #include <vector> |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 20 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 21 | #include <mediautils/TimerThread.h> |
| 22 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 23 | namespace android::mediautils { |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 24 | |
| 25 | // A class monitoring execution time for a code block (scoped variable) and causing an assert |
| 26 | // if it exceeds a certain time |
| 27 | |
| 28 | class TimeCheck { |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 29 | public: |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 30 | |
| 31 | // Duration for TimeCheck is based on steady_clock, typically nanoseconds. |
| 32 | using Duration = std::chrono::steady_clock::duration; |
| 33 | |
| 34 | // Duration for printing is in milliseconds, using float for additional precision. |
| 35 | using FloatMs = std::chrono::duration<float, std::milli>; |
| 36 | |
| 37 | // OnTimerFunc is the callback function with 2 parameters. |
| 38 | // bool timeout (which is true when the TimeCheck object |
| 39 | // times out, false when the TimeCheck object is |
| 40 | // destroyed or leaves scope before the timer expires.) |
| 41 | // float elapsedMs (the elapsed time to this event). |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 42 | using OnTimerFunc = std::function<void(bool /* timeout */, float /* elapsedMs */ )>; |
| 43 | |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 44 | // 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^] | 45 | // Note: kDefaultTimeOutMs should be no less than 2 seconds, otherwise spurious timeouts |
| 46 | // may occur with system suspend. |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 47 | static constexpr uint32_t kDefaultTimeOutMs = 5000; |
| 48 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 49 | /** |
| 50 | * TimeCheck is a RAII object which will notify a callback |
| 51 | * on timer expiration or when the object is deallocated. |
| 52 | * |
| 53 | * TimeCheck is used as a watchdog and aborts by default on timer expiration. |
| 54 | * When it aborts, it will also send a debugger signal to pids passed in through |
| 55 | * setAudioHalPids(). |
| 56 | * |
| 57 | * If the callback function returns for timeout it will not be called again for |
| 58 | * the deallocation. |
| 59 | * |
| 60 | * \param tag string associated with the TimeCheck object. |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 61 | * \param onTimer callback function with 2 parameters (described above in OnTimerFunc). |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 62 | * 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] | 63 | * This will cancel the callback on the destructor but is not guaranteed |
| 64 | * to block for callback completion if it is already in progress |
| 65 | * (for maximum concurrency and reduced deadlock potential), so use proper |
| 66 | * lifetime analysis (e.g. shared or weak pointers). |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 67 | * \param requestedTimeoutMs timeout in milliseconds. |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 68 | * A zero timeout means no timeout is set - |
| 69 | * the callback is called only when |
| 70 | * the TimeCheck object is destroyed or leaves scope. |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 71 | * \param crashOnTimeout true if the object issues an abort on timeout. |
| 72 | */ |
Andy Hung | c8c2dde | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 73 | explicit TimeCheck(std::string_view tag, OnTimerFunc&& onTimer = {}, |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 74 | uint32_t requestedTimeoutMs = kDefaultTimeOutMs, bool crashOnTimeout = true); |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 75 | |
| 76 | TimeCheck() = default; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 77 | // Remove copy constructors as there should only be one call to the destructor. |
| 78 | // Move is kept implicitly disabled, but would be logically consistent if enabled. |
| 79 | TimeCheck(const TimeCheck& other) = delete; |
| 80 | TimeCheck& operator=(const TimeCheck&) = delete; |
| 81 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 82 | ~TimeCheck(); |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 83 | static std::string toString(); |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 84 | static void setAudioHalPids(const std::vector<pid_t>& pids); |
| 85 | static std::vector<pid_t> getAudioHalPids(); |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 86 | |
Ytai Ben-Tsvi | 1ea62c9 | 2021-11-10 14:38:27 -0800 | [diff] [blame] | 87 | private: |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 88 | // Helper class for handling events. |
| 89 | // The usage here is const safe. |
| 90 | class TimeCheckHandler { |
| 91 | public: |
Andy Hung | c8c2dde | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 92 | template <typename S, typename F> |
| 93 | TimeCheckHandler(S&& _tag, F&& _onTimer, bool _crashOnTimeout, |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 94 | Duration _timeoutDuration, |
| 95 | std::chrono::system_clock::time_point _startSystemTime, |
Andy Hung | c8c2dde | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 96 | pid_t _tid) |
| 97 | : tag(std::forward<S>(_tag)) |
| 98 | , onTimer(std::forward<F>(_onTimer)) |
| 99 | , crashOnTimeout(_crashOnTimeout) |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 100 | , timeoutDuration(_timeoutDuration) |
| 101 | , startSystemTime(_startSystemTime) |
Andy Hung | c8c2dde | 2022-07-15 15:18:59 -0700 | [diff] [blame] | 102 | , tid(_tid) |
| 103 | {} |
| 104 | const FixedString62 tag; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 105 | const OnTimerFunc onTimer; |
| 106 | const bool crashOnTimeout; |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 107 | const Duration timeoutDuration; |
| 108 | const std::chrono::system_clock::time_point startSystemTime; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 109 | const pid_t tid; |
| 110 | |
| 111 | void onCancel(TimerThread::Handle handle) const; |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 112 | void onTimeout(TimerThread::Handle handle) const; |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 113 | }; |
| 114 | |
Andy Hung | df1ed5c | 2022-06-13 19:49:43 -0700 | [diff] [blame^] | 115 | // Returns a string that represents the timeout vs elapsed time, |
| 116 | // and diagnostics if there are any potential issues. |
| 117 | static std::string analyzeTimeouts( |
| 118 | float timeoutMs, float elapsedSteadyMs, float elapsedSystemMs); |
| 119 | |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 120 | static TimerThread& getTimeCheckThread(); |
| 121 | static void accessAudioHalPids(std::vector<pid_t>* pids, bool update); |
| 122 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 123 | // mTimeCheckHandler is immutable, prefer to be first initialized, last destroyed. |
| 124 | // Technically speaking, we do not need a shared_ptr here because TimerThread::cancelTask() |
| 125 | // is mutually exclusive of the callback, but the price paid for lifetime safety is minimal. |
| 126 | const std::shared_ptr<const TimeCheckHandler> mTimeCheckHandler; |
Andy Hung | a2a1ac3 | 2022-03-18 16:12:11 -0700 | [diff] [blame] | 127 | const TimerThread::Handle mTimerHandle = TimerThread::INVALID_HANDLE; |
Eric Laurent | 3528c93 | 2018-02-23 17:17:22 -0800 | [diff] [blame] | 128 | }; |
| 129 | |
Andy Hung | 224f82f | 2022-03-22 00:00:49 -0700 | [diff] [blame] | 130 | // Returns a TimeCheck object that sends info to MethodStatistics |
| 131 | // obtained from getStatisticsForClass(className). |
| 132 | TimeCheck makeTimeCheckStatsForClassMethod( |
| 133 | std::string_view className, std::string_view methodName); |
| 134 | |
Andy Hung | 5c6d68a | 2022-03-09 21:54:59 -0800 | [diff] [blame] | 135 | } // namespace android::mediautils |