Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright (C) 2021 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 | #define LOG_TAG "LatencyAggregator" |
| 18 | #include "LatencyAggregator.h" |
| 19 | |
| 20 | #include <inttypes.h> |
| 21 | |
| 22 | #include <android-base/stringprintf.h> |
| 23 | #include <input/Input.h> |
| 24 | #include <log/log.h> |
| 25 | #include <server_configurable_flags/get_flags.h> |
| 26 | |
| 27 | using android::base::StringPrintf; |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 28 | using dist_proc::aggregation::KllQuantile; |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 29 | using std::chrono_literals::operator""ms; |
| 30 | |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 31 | // Convert the provided nanoseconds into hundreds of microseconds. |
| 32 | // Use hundreds of microseconds (as opposed to microseconds) to preserve space. |
| 33 | static inline int64_t ns2hus(nsecs_t nanos) { |
| 34 | return ns2us(nanos) / 100; |
| 35 | } |
| 36 | |
| 37 | // The maximum number of events that we will store in the statistics. Any events that we will |
| 38 | // receive after we have reached this number will be ignored. We could also implement this by |
| 39 | // checking the actual size of the current data and making sure that we do not go over. However, |
| 40 | // the serialization process of sketches is too heavy (1 ms for all 14 sketches), and would be too |
| 41 | // much to do (even if infrequently). |
| 42 | // The value here has been determined empirically. |
| 43 | static constexpr size_t MAX_EVENTS_FOR_STATISTICS = 20000; |
| 44 | |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 45 | // Category (=namespace) name for the input settings that are applied at boot time |
| 46 | static const char* INPUT_NATIVE_BOOT = "input_native_boot"; |
| 47 | // Feature flag name for the threshold of end-to-end touch latency that would trigger |
| 48 | // SlowEventReported atom to be pushed |
| 49 | static const char* SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS = |
| 50 | "slow_event_min_reporting_latency_millis"; |
| 51 | // Feature flag name for the minimum delay before reporting a slow event after having just reported |
| 52 | // a slow event. This helps limit the amount of data sent to the server |
| 53 | static const char* SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS = |
| 54 | "slow_event_min_reporting_interval_millis"; |
| 55 | |
| 56 | // If an event has end-to-end latency > 200 ms, it will get reported as a slow event. |
| 57 | std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY = 200ms; |
| 58 | // If we receive two slow events less than 1 min apart, we will only report 1 of them. |
| 59 | std::chrono::milliseconds DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL = 60000ms; |
| 60 | |
| 61 | static std::chrono::milliseconds getSlowEventMinReportingLatency() { |
| 62 | std::string millis = server_configurable_flags:: |
| 63 | GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_LATENCY_MILLIS, |
| 64 | std::to_string( |
| 65 | DEFAULT_SLOW_EVENT_MIN_REPORTING_LATENCY.count())); |
| 66 | return std::chrono::milliseconds(std::stoi(millis)); |
| 67 | } |
| 68 | |
| 69 | static std::chrono::milliseconds getSlowEventMinReportingInterval() { |
| 70 | std::string millis = server_configurable_flags:: |
| 71 | GetServerConfigurableFlag(INPUT_NATIVE_BOOT, SLOW_EVENT_MIN_REPORTING_INTERVAL_MILLIS, |
| 72 | std::to_string( |
| 73 | DEFAULT_SLOW_EVENT_MIN_REPORTING_INTERVAL.count())); |
| 74 | return std::chrono::milliseconds(std::stoi(millis)); |
| 75 | } |
| 76 | |
| 77 | namespace android::inputdispatcher { |
| 78 | |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 79 | /** |
| 80 | * Same as android::util::BytesField, but doesn't store raw pointers, and therefore deletes its |
| 81 | * resources automatically. |
| 82 | */ |
| 83 | class SafeBytesField { |
| 84 | public: |
| 85 | explicit SafeBytesField(dist_proc::aggregation::KllQuantile& quantile) { |
| 86 | const zetasketch::android::AggregatorStateProto aggProto = quantile.SerializeToProto(); |
| 87 | mBuffer.resize(aggProto.ByteSizeLong()); |
| 88 | aggProto.SerializeToArray(mBuffer.data(), mBuffer.size()); |
| 89 | } |
| 90 | android::util::BytesField getBytesField() { |
| 91 | return android::util::BytesField(mBuffer.data(), mBuffer.size()); |
| 92 | } |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 93 | |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 94 | private: |
| 95 | std::vector<char> mBuffer; |
| 96 | }; |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 97 | |
| 98 | LatencyAggregator::LatencyAggregator() { |
| 99 | AStatsManager_setPullAtomCallback(android::util::INPUT_EVENT_LATENCY_SKETCH, nullptr, |
| 100 | LatencyAggregator::pullAtomCallback, this); |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 101 | dist_proc::aggregation::KllQuantileOptions options; |
| 102 | options.set_inv_eps(100); // Request precision of 1.0%, instead of default 0.1% |
| 103 | for (size_t i = 0; i < SketchIndex::SIZE; i++) { |
| 104 | mDownSketches[i] = KllQuantile::Create(options); |
| 105 | mMoveSketches[i] = KllQuantile::Create(options); |
| 106 | } |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 107 | } |
| 108 | |
| 109 | LatencyAggregator::~LatencyAggregator() { |
| 110 | AStatsManager_clearPullAtomCallback(android::util::INPUT_EVENT_LATENCY_SKETCH); |
| 111 | } |
| 112 | |
| 113 | AStatsManager_PullAtomCallbackReturn LatencyAggregator::pullAtomCallback(int32_t atomTag, |
| 114 | AStatsEventList* data, |
| 115 | void* cookie) { |
| 116 | LatencyAggregator* pAggregator = reinterpret_cast<LatencyAggregator*>(cookie); |
| 117 | if (pAggregator == nullptr) { |
| 118 | LOG_ALWAYS_FATAL("pAggregator is null!"); |
| 119 | } |
| 120 | return pAggregator->pullData(data); |
| 121 | } |
| 122 | |
| 123 | void LatencyAggregator::processTimeline(const InputEventTimeline& timeline) { |
| 124 | processStatistics(timeline); |
| 125 | processSlowEvent(timeline); |
| 126 | } |
| 127 | |
| 128 | void LatencyAggregator::processStatistics(const InputEventTimeline& timeline) { |
Siarhei Vishniakou | ad16bf2 | 2023-09-05 18:24:44 -0700 | [diff] [blame] | 129 | std::scoped_lock lock(mLock); |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 130 | // Before we do any processing, check that we have not yet exceeded MAX_SIZE |
| 131 | if (mNumSketchEventsProcessed >= MAX_EVENTS_FOR_STATISTICS) { |
| 132 | return; |
| 133 | } |
| 134 | mNumSketchEventsProcessed++; |
| 135 | |
| 136 | std::array<std::unique_ptr<KllQuantile>, SketchIndex::SIZE>& sketches = |
jioana | 97cc8ac | 2024-09-09 15:01:43 +0000 | [diff] [blame] | 137 | timeline.inputEventActionType == InputEventActionType::MOTION_ACTION_DOWN |
| 138 | ? mDownSketches |
| 139 | : mMoveSketches; |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 140 | |
| 141 | // Process common ones first |
| 142 | const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 143 | sketches[SketchIndex::EVENT_TO_READ]->Add(ns2hus(eventToRead)); |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 144 | |
| 145 | // Now process per-connection ones |
| 146 | for (const auto& [connectionToken, connectionTimeline] : timeline.connectionTimelines) { |
| 147 | if (!connectionTimeline.isComplete()) { |
| 148 | continue; |
| 149 | } |
| 150 | const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime; |
| 151 | const nsecs_t deliverToConsume = |
| 152 | connectionTimeline.consumeTime - connectionTimeline.deliveryTime; |
| 153 | const nsecs_t consumeToFinish = |
| 154 | connectionTimeline.finishTime - connectionTimeline.consumeTime; |
| 155 | const nsecs_t gpuCompletedTime = |
| 156 | connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; |
| 157 | const nsecs_t presentTime = |
| 158 | connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; |
| 159 | const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; |
| 160 | const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; |
| 161 | const nsecs_t endToEnd = presentTime - timeline.eventTime; |
| 162 | |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 163 | sketches[SketchIndex::READ_TO_DELIVER]->Add(ns2hus(readToDeliver)); |
| 164 | sketches[SketchIndex::DELIVER_TO_CONSUME]->Add(ns2hus(deliverToConsume)); |
| 165 | sketches[SketchIndex::CONSUME_TO_FINISH]->Add(ns2hus(consumeToFinish)); |
| 166 | sketches[SketchIndex::CONSUME_TO_GPU_COMPLETE]->Add(ns2hus(consumeToGpuComplete)); |
| 167 | sketches[SketchIndex::GPU_COMPLETE_TO_PRESENT]->Add(ns2hus(gpuCompleteToPresent)); |
| 168 | sketches[SketchIndex::END_TO_END]->Add(ns2hus(endToEnd)); |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 169 | } |
| 170 | } |
| 171 | |
| 172 | AStatsManager_PullAtomCallbackReturn LatencyAggregator::pullData(AStatsEventList* data) { |
Siarhei Vishniakou | ad16bf2 | 2023-09-05 18:24:44 -0700 | [diff] [blame] | 173 | std::scoped_lock lock(mLock); |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 174 | std::array<std::unique_ptr<SafeBytesField>, SketchIndex::SIZE> serializedDownData; |
| 175 | std::array<std::unique_ptr<SafeBytesField>, SketchIndex::SIZE> serializedMoveData; |
| 176 | for (size_t i = 0; i < SketchIndex::SIZE; i++) { |
| 177 | serializedDownData[i] = std::make_unique<SafeBytesField>(*mDownSketches[i]); |
| 178 | serializedMoveData[i] = std::make_unique<SafeBytesField>(*mMoveSketches[i]); |
| 179 | } |
| 180 | android::util:: |
| 181 | addAStatsEvent(data, android::util::INPUT_EVENT_LATENCY_SKETCH, |
| 182 | // DOWN sketches |
| 183 | serializedDownData[SketchIndex::EVENT_TO_READ]->getBytesField(), |
| 184 | serializedDownData[SketchIndex::READ_TO_DELIVER]->getBytesField(), |
| 185 | serializedDownData[SketchIndex::DELIVER_TO_CONSUME]->getBytesField(), |
| 186 | serializedDownData[SketchIndex::CONSUME_TO_FINISH]->getBytesField(), |
| 187 | serializedDownData[SketchIndex::CONSUME_TO_GPU_COMPLETE] |
| 188 | ->getBytesField(), |
| 189 | serializedDownData[SketchIndex::GPU_COMPLETE_TO_PRESENT] |
| 190 | ->getBytesField(), |
| 191 | serializedDownData[SketchIndex::END_TO_END]->getBytesField(), |
| 192 | // MOVE sketches |
| 193 | serializedMoveData[SketchIndex::EVENT_TO_READ]->getBytesField(), |
| 194 | serializedMoveData[SketchIndex::READ_TO_DELIVER]->getBytesField(), |
| 195 | serializedMoveData[SketchIndex::DELIVER_TO_CONSUME]->getBytesField(), |
| 196 | serializedMoveData[SketchIndex::CONSUME_TO_FINISH]->getBytesField(), |
| 197 | serializedMoveData[SketchIndex::CONSUME_TO_GPU_COMPLETE] |
| 198 | ->getBytesField(), |
| 199 | serializedMoveData[SketchIndex::GPU_COMPLETE_TO_PRESENT] |
| 200 | ->getBytesField(), |
| 201 | serializedMoveData[SketchIndex::END_TO_END]->getBytesField()); |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 202 | |
| 203 | for (size_t i = 0; i < SketchIndex::SIZE; i++) { |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 204 | mDownSketches[i]->Reset(); |
| 205 | mMoveSketches[i]->Reset(); |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 206 | } |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 207 | // Start new aggregations |
| 208 | mNumSketchEventsProcessed = 0; |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 209 | return AStatsManager_PULL_SUCCESS; |
| 210 | } |
| 211 | |
| 212 | void LatencyAggregator::processSlowEvent(const InputEventTimeline& timeline) { |
| 213 | static const std::chrono::duration sSlowEventThreshold = getSlowEventMinReportingLatency(); |
| 214 | static const std::chrono::duration sSlowEventReportingInterval = |
| 215 | getSlowEventMinReportingInterval(); |
| 216 | for (const auto& [token, connectionTimeline] : timeline.connectionTimelines) { |
| 217 | if (!connectionTimeline.isComplete()) { |
| 218 | continue; |
| 219 | } |
| 220 | mNumEventsSinceLastSlowEventReport++; |
| 221 | const nsecs_t presentTime = |
| 222 | connectionTimeline.graphicsTimeline[GraphicsTimeline::PRESENT_TIME]; |
| 223 | const std::chrono::nanoseconds endToEndLatency = |
| 224 | std::chrono::nanoseconds(presentTime - timeline.eventTime); |
| 225 | if (endToEndLatency < sSlowEventThreshold) { |
| 226 | continue; |
| 227 | } |
| 228 | // This is a slow event. Before we report it, check if we are reporting too often |
| 229 | const std::chrono::duration elapsedSinceLastReport = |
| 230 | std::chrono::nanoseconds(timeline.eventTime - mLastSlowEventTime); |
| 231 | if (elapsedSinceLastReport < sSlowEventReportingInterval) { |
| 232 | mNumSkippedSlowEvents++; |
| 233 | continue; |
| 234 | } |
| 235 | |
| 236 | const nsecs_t eventToRead = timeline.readTime - timeline.eventTime; |
| 237 | const nsecs_t readToDeliver = connectionTimeline.deliveryTime - timeline.readTime; |
| 238 | const nsecs_t deliverToConsume = |
| 239 | connectionTimeline.consumeTime - connectionTimeline.deliveryTime; |
| 240 | const nsecs_t consumeToFinish = |
| 241 | connectionTimeline.finishTime - connectionTimeline.consumeTime; |
| 242 | const nsecs_t gpuCompletedTime = |
| 243 | connectionTimeline.graphicsTimeline[GraphicsTimeline::GPU_COMPLETED_TIME]; |
| 244 | const nsecs_t consumeToGpuComplete = gpuCompletedTime - connectionTimeline.consumeTime; |
| 245 | const nsecs_t gpuCompleteToPresent = presentTime - gpuCompletedTime; |
| 246 | |
jioana | 97cc8ac | 2024-09-09 15:01:43 +0000 | [diff] [blame] | 247 | android::util::stats_write(android::util::SLOW_INPUT_EVENT_REPORTED, |
| 248 | timeline.inputEventActionType == |
| 249 | InputEventActionType::MOTION_ACTION_DOWN, |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 250 | static_cast<int32_t>(ns2us(eventToRead)), |
| 251 | static_cast<int32_t>(ns2us(readToDeliver)), |
| 252 | static_cast<int32_t>(ns2us(deliverToConsume)), |
| 253 | static_cast<int32_t>(ns2us(consumeToFinish)), |
| 254 | static_cast<int32_t>(ns2us(consumeToGpuComplete)), |
| 255 | static_cast<int32_t>(ns2us(gpuCompleteToPresent)), |
| 256 | static_cast<int32_t>(ns2us(endToEndLatency.count())), |
| 257 | static_cast<int32_t>(mNumEventsSinceLastSlowEventReport), |
| 258 | static_cast<int32_t>(mNumSkippedSlowEvents)); |
| 259 | mNumEventsSinceLastSlowEventReport = 0; |
| 260 | mNumSkippedSlowEvents = 0; |
| 261 | mLastSlowEventTime = timeline.readTime; |
| 262 | } |
| 263 | } |
| 264 | |
Siarhei Vishniakou | 4c9d6ff | 2023-04-18 11:23:20 -0700 | [diff] [blame] | 265 | std::string LatencyAggregator::dump(const char* prefix) const { |
Siarhei Vishniakou | ad16bf2 | 2023-09-05 18:24:44 -0700 | [diff] [blame] | 266 | std::scoped_lock lock(mLock); |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 267 | std::string sketchDump = StringPrintf("%s Sketches:\n", prefix); |
| 268 | for (size_t i = 0; i < SketchIndex::SIZE; i++) { |
| 269 | const int64_t numDown = mDownSketches[i]->num_values(); |
| 270 | SafeBytesField downBytesField(*mDownSketches[i]); |
| 271 | const float downBytesKb = downBytesField.getBytesField().arg_length * 1E-3; |
| 272 | const int64_t numMove = mMoveSketches[i]->num_values(); |
| 273 | SafeBytesField moveBytesField(*mMoveSketches[i]); |
| 274 | const float moveBytesKb = moveBytesField.getBytesField().arg_length * 1E-3; |
| 275 | sketchDump += |
| 276 | StringPrintf("%s mDownSketches[%zu]->num_values = %" PRId64 " size = %.1fKB" |
| 277 | " mMoveSketches[%zu]->num_values = %" PRId64 " size = %.1fKB\n", |
| 278 | prefix, i, numDown, downBytesKb, i, numMove, moveBytesKb); |
| 279 | } |
| 280 | |
| 281 | return StringPrintf("%sLatencyAggregator:\n", prefix) + sketchDump + |
| 282 | StringPrintf("%s mNumSketchEventsProcessed=%zu\n", prefix, mNumSketchEventsProcessed) + |
| 283 | StringPrintf("%s mLastSlowEventTime=%" PRId64 "\n", prefix, mLastSlowEventTime) + |
| 284 | StringPrintf("%s mNumEventsSinceLastSlowEventReport = %zu\n", prefix, |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 285 | mNumEventsSinceLastSlowEventReport) + |
Siarhei Vishniakou | 99b9d38 | 2021-04-01 08:03:41 +0000 | [diff] [blame] | 286 | StringPrintf("%s mNumSkippedSlowEvents = %zu\n", prefix, mNumSkippedSlowEvents); |
Siarhei Vishniakou | a04181f | 2021-03-26 05:56:49 +0000 | [diff] [blame] | 287 | } |
| 288 | |
| 289 | } // namespace android::inputdispatcher |