blob: 5f2dd321820ad7ba9658f26dbd41636ec9441a77 [file] [log] [blame]
Yiwei Zhang0102ad22018-05-02 17:37:17 -07001/*
2 * Copyright 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#undef LOG_TAG
17#define LOG_TAG "TimeStats"
18#define ATRACE_TAG ATRACE_TAG_GRAPHICS
19
20#include "TimeStats.h"
21
22#include <android-base/stringprintf.h>
23
24#include <log/log.h>
25
26#include <utils/String8.h>
27#include <utils/Trace.h>
28
29#include <algorithm>
30#include <regex>
31
32namespace android {
33
34TimeStats& TimeStats::getInstance() {
35 static std::unique_ptr<TimeStats> sInstance;
36 static std::once_flag sOnceFlag;
37
38 std::call_once(sOnceFlag, [] { sInstance.reset(new TimeStats); });
39 return *sInstance.get();
40}
41
42void TimeStats::parseArgs(bool asProto, const Vector<String16>& args, size_t& index,
43 String8& result) {
44 ATRACE_CALL();
45
46 if (args.size() > index + 10) {
47 ALOGD("Invalid args count");
48 return;
49 }
50
51 std::unordered_map<std::string, int32_t> argsMap;
52 while (index < args.size()) {
53 argsMap[std::string(String8(args[index]).c_str())] = index;
54 ++index;
55 }
56
57 if (argsMap.count("-disable")) {
58 disable();
59 }
60
61 if (argsMap.count("-dump")) {
62 int64_t maxLayers = 0;
63 auto iter = argsMap.find("-maxlayers");
64 if (iter != argsMap.end() && iter->second + 1 < static_cast<int32_t>(args.size())) {
65 maxLayers = strtol(String8(args[iter->second + 1]).c_str(), nullptr, 10);
66 maxLayers = std::clamp(maxLayers, int64_t(0), int64_t(UINT32_MAX));
67 }
68
69 dump(asProto, static_cast<uint32_t>(maxLayers), result);
70 }
71
72 if (argsMap.count("-clear")) {
73 clear();
74 }
75
76 if (argsMap.count("-enable")) {
77 enable();
78 }
79}
80
81void TimeStats::incrementTotalFrames() {
82 if (!mEnabled.load()) return;
83
84 ATRACE_CALL();
85
86 std::lock_guard<std::mutex> lock(mMutex);
87 timeStats.totalFrames++;
88}
89
90void TimeStats::incrementMissedFrames(bool propagateBackpressure) {
91 if (!mEnabled.load()) return;
92
93 ATRACE_CALL();
94
95 std::lock_guard<std::mutex> lock(mMutex);
96 if (propagateBackpressure) {
97 timeStats.totalFrames--;
98 }
99 timeStats.missedFrames++;
100}
101
102void TimeStats::incrementClientCompositionFrames() {
103 if (!mEnabled.load()) return;
104
105 ATRACE_CALL();
106
107 std::lock_guard<std::mutex> lock(mMutex);
108 timeStats.clientCompositionFrames++;
109}
110
111bool TimeStats::recordReadyLocked(const std::string& layerName, TimeRecord* timeRecord) {
112 if (!timeRecord->ready) {
113 ALOGV("[%s]-[%" PRIu64 "]-presentFence is still not received", layerName.c_str(),
114 timeRecord->frameNumber);
115 return false;
116 }
117
118 if (timeRecord->acquireFence != nullptr) {
119 if (timeRecord->acquireFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
120 return false;
121 }
122 if (timeRecord->acquireFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) {
123 timeRecord->acquireTime = timeRecord->acquireFence->getSignalTime();
124 timeRecord->acquireFence = nullptr;
125 } else {
126 ALOGV("[%s]-[%" PRIu64 "]-acquireFence signal time is invalid", layerName.c_str(),
127 timeRecord->frameNumber);
128 }
129 }
130
131 if (timeRecord->presentFence != nullptr) {
132 if (timeRecord->presentFence->getSignalTime() == Fence::SIGNAL_TIME_PENDING) {
133 return false;
134 }
135 if (timeRecord->presentFence->getSignalTime() != Fence::SIGNAL_TIME_INVALID) {
136 timeRecord->presentTime = timeRecord->presentFence->getSignalTime();
137 timeRecord->presentFence = nullptr;
138 } else {
139 ALOGV("[%s]-[%" PRIu64 "]-presentFence signal time invalid", layerName.c_str(),
140 timeRecord->frameNumber);
141 }
142 }
143
144 return true;
145}
146
147static int32_t msBetween(nsecs_t start, nsecs_t end) {
148 int64_t delta = (end - start) / 1000000;
149 delta = std::clamp(delta, int64_t(INT32_MIN), int64_t(INT32_MAX));
150 return static_cast<int32_t>(delta);
151}
152
153void TimeStats::flushAvailableRecordsToStatsLocked(const std::string& layerName) {
154 ATRACE_CALL();
155
156 LayerRecord& layerRecord = timeStatsTracker[layerName];
157 TimeRecord& prevTimeRecord = layerRecord.prevTimeRecord;
158 std::vector<TimeRecord>& timeRecords = layerRecord.timeRecords;
159 while (!timeRecords.empty()) {
160 if (!recordReadyLocked(layerName, &timeRecords[0])) break;
161 ALOGV("[%s]-[%" PRIu64 "]-presentFenceTime[%" PRId64 "]", layerName.c_str(),
162 timeRecords[0].frameNumber, timeRecords[0].presentTime);
163
164 if (prevTimeRecord.ready) {
165 if (!timeStats.stats.count(layerName)) {
166 timeStats.stats[layerName].layerName = layerName;
167 timeStats.stats[layerName].statsStart = static_cast<int64_t>(std::time(0));
168 }
169 TimeStatsHelper::TimeStatsLayer& timeStatsLayer = timeStats.stats[layerName];
170 timeStatsLayer.totalFrames++;
171
172 const int32_t postToPresentMs =
173 msBetween(timeRecords[0].postTime, timeRecords[0].presentTime);
174 ALOGV("[%s]-[%" PRIu64 "]-post2present[%d]", layerName.c_str(),
175 timeRecords[0].frameNumber, postToPresentMs);
176 timeStatsLayer.deltas["post2present"].insert(postToPresentMs);
177
178 const int32_t acquireToPresentMs =
179 msBetween(timeRecords[0].acquireTime, timeRecords[0].presentTime);
180 ALOGV("[%s]-[%" PRIu64 "]-acquire2present[%d]", layerName.c_str(),
181 timeRecords[0].frameNumber, acquireToPresentMs);
182 timeStatsLayer.deltas["acquire2present"].insert(acquireToPresentMs);
183
184 const int32_t latchToPresentMs =
185 msBetween(timeRecords[0].latchTime, timeRecords[0].presentTime);
186 ALOGV("[%s]-[%" PRIu64 "]-latch2present[%d]", layerName.c_str(),
187 timeRecords[0].frameNumber, latchToPresentMs);
188 timeStatsLayer.deltas["latch2present"].insert(latchToPresentMs);
189
190 const int32_t desiredToPresentMs =
191 msBetween(timeRecords[0].desiredTime, timeRecords[0].presentTime);
192 ALOGV("[%s]-[%" PRIu64 "]-desired2present[%d]", layerName.c_str(),
193 timeRecords[0].frameNumber, desiredToPresentMs);
194 timeStatsLayer.deltas["desired2present"].insert(desiredToPresentMs);
195
196 const int32_t presentToPresentMs =
197 msBetween(prevTimeRecord.presentTime, timeRecords[0].presentTime);
198 ALOGV("[%s]-[%" PRIu64 "]-present2present[%d]", layerName.c_str(),
199 timeRecords[0].frameNumber, presentToPresentMs);
200 timeStatsLayer.deltas["present2present"].insert(presentToPresentMs);
201
202 timeStats.stats[layerName].statsEnd = static_cast<int64_t>(std::time(0));
203 }
204 prevTimeRecord = timeRecords[0];
205 // TODO(zzyiwei): change timeRecords to use std::deque
206 timeRecords.erase(timeRecords.begin());
207 layerRecord.waitData--;
208 }
209}
210
211static bool layerNameIsValid(const std::string& layerName) {
212 // This regular expression captures the following layer names for instance:
213 // 1) StatusBat#0
214 // 2) NavigationBar#1
215 // 3) com.*#0
216 // 4) SurfaceView - com.*#0
217 // Using [-\\s\t]+ for the conjunction part between SurfaceView and com.* is
218 // a bit more robust in case there's a slight change.
219 // The layer name would only consist of . / $ _ 0-9 a-z A-Z in most cases.
220 std::regex re("(((SurfaceView[-\\s\\t]+)?com\\.[./$\\w]+)|((Status|Navigation)Bar))#\\d+");
221 return std::regex_match(layerName.begin(), layerName.end(), re);
222}
223
224void TimeStats::setPostTime(const std::string& layerName, uint64_t frameNumber, nsecs_t postTime) {
225 if (!mEnabled.load()) return;
226
227 ATRACE_CALL();
228 ALOGV("[%s]-[%" PRIu64 "]-PostTime[%" PRId64 "]", layerName.c_str(), frameNumber, postTime);
229
230 std::lock_guard<std::mutex> lock(mMutex);
231 if (!timeStatsTracker.count(layerName) && !layerNameIsValid(layerName)) {
232 return;
233 }
234 LayerRecord& layerRecord = timeStatsTracker[layerName];
235 if (layerRecord.timeRecords.size() == MAX_NUM_TIME_RECORDS) {
236 ALOGV("[%s]-timeRecords is already at its maximum size[%zu]", layerName.c_str(),
237 MAX_NUM_TIME_RECORDS);
238 // TODO(zzyiwei): if this happens, there must be a present fence missing
239 // or waitData is not in the correct position. Need to think out a
240 // reasonable way to recover from this state.
241 return;
242 }
243 // For most media content, the acquireFence is invalid because the buffer is
244 // ready at the queueBuffer stage. In this case, acquireTime should be given
245 // a default value as postTime.
246 TimeRecord timeRecord = {
247 .frameNumber = frameNumber,
248 .postTime = postTime,
249 .acquireTime = postTime,
250 };
251 layerRecord.timeRecords.push_back(timeRecord);
252 if (layerRecord.waitData < 0 ||
253 layerRecord.waitData >= static_cast<int32_t>(layerRecord.timeRecords.size()))
254 layerRecord.waitData = layerRecord.timeRecords.size() - 1;
255}
256
257void TimeStats::setLatchTime(const std::string& layerName, uint64_t frameNumber,
258 nsecs_t latchTime) {
259 if (!mEnabled.load()) return;
260
261 ATRACE_CALL();
262 ALOGV("[%s]-[%" PRIu64 "]-LatchTime[%" PRId64 "]", layerName.c_str(), frameNumber, latchTime);
263
264 std::lock_guard<std::mutex> lock(mMutex);
265 if (!timeStatsTracker.count(layerName)) return;
266 LayerRecord& layerRecord = timeStatsTracker[layerName];
267 TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
268 if (timeRecord.frameNumber == frameNumber) {
269 timeRecord.latchTime = latchTime;
270 }
271}
272
273void TimeStats::setDesiredTime(const std::string& layerName, uint64_t frameNumber,
274 nsecs_t desiredTime) {
275 if (!mEnabled.load()) return;
276
277 ATRACE_CALL();
278 ALOGV("[%s]-[%" PRIu64 "]-DesiredTime[%" PRId64 "]", layerName.c_str(), frameNumber,
279 desiredTime);
280
281 std::lock_guard<std::mutex> lock(mMutex);
282 if (!timeStatsTracker.count(layerName)) return;
283 LayerRecord& layerRecord = timeStatsTracker[layerName];
284 TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
285 if (timeRecord.frameNumber == frameNumber) {
286 timeRecord.desiredTime = desiredTime;
287 }
288}
289
290void TimeStats::setAcquireTime(const std::string& layerName, uint64_t frameNumber,
291 nsecs_t acquireTime) {
292 if (!mEnabled.load()) return;
293
294 ATRACE_CALL();
295 ALOGV("[%s]-[%" PRIu64 "]-AcquireTime[%" PRId64 "]", layerName.c_str(), frameNumber,
296 acquireTime);
297
298 std::lock_guard<std::mutex> lock(mMutex);
299 if (!timeStatsTracker.count(layerName)) return;
300 LayerRecord& layerRecord = timeStatsTracker[layerName];
301 TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
302 if (timeRecord.frameNumber == frameNumber) {
303 timeRecord.acquireTime = acquireTime;
304 }
305}
306
307void TimeStats::setAcquireFence(const std::string& layerName, uint64_t frameNumber,
308 const std::shared_ptr<FenceTime>& acquireFence) {
309 if (!mEnabled.load()) return;
310
311 ATRACE_CALL();
312 ALOGV("[%s]-[%" PRIu64 "]-AcquireFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
313 acquireFence->getSignalTime());
314
315 std::lock_guard<std::mutex> lock(mMutex);
316 if (!timeStatsTracker.count(layerName)) return;
317 LayerRecord& layerRecord = timeStatsTracker[layerName];
318 TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
319 if (timeRecord.frameNumber == frameNumber) {
320 timeRecord.acquireFence = acquireFence;
321 }
322}
323
324void TimeStats::setPresentTime(const std::string& layerName, uint64_t frameNumber,
325 nsecs_t presentTime) {
326 if (!mEnabled.load()) return;
327
328 ATRACE_CALL();
329 ALOGV("[%s]-[%" PRIu64 "]-PresentTime[%" PRId64 "]", layerName.c_str(), frameNumber,
330 presentTime);
331
332 std::lock_guard<std::mutex> lock(mMutex);
333 if (!timeStatsTracker.count(layerName)) return;
334 LayerRecord& layerRecord = timeStatsTracker[layerName];
335 TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
336 if (timeRecord.frameNumber == frameNumber) {
337 timeRecord.presentTime = presentTime;
338 timeRecord.ready = true;
339 layerRecord.waitData++;
340 }
341
342 flushAvailableRecordsToStatsLocked(layerName);
343}
344
345void TimeStats::setPresentFence(const std::string& layerName, uint64_t frameNumber,
346 const std::shared_ptr<FenceTime>& presentFence) {
347 if (!mEnabled.load()) return;
348
349 ATRACE_CALL();
350 ALOGV("[%s]-[%" PRIu64 "]-PresentFenceTime[%" PRId64 "]", layerName.c_str(), frameNumber,
351 presentFence->getSignalTime());
352
353 std::lock_guard<std::mutex> lock(mMutex);
354 if (!timeStatsTracker.count(layerName)) return;
355 LayerRecord& layerRecord = timeStatsTracker[layerName];
356 TimeRecord& timeRecord = layerRecord.timeRecords[layerRecord.waitData];
357 if (timeRecord.frameNumber == frameNumber) {
358 timeRecord.presentFence = presentFence;
359 timeRecord.ready = true;
360 layerRecord.waitData++;
361 }
362
363 flushAvailableRecordsToStatsLocked(layerName);
364}
365
366void TimeStats::onDisconnect(const std::string& layerName) {
367 if (!mEnabled.load()) return;
368
369 ATRACE_CALL();
370 ALOGV("[%s]-onDisconnect", layerName.c_str());
371
372 std::lock_guard<std::mutex> lock(mMutex);
373 if (!timeStatsTracker.count(layerName)) return;
374 flushAvailableRecordsToStatsLocked(layerName);
375 timeStatsTracker.erase(layerName);
376}
377
378void TimeStats::clearLayerRecord(const std::string& layerName) {
379 if (!mEnabled.load()) return;
380
381 ATRACE_CALL();
382 ALOGV("[%s]-clearLayerRecord", layerName.c_str());
383
384 std::lock_guard<std::mutex> lock(mMutex);
385 if (!timeStatsTracker.count(layerName)) return;
386 LayerRecord& layerRecord = timeStatsTracker[layerName];
387 layerRecord.timeRecords.clear();
388 layerRecord.prevTimeRecord.ready = false;
389 layerRecord.waitData = -1;
390}
391
392void TimeStats::removeTimeRecord(const std::string& layerName, uint64_t frameNumber) {
393 if (!mEnabled.load()) return;
394
395 ATRACE_CALL();
396 ALOGV("[%s]-[%" PRIu64 "]-removeTimeRecord", layerName.c_str(), frameNumber);
397
398 std::lock_guard<std::mutex> lock(mMutex);
399 if (!timeStatsTracker.count(layerName)) return;
400 LayerRecord& layerRecord = timeStatsTracker[layerName];
401 size_t removeAt = 0;
402 for (const TimeRecord& record : layerRecord.timeRecords) {
403 if (record.frameNumber == frameNumber) break;
404 removeAt++;
405 }
406 if (removeAt == layerRecord.timeRecords.size()) return;
407 layerRecord.timeRecords.erase(layerRecord.timeRecords.begin() + removeAt);
408 if (layerRecord.waitData > static_cast<int32_t>(removeAt)) {
409 --layerRecord.waitData;
410 }
411}
412
413void TimeStats::enable() {
414 if (mEnabled.load()) return;
415
416 ATRACE_CALL();
417
418 std::lock_guard<std::mutex> lock(mMutex);
419 ALOGD("Enabled");
420 mEnabled.store(true);
421 timeStats.statsStart = static_cast<int64_t>(std::time(0));
422}
423
424void TimeStats::disable() {
425 if (!mEnabled.load()) return;
426
427 ATRACE_CALL();
428
429 std::lock_guard<std::mutex> lock(mMutex);
430 ALOGD("Disabled");
431 mEnabled.store(false);
432 timeStats.statsEnd = static_cast<int64_t>(std::time(0));
433}
434
435void TimeStats::clear() {
436 ATRACE_CALL();
437
438 std::lock_guard<std::mutex> lock(mMutex);
439 ALOGD("Cleared");
440 timeStats.dumpStats.clear();
441 timeStats.stats.clear();
442 timeStats.statsStart = (mEnabled.load() ? static_cast<int64_t>(std::time(0)) : 0);
443 timeStats.statsEnd = 0;
444 timeStats.totalFrames = 0;
445 timeStats.missedFrames = 0;
446 timeStats.clientCompositionFrames = 0;
447}
448
449bool TimeStats::isEnabled() {
450 return mEnabled.load();
451}
452
453void TimeStats::dump(bool asProto, uint32_t maxLayers, String8& result) {
454 ATRACE_CALL();
455
456 std::lock_guard<std::mutex> lock(mMutex);
457 if (timeStats.statsStart == 0) {
458 return;
459 }
460
461 timeStats.statsEnd = static_cast<int64_t>(std::time(0));
462
463 // TODO(zzyiwei): refactor dumpStats into TimeStatsHelper
464 timeStats.dumpStats.clear();
465 for (auto& ele : timeStats.stats) {
466 timeStats.dumpStats.push_back(&ele.second);
467 }
468
469 std::sort(timeStats.dumpStats.begin(), timeStats.dumpStats.end(),
470 [](TimeStatsHelper::TimeStatsLayer* const& l,
471 TimeStatsHelper::TimeStatsLayer* const& r) {
472 return l->totalFrames > r->totalFrames;
473 });
474
475 if (maxLayers != 0 && maxLayers < timeStats.dumpStats.size()) {
476 timeStats.dumpStats.resize(maxLayers);
477 }
478
479 if (asProto) {
480 dumpAsProtoLocked(result);
481 } else {
482 dumpAsTextLocked(result);
483 }
484}
485
486void TimeStats::dumpAsTextLocked(String8& result) {
487 ALOGD("Dumping TimeStats as text");
488 result.append(timeStats.toString().c_str());
489 result.append("\n");
490}
491
492void TimeStats::dumpAsProtoLocked(String8& result) {
493 ALOGD("Dumping TimeStats as proto");
494 SFTimeStatsGlobalProto timeStatsProto = timeStats.toProto();
495 result.append(timeStatsProto.SerializeAsString().c_str(), timeStatsProto.ByteSize());
496}
497
498} // namespace android