Pascal Mütschard | d56514e | 2024-05-24 17:37:13 +0200 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2024 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 | #include "JankTracker.h" |
| 18 | |
| 19 | #include <android/gui/IJankListener.h> |
| 20 | #include "BackgroundExecutor.h" |
| 21 | |
| 22 | namespace android { |
| 23 | |
| 24 | namespace { |
| 25 | |
| 26 | constexpr size_t kJankDataBatchSize = 50; |
| 27 | |
| 28 | } // anonymous namespace |
| 29 | |
| 30 | std::atomic<size_t> JankTracker::sListenerCount(0); |
| 31 | std::atomic<bool> JankTracker::sCollectAllJankDataForTesting(false); |
| 32 | |
| 33 | JankTracker::~JankTracker() {} |
| 34 | |
| 35 | void JankTracker::addJankListener(int32_t layerId, sp<IBinder> listener) { |
| 36 | // Increment right away, so that if an onJankData call comes in before the background thread has |
| 37 | // added this listener, it will not drop the data. |
| 38 | sListenerCount++; |
| 39 | |
| 40 | BackgroundExecutor::getLowPriorityInstance().sendCallbacks( |
| 41 | {[layerId, listener = std::move(listener)]() { |
| 42 | JankTracker& tracker = getInstance(); |
| 43 | const std::lock_guard<std::mutex> _l(tracker.mLock); |
| 44 | tracker.addJankListenerLocked(layerId, listener); |
| 45 | }}); |
| 46 | } |
| 47 | |
| 48 | void JankTracker::flushJankData(int32_t layerId) { |
| 49 | BackgroundExecutor::getLowPriorityInstance().sendCallbacks( |
| 50 | {[layerId]() { getInstance().doFlushJankData(layerId); }}); |
| 51 | } |
| 52 | |
| 53 | void JankTracker::removeJankListener(int32_t layerId, sp<IBinder> listener, int64_t afterVsync) { |
| 54 | BackgroundExecutor::getLowPriorityInstance().sendCallbacks( |
| 55 | {[layerId, listener = std::move(listener), afterVsync]() { |
| 56 | JankTracker& tracker = getInstance(); |
| 57 | const std::lock_guard<std::mutex> _l(tracker.mLock); |
| 58 | tracker.markJankListenerForRemovalLocked(layerId, listener, afterVsync); |
| 59 | }}); |
| 60 | } |
| 61 | |
| 62 | void JankTracker::onJankData(int32_t layerId, gui::JankData data) { |
| 63 | if (sListenerCount == 0) { |
| 64 | return; |
| 65 | } |
| 66 | |
| 67 | BackgroundExecutor::getLowPriorityInstance().sendCallbacks( |
| 68 | {[layerId, data = std::move(data)]() { |
| 69 | JankTracker& tracker = getInstance(); |
| 70 | |
| 71 | tracker.mLock.lock(); |
| 72 | bool hasListeners = tracker.mJankListeners.count(layerId) > 0; |
| 73 | tracker.mLock.unlock(); |
| 74 | |
| 75 | if (!hasListeners && !sCollectAllJankDataForTesting) { |
| 76 | return; |
| 77 | } |
| 78 | |
| 79 | tracker.mJankDataLock.lock(); |
| 80 | tracker.mJankData.emplace(layerId, data); |
| 81 | size_t count = tracker.mJankData.count(layerId); |
| 82 | tracker.mJankDataLock.unlock(); |
| 83 | |
| 84 | if (count >= kJankDataBatchSize && !sCollectAllJankDataForTesting) { |
| 85 | tracker.doFlushJankData(layerId); |
| 86 | } |
| 87 | }}); |
| 88 | } |
| 89 | |
| 90 | void JankTracker::addJankListenerLocked(int32_t layerId, sp<IBinder> listener) { |
| 91 | for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { |
| 92 | if (it->second.mListener == listener) { |
| 93 | // Undo the duplicate increment in addJankListener. |
| 94 | sListenerCount--; |
| 95 | return; |
| 96 | } |
| 97 | } |
| 98 | |
| 99 | mJankListeners.emplace(layerId, std::move(listener)); |
| 100 | } |
| 101 | |
| 102 | void JankTracker::doFlushJankData(int32_t layerId) { |
| 103 | std::vector<gui::JankData> jankData; |
| 104 | int64_t maxVsync = transferAvailableJankData(layerId, jankData); |
| 105 | |
| 106 | std::vector<sp<IBinder>> toSend; |
| 107 | |
| 108 | mLock.lock(); |
| 109 | for (auto it = mJankListeners.find(layerId); it != mJankListeners.end();) { |
| 110 | if (!jankData.empty()) { |
| 111 | toSend.emplace_back(it->second.mListener); |
| 112 | } |
| 113 | |
| 114 | int64_t removeAfter = it->second.mRemoveAfter; |
| 115 | if (removeAfter != -1 && removeAfter <= maxVsync) { |
| 116 | it = mJankListeners.erase(it); |
| 117 | sListenerCount--; |
| 118 | } else { |
| 119 | it++; |
| 120 | } |
| 121 | } |
| 122 | mLock.unlock(); |
| 123 | |
| 124 | for (const auto& listener : toSend) { |
| 125 | binder::Status status = interface_cast<gui::IJankListener>(listener)->onJankData(jankData); |
| 126 | if (status.exceptionCode() == binder::Status::EX_NULL_POINTER) { |
| 127 | // Remove any listeners, where the App side has gone away, without |
| 128 | // deregistering. |
| 129 | dropJankListener(layerId, listener); |
| 130 | } |
| 131 | } |
| 132 | } |
| 133 | |
| 134 | void JankTracker::markJankListenerForRemovalLocked(int32_t layerId, sp<IBinder> listener, |
| 135 | int64_t afterVysnc) { |
| 136 | for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { |
| 137 | if (it->second.mListener == listener) { |
| 138 | it->second.mRemoveAfter = std::max(static_cast<int64_t>(0), afterVysnc); |
| 139 | return; |
| 140 | } |
| 141 | } |
| 142 | } |
| 143 | |
| 144 | int64_t JankTracker::transferAvailableJankData(int32_t layerId, |
| 145 | std::vector<gui::JankData>& outJankData) { |
| 146 | const std::lock_guard<std::mutex> _l(mJankDataLock); |
| 147 | int64_t maxVsync = 0; |
| 148 | auto range = mJankData.equal_range(layerId); |
| 149 | for (auto it = range.first; it != range.second;) { |
| 150 | maxVsync = std::max(it->second.frameVsyncId, maxVsync); |
| 151 | outJankData.emplace_back(std::move(it->second)); |
| 152 | it = mJankData.erase(it); |
| 153 | } |
| 154 | return maxVsync; |
| 155 | } |
| 156 | |
| 157 | void JankTracker::dropJankListener(int32_t layerId, sp<IBinder> listener) { |
| 158 | const std::lock_guard<std::mutex> _l(mLock); |
| 159 | for (auto it = mJankListeners.find(layerId); it != mJankListeners.end(); it++) { |
| 160 | if (it->second.mListener == listener) { |
| 161 | mJankListeners.erase(it); |
| 162 | sListenerCount--; |
| 163 | return; |
| 164 | } |
| 165 | } |
| 166 | } |
| 167 | |
| 168 | void JankTracker::clearAndStartCollectingAllJankDataForTesting() { |
| 169 | BackgroundExecutor::getLowPriorityInstance().flushQueue(); |
| 170 | |
| 171 | // Clear all past tracked jank data. |
| 172 | JankTracker& tracker = getInstance(); |
| 173 | const std::lock_guard<std::mutex> _l(tracker.mJankDataLock); |
| 174 | tracker.mJankData.clear(); |
| 175 | |
| 176 | // Pretend there's at least one listener. |
| 177 | sListenerCount++; |
| 178 | sCollectAllJankDataForTesting = true; |
| 179 | } |
| 180 | |
| 181 | std::vector<gui::JankData> JankTracker::getCollectedJankDataForTesting(int32_t layerId) { |
| 182 | JankTracker& tracker = getInstance(); |
| 183 | const std::lock_guard<std::mutex> _l(tracker.mJankDataLock); |
| 184 | |
| 185 | auto range = tracker.mJankData.equal_range(layerId); |
| 186 | std::vector<gui::JankData> result; |
| 187 | std::transform(range.first, range.second, std::back_inserter(result), |
| 188 | [](std::pair<int32_t, gui::JankData> layerIdToJankData) { |
| 189 | return layerIdToJankData.second; |
| 190 | }); |
| 191 | |
| 192 | return result; |
| 193 | } |
| 194 | |
| 195 | void JankTracker::clearAndStopCollectingAllJankDataForTesting() { |
| 196 | // Undo startCollectingAllJankDataForTesting. |
| 197 | sListenerCount--; |
| 198 | sCollectAllJankDataForTesting = false; |
| 199 | |
| 200 | // Clear all tracked jank data. |
| 201 | JankTracker& tracker = getInstance(); |
| 202 | const std::lock_guard<std::mutex> _l(tracker.mJankDataLock); |
| 203 | tracker.mJankData.clear(); |
| 204 | } |
| 205 | |
| 206 | } // namespace android |