blob: ece59051dae7334ef6b728b6abe0b02f41b212c9 [file] [log] [blame]
John Reckdf1742e2017-01-19 15:56:21 -08001/*
2 * Copyright (C) 2017 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 "GraphicsStatsService.h"
18
Tej Singh78f65b62021-03-18 16:19:55 -070019#include <android/util/ProtoOutputStream.h>
Dan Albert110e0072017-10-11 12:41:26 -070020#include <errno.h>
John Reckdf1742e2017-01-19 15:56:21 -080021#include <fcntl.h>
Stan Iliev637ba5e2019-08-16 13:43:08 -040022#include <google/protobuf/io/zero_copy_stream_impl_lite.h>
Dan Albert110e0072017-10-11 12:41:26 -070023#include <inttypes.h>
Stan Iliev637ba5e2019-08-16 13:43:08 -040024#include <log/log.h>
Tej Singh78f65b62021-03-18 16:19:55 -070025#include <stats_event.h>
26#include <statslog_hwui.h>
John Reck1bcacfd2017-11-03 10:12:19 -070027#include <sys/mman.h>
Dan Albert110e0072017-10-11 12:41:26 -070028#include <sys/stat.h>
29#include <sys/types.h>
John Reckdf1742e2017-01-19 15:56:21 -080030#include <unistd.h>
31
Stan Iliev637ba5e2019-08-16 13:43:08 -040032#include "JankTracker.h"
33#include "protos/graphicsstats.pb.h"
34
John Reckdf1742e2017-01-19 15:56:21 -080035namespace android {
36namespace uirenderer {
37
38using namespace google::protobuf;
Stan Iliev637ba5e2019-08-16 13:43:08 -040039using namespace uirenderer::protos;
John Reckdf1742e2017-01-19 15:56:21 -080040
41constexpr int32_t sCurrentFileVersion = 1;
42constexpr int32_t sHeaderSize = 4;
43static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong");
44
John Reck7075c792017-07-05 14:03:43 -070045constexpr int sHistogramSize = ProfileData::HistogramSize();
Stan Iliev7203e1f2019-07-25 13:12:02 -040046constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize();
John Reckdf1742e2017-01-19 15:56:21 -080047
Stan Iliev637ba5e2019-08-16 13:43:08 -040048static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package,
49 int64_t versionCode, int64_t startTime, int64_t endTime,
50 const ProfileData* data);
Kweku Adams1856a4c2018-04-03 16:31:10 -070051static void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int outFd);
John Reckdf1742e2017-01-19 15:56:21 -080052
John Reck915883b2017-05-03 10:27:20 -070053class FileDescriptor {
54public:
Chih-Hung Hsiehf21b0b62018-12-20 13:48:02 -080055 explicit FileDescriptor(int fd) : mFd(fd) {}
John Reck915883b2017-05-03 10:27:20 -070056 ~FileDescriptor() {
57 if (mFd != -1) {
58 close(mFd);
59 mFd = -1;
60 }
61 }
62 bool valid() { return mFd != -1; }
Stan Ilievc9043812020-02-03 16:57:09 -050063 operator int() { return mFd; } // NOLINT(google-explicit-constructor)
John Reck1bcacfd2017-11-03 10:12:19 -070064
John Reck915883b2017-05-03 10:27:20 -070065private:
66 int mFd;
67};
68
69class FileOutputStreamLite : public io::ZeroCopyOutputStream {
70public:
Chih-Hung Hsiehf21b0b62018-12-20 13:48:02 -080071 explicit FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
John Reck915883b2017-05-03 10:27:20 -070072 virtual ~FileOutputStreamLite() {}
73
74 int GetErrno() { return mCopyAdapter.mErrno; }
75
John Reck1bcacfd2017-11-03 10:12:19 -070076 virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); }
John Reck915883b2017-05-03 10:27:20 -070077
John Reck1bcacfd2017-11-03 10:12:19 -070078 virtual void BackUp(int count) override { mImpl.BackUp(count); }
John Reck915883b2017-05-03 10:27:20 -070079
John Reck1bcacfd2017-11-03 10:12:19 -070080 virtual int64 ByteCount() const override { return mImpl.ByteCount(); }
John Reck915883b2017-05-03 10:27:20 -070081
John Reck1bcacfd2017-11-03 10:12:19 -070082 bool Flush() { return mImpl.Flush(); }
John Reck915883b2017-05-03 10:27:20 -070083
84private:
85 struct FDAdapter : public io::CopyingOutputStream {
86 int mFd;
87 int mErrno = 0;
88
Chih-Hung Hsiehf21b0b62018-12-20 13:48:02 -080089 explicit FDAdapter(int fd) : mFd(fd) {}
John Reck915883b2017-05-03 10:27:20 -070090 virtual ~FDAdapter() {}
91
92 virtual bool Write(const void* buffer, int size) override {
93 int ret;
94 while (size) {
John Reck1bcacfd2017-11-03 10:12:19 -070095 ret = TEMP_FAILURE_RETRY(write(mFd, buffer, size));
John Reck915883b2017-05-03 10:27:20 -070096 if (ret <= 0) {
97 mErrno = errno;
98 return false;
99 }
100 size -= ret;
101 }
102 return true;
103 }
104 };
105
106 FileOutputStreamLite::FDAdapter mCopyAdapter;
107 io::CopyingOutputStreamAdaptor mImpl;
108};
109
John Reck1bcacfd2017-11-03 10:12:19 -0700110bool GraphicsStatsService::parseFromFile(const std::string& path,
Kweku Adams1856a4c2018-04-03 16:31:10 -0700111 protos::GraphicsStatsProto* output) {
John Reck915883b2017-05-03 10:27:20 -0700112 FileDescriptor fd{open(path.c_str(), O_RDONLY)};
113 if (!fd.valid()) {
John Reckdf1742e2017-01-19 15:56:21 -0800114 int err = errno;
115 // The file not existing is normal for addToDump(), so only log if
116 // we get an unexpected error
117 if (err != ENOENT) {
118 ALOGW("Failed to open '%s', errno=%d (%s)", path.c_str(), err, strerror(err));
119 }
120 return false;
121 }
John Reck915883b2017-05-03 10:27:20 -0700122 struct stat sb;
123 if (fstat(fd, &sb) || sb.st_size < sHeaderSize) {
124 int err = errno;
125 // The file not existing is normal for addToDump(), so only log if
126 // we get an unexpected error
127 if (err != ENOENT) {
128 ALOGW("Failed to fstat '%s', errno=%d (%s) (st_size %d)", path.c_str(), err,
John Reck1bcacfd2017-11-03 10:12:19 -0700129 strerror(err), (int)sb.st_size);
John Reck915883b2017-05-03 10:27:20 -0700130 }
131 return false;
132 }
133 void* addr = mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
zhangkuili24a1bc32018-05-29 10:23:29 +0800134 if (addr == MAP_FAILED) {
John Reck915883b2017-05-03 10:27:20 -0700135 int err = errno;
136 // The file not existing is normal for addToDump(), so only log if
137 // we get an unexpected error
138 if (err != ENOENT) {
139 ALOGW("Failed to mmap '%s', errno=%d (%s)", path.c_str(), err, strerror(err));
140 }
141 return false;
142 }
143 uint32_t file_version = *reinterpret_cast<uint32_t*>(addr);
144 if (file_version != sCurrentFileVersion) {
145 ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version);
liulvping4832438c2018-12-20 20:34:56 +0800146 munmap(addr, sb.st_size);
John Reckdf1742e2017-01-19 15:56:21 -0800147 return false;
148 }
149
John Reck915883b2017-05-03 10:27:20 -0700150 void* data = reinterpret_cast<uint8_t*>(addr) + sHeaderSize;
151 int dataSize = sb.st_size - sHeaderSize;
152 io::ArrayInputStream input{data, dataSize};
John Reckdf1742e2017-01-19 15:56:21 -0800153 bool success = output->ParseFromZeroCopyStream(&input);
John Reck915883b2017-05-03 10:27:20 -0700154 if (!success) {
John Reck1bcacfd2017-11-03 10:12:19 -0700155 ALOGW("Parse failed on '%s' error='%s'", path.c_str(),
156 output->InitializationErrorString().c_str());
John Reckdf1742e2017-01-19 15:56:21 -0800157 }
liulvping4832438c2018-12-20 20:34:56 +0800158 munmap(addr, sb.st_size);
John Reckdf1742e2017-01-19 15:56:21 -0800159 return success;
160}
161
Kweku Adams1856a4c2018-04-03 16:31:10 -0700162bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, const std::string& package,
Dianne Hackborn73453e42017-12-11 16:30:36 -0800163 int64_t versionCode, int64_t startTime, int64_t endTime,
John Reck1bcacfd2017-11-03 10:12:19 -0700164 const ProfileData* data) {
John Reckdf1742e2017-01-19 15:56:21 -0800165 if (proto->stats_start() == 0 || proto->stats_start() > startTime) {
166 proto->set_stats_start(startTime);
167 }
168 if (proto->stats_end() == 0 || proto->stats_end() < endTime) {
169 proto->set_stats_end(endTime);
170 }
171 proto->set_package_name(package);
172 proto->set_version_code(versionCode);
Stan Iliev637ba5e2019-08-16 13:43:08 -0400173 proto->set_pipeline(data->pipelineType() == RenderPipelineType::SkiaGL ?
174 GraphicsStatsProto_PipelineType_GL : GraphicsStatsProto_PipelineType_VULKAN);
John Reckdf1742e2017-01-19 15:56:21 -0800175 auto summary = proto->mutable_summary();
John Reck7075c792017-07-05 14:03:43 -0700176 summary->set_total_frames(summary->total_frames() + data->totalFrameCount());
177 summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount());
John Reck1bcacfd2017-11-03 10:12:19 -0700178 summary->set_missed_vsync_count(summary->missed_vsync_count() +
179 data->jankTypeCount(kMissedVsync));
180 summary->set_high_input_latency_count(summary->high_input_latency_count() +
181 data->jankTypeCount(kHighInputLatency));
182 summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() +
183 data->jankTypeCount(kSlowUI));
184 summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
185 data->jankTypeCount(kSlowSync));
186 summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT));
Stan Iliev637ba5e2019-08-16 13:43:08 -0400187 summary->set_missed_deadline_count(summary->missed_deadline_count() +
188 data->jankTypeCount(kMissedDeadline));
John Reckdf1742e2017-01-19 15:56:21 -0800189
190 bool creatingHistogram = false;
191 if (proto->histogram_size() == 0) {
192 proto->mutable_histogram()->Reserve(sHistogramSize);
193 creatingHistogram = true;
194 } else if (proto->histogram_size() != sHistogramSize) {
John Reck1bcacfd2017-11-03 10:12:19 -0700195 ALOGE("Histogram size mismatch, proto is %d expected %d", proto->histogram_size(),
196 sHistogramSize);
John Reck5206a872017-09-18 11:08:31 -0700197 return false;
John Reckdf1742e2017-01-19 15:56:21 -0800198 }
John Reck7075c792017-07-05 14:03:43 -0700199 int index = 0;
John Reck5206a872017-09-18 11:08:31 -0700200 bool hitMergeError = false;
John Reck7075c792017-07-05 14:03:43 -0700201 data->histogramForEach([&](ProfileData::HistogramEntry entry) {
John Reck5206a872017-09-18 11:08:31 -0700202 if (hitMergeError) return;
203
Kweku Adams1856a4c2018-04-03 16:31:10 -0700204 protos::GraphicsStatsHistogramBucketProto* bucket;
John Reckdf1742e2017-01-19 15:56:21 -0800205 if (creatingHistogram) {
206 bucket = proto->add_histogram();
John Reck7075c792017-07-05 14:03:43 -0700207 bucket->set_render_millis(entry.renderTimeMs);
John Reckdf1742e2017-01-19 15:56:21 -0800208 } else {
John Reck7075c792017-07-05 14:03:43 -0700209 bucket = proto->mutable_histogram(index);
John Reck5206a872017-09-18 11:08:31 -0700210 if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) {
John Reck1bcacfd2017-11-03 10:12:19 -0700211 ALOGW("Frame time mistmatch %d vs. %u", bucket->render_millis(),
212 entry.renderTimeMs);
John Reck5206a872017-09-18 11:08:31 -0700213 hitMergeError = true;
214 return;
215 }
John Reckdf1742e2017-01-19 15:56:21 -0800216 }
John Reck7075c792017-07-05 14:03:43 -0700217 bucket->set_frame_count(bucket->frame_count() + entry.frameCount);
218 index++;
219 });
Stan Iliev7203e1f2019-07-25 13:12:02 -0400220 if (hitMergeError) return false;
221 // fill in GPU frame time histogram
222 creatingHistogram = false;
223 if (proto->gpu_histogram_size() == 0) {
224 proto->mutable_gpu_histogram()->Reserve(sGPUHistogramSize);
225 creatingHistogram = true;
226 } else if (proto->gpu_histogram_size() != sGPUHistogramSize) {
227 ALOGE("GPU histogram size mismatch, proto is %d expected %d", proto->gpu_histogram_size(),
228 sGPUHistogramSize);
229 return false;
230 }
231 index = 0;
232 data->histogramGPUForEach([&](ProfileData::HistogramEntry entry) {
233 if (hitMergeError) return;
234
235 protos::GraphicsStatsHistogramBucketProto* bucket;
236 if (creatingHistogram) {
237 bucket = proto->add_gpu_histogram();
238 bucket->set_render_millis(entry.renderTimeMs);
239 } else {
240 bucket = proto->mutable_gpu_histogram(index);
241 if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) {
242 ALOGW("GPU frame time mistmatch %d vs. %u", bucket->render_millis(),
243 entry.renderTimeMs);
244 hitMergeError = true;
245 return;
246 }
247 }
248 bucket->set_frame_count(bucket->frame_count() + entry.frameCount);
249 index++;
250 });
John Reck5206a872017-09-18 11:08:31 -0700251 return !hitMergeError;
John Reckdf1742e2017-01-19 15:56:21 -0800252}
253
Kweku Adams1856a4c2018-04-03 16:31:10 -0700254static int32_t findPercentile(protos::GraphicsStatsProto* proto, int percentile) {
John Reckdf1742e2017-01-19 15:56:21 -0800255 int32_t pos = percentile * proto->summary().total_frames() / 100;
256 int32_t remaining = proto->summary().total_frames() - pos;
257 for (auto it = proto->histogram().rbegin(); it != proto->histogram().rend(); ++it) {
258 remaining -= it->frame_count();
259 if (remaining <= 0) {
260 return it->render_millis();
261 }
262 }
263 return 0;
264}
265
Stan Iliev7203e1f2019-07-25 13:12:02 -0400266static int32_t findGPUPercentile(protos::GraphicsStatsProto* proto, int percentile) {
267 uint32_t totalGPUFrameCount = 0; // this is usually proto->summary().total_frames() - 3.
268 for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) {
269 totalGPUFrameCount += it->frame_count();
270 }
271 int32_t pos = percentile * totalGPUFrameCount / 100;
272 int32_t remaining = totalGPUFrameCount - pos;
273 for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) {
274 remaining -= it->frame_count();
275 if (remaining <= 0) {
276 return it->render_millis();
277 }
278 }
279 return 0;
280}
281
Kweku Adams1856a4c2018-04-03 16:31:10 -0700282void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) {
John Reckdf1742e2017-01-19 15:56:21 -0800283 // This isn't a full validation, just enough that we can deref at will
John Reck5206a872017-09-18 11:08:31 -0700284 if (proto->package_name().empty() || !proto->has_summary()) {
285 ALOGW("Skipping dump, invalid package_name() '%s' or summary %d",
John Reck1bcacfd2017-11-03 10:12:19 -0700286 proto->package_name().c_str(), proto->has_summary());
John Reck5206a872017-09-18 11:08:31 -0700287 return;
288 }
John Reckdf1742e2017-01-19 15:56:21 -0800289 dprintf(fd, "\nPackage: %s", proto->package_name().c_str());
Colin Crossd013a882018-10-26 13:04:41 -0700290 dprintf(fd, "\nVersion: %" PRId64, proto->version_code());
291 dprintf(fd, "\nStats since: %" PRId64 "ns", proto->stats_start());
292 dprintf(fd, "\nStats end: %" PRId64 "ns", proto->stats_end());
John Reckdf1742e2017-01-19 15:56:21 -0800293 auto summary = proto->summary();
294 dprintf(fd, "\nTotal frames rendered: %d", summary.total_frames());
295 dprintf(fd, "\nJanky frames: %d (%.2f%%)", summary.janky_frames(),
John Reck1bcacfd2017-11-03 10:12:19 -0700296 (float)summary.janky_frames() / (float)summary.total_frames() * 100.0f);
John Reckdf1742e2017-01-19 15:56:21 -0800297 dprintf(fd, "\n50th percentile: %dms", findPercentile(proto, 50));
298 dprintf(fd, "\n90th percentile: %dms", findPercentile(proto, 90));
299 dprintf(fd, "\n95th percentile: %dms", findPercentile(proto, 95));
300 dprintf(fd, "\n99th percentile: %dms", findPercentile(proto, 99));
301 dprintf(fd, "\nNumber Missed Vsync: %d", summary.missed_vsync_count());
302 dprintf(fd, "\nNumber High input latency: %d", summary.high_input_latency_count());
303 dprintf(fd, "\nNumber Slow UI thread: %d", summary.slow_ui_thread_count());
304 dprintf(fd, "\nNumber Slow bitmap uploads: %d", summary.slow_bitmap_upload_count());
305 dprintf(fd, "\nNumber Slow issue draw commands: %d", summary.slow_draw_count());
John Reck0e486472018-03-19 14:06:16 -0700306 dprintf(fd, "\nNumber Frame deadline missed: %d", summary.missed_deadline_count());
John Reckdf1742e2017-01-19 15:56:21 -0800307 dprintf(fd, "\nHISTOGRAM:");
308 for (const auto& it : proto->histogram()) {
309 dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
310 }
Stan Iliev7203e1f2019-07-25 13:12:02 -0400311 dprintf(fd, "\n50th gpu percentile: %dms", findGPUPercentile(proto, 50));
312 dprintf(fd, "\n90th gpu percentile: %dms", findGPUPercentile(proto, 90));
313 dprintf(fd, "\n95th gpu percentile: %dms", findGPUPercentile(proto, 95));
314 dprintf(fd, "\n99th gpu percentile: %dms", findGPUPercentile(proto, 99));
315 dprintf(fd, "\nGPU HISTOGRAM:");
316 for (const auto& it : proto->gpu_histogram()) {
317 dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
318 }
John Reckdf1742e2017-01-19 15:56:21 -0800319 dprintf(fd, "\n");
320}
321
322void GraphicsStatsService::saveBuffer(const std::string& path, const std::string& package,
Dianne Hackborn73453e42017-12-11 16:30:36 -0800323 int64_t versionCode, int64_t startTime, int64_t endTime,
John Reck1bcacfd2017-11-03 10:12:19 -0700324 const ProfileData* data) {
Kweku Adams1856a4c2018-04-03 16:31:10 -0700325 protos::GraphicsStatsProto statsProto;
John Reckdf1742e2017-01-19 15:56:21 -0800326 if (!parseFromFile(path, &statsProto)) {
327 statsProto.Clear();
328 }
John Reck5206a872017-09-18 11:08:31 -0700329 if (!mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data)) {
330 return;
331 }
John Reckdf1742e2017-01-19 15:56:21 -0800332 // Although we might not have read any data from the file, merging the existing data
333 // should always fully-initialize the proto
John Reck5206a872017-09-18 11:08:31 -0700334 if (!statsProto.IsInitialized()) {
335 ALOGE("proto initialization error %s", statsProto.InitializationErrorString().c_str());
336 return;
337 }
338 if (statsProto.package_name().empty() || !statsProto.has_summary()) {
John Reck1bcacfd2017-11-03 10:12:19 -0700339 ALOGE("missing package_name() '%s' summary %d", statsProto.package_name().c_str(),
340 statsProto.has_summary());
John Reck5206a872017-09-18 11:08:31 -0700341 return;
342 }
John Reckdf1742e2017-01-19 15:56:21 -0800343 int outFd = open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0660);
344 if (outFd <= 0) {
345 int err = errno;
346 ALOGW("Failed to open '%s', error=%d (%s)", path.c_str(), err, strerror(err));
347 return;
348 }
349 int wrote = write(outFd, &sCurrentFileVersion, sHeaderSize);
350 if (wrote != sHeaderSize) {
351 int err = errno;
John Reck1bcacfd2017-11-03 10:12:19 -0700352 ALOGW("Failed to write header to '%s', returned=%d errno=%d (%s)", path.c_str(), wrote, err,
353 strerror(err));
John Reckdf1742e2017-01-19 15:56:21 -0800354 close(outFd);
355 return;
356 }
357 {
John Reck915883b2017-05-03 10:27:20 -0700358 FileOutputStreamLite output(outFd);
John Reckdf1742e2017-01-19 15:56:21 -0800359 bool success = statsProto.SerializeToZeroCopyStream(&output) && output.Flush();
360 if (output.GetErrno() != 0) {
John Reck1bcacfd2017-11-03 10:12:19 -0700361 ALOGW("Error writing to fd=%d, path='%s' err=%d (%s)", outFd, path.c_str(),
362 output.GetErrno(), strerror(output.GetErrno()));
John Reckdf1742e2017-01-19 15:56:21 -0800363 success = false;
364 } else if (!success) {
365 ALOGW("Serialize failed on '%s' unknown error", path.c_str());
366 }
367 }
368 close(outFd);
369}
370
371class GraphicsStatsService::Dump {
372public:
Stan Iliev637ba5e2019-08-16 13:43:08 -0400373 Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {
374 if (mFd == -1 && mType == DumpType::Protobuf) {
375 mType = DumpType::ProtobufStatsd;
376 }
377 }
John Reckdf1742e2017-01-19 15:56:21 -0800378 int fd() { return mFd; }
379 DumpType type() { return mType; }
Kweku Adams1856a4c2018-04-03 16:31:10 -0700380 protos::GraphicsStatsServiceDumpProto& proto() { return mProto; }
Stan Iliev637ba5e2019-08-16 13:43:08 -0400381 void mergeStat(const protos::GraphicsStatsProto& stat);
382 void updateProto();
John Reck1bcacfd2017-11-03 10:12:19 -0700383
John Reckdf1742e2017-01-19 15:56:21 -0800384private:
Stan Iliev637ba5e2019-08-16 13:43:08 -0400385 // use package name and app version for a key
386 typedef std::pair<std::string, int64_t> DumpKey;
387
388 std::map<DumpKey, protos::GraphicsStatsProto> mStats;
John Reckdf1742e2017-01-19 15:56:21 -0800389 int mFd;
390 DumpType mType;
Kweku Adams1856a4c2018-04-03 16:31:10 -0700391 protos::GraphicsStatsServiceDumpProto mProto;
John Reckdf1742e2017-01-19 15:56:21 -0800392};
393
Stan Iliev637ba5e2019-08-16 13:43:08 -0400394void GraphicsStatsService::Dump::mergeStat(const protos::GraphicsStatsProto& stat) {
395 auto dumpKey = std::make_pair(stat.package_name(), stat.version_code());
396 auto findIt = mStats.find(dumpKey);
397 if (findIt == mStats.end()) {
398 mStats[dumpKey] = stat;
399 } else {
400 auto summary = findIt->second.mutable_summary();
401 summary->set_total_frames(summary->total_frames() + stat.summary().total_frames());
402 summary->set_janky_frames(summary->janky_frames() + stat.summary().janky_frames());
403 summary->set_missed_vsync_count(summary->missed_vsync_count() +
404 stat.summary().missed_vsync_count());
405 summary->set_high_input_latency_count(summary->high_input_latency_count() +
406 stat.summary().high_input_latency_count());
407 summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() +
408 stat.summary().slow_ui_thread_count());
409 summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
410 stat.summary().slow_bitmap_upload_count());
411 summary->set_slow_draw_count(summary->slow_draw_count() + stat.summary().slow_draw_count());
412 summary->set_missed_deadline_count(summary->missed_deadline_count() +
413 stat.summary().missed_deadline_count());
414 for (int bucketIndex = 0; bucketIndex < findIt->second.histogram_size(); bucketIndex++) {
415 auto bucket = findIt->second.mutable_histogram(bucketIndex);
416 bucket->set_frame_count(bucket->frame_count() +
417 stat.histogram(bucketIndex).frame_count());
418 }
419 for (int bucketIndex = 0; bucketIndex < findIt->second.gpu_histogram_size();
420 bucketIndex++) {
421 auto bucket = findIt->second.mutable_gpu_histogram(bucketIndex);
422 bucket->set_frame_count(bucket->frame_count() +
423 stat.gpu_histogram(bucketIndex).frame_count());
424 }
425 findIt->second.set_stats_start(std::min(findIt->second.stats_start(), stat.stats_start()));
426 findIt->second.set_stats_end(std::max(findIt->second.stats_end(), stat.stats_end()));
427 }
428}
429
430void GraphicsStatsService::Dump::updateProto() {
431 for (auto& stat : mStats) {
432 mProto.add_stats()->CopyFrom(stat.second);
433 }
434}
435
John Reckdf1742e2017-01-19 15:56:21 -0800436GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType type) {
437 return new Dump(outFd, type);
438}
439
John Reck1bcacfd2017-11-03 10:12:19 -0700440void GraphicsStatsService::addToDump(Dump* dump, const std::string& path,
Dianne Hackborn73453e42017-12-11 16:30:36 -0800441 const std::string& package, int64_t versionCode,
442 int64_t startTime, int64_t endTime, const ProfileData* data) {
Kweku Adams1856a4c2018-04-03 16:31:10 -0700443 protos::GraphicsStatsProto statsProto;
John Reckdf1742e2017-01-19 15:56:21 -0800444 if (!path.empty() && !parseFromFile(path, &statsProto)) {
445 statsProto.Clear();
446 }
John Reck1bcacfd2017-11-03 10:12:19 -0700447 if (data &&
448 !mergeProfileDataIntoProto(&statsProto, package, versionCode, startTime, endTime, data)) {
John Reck5206a872017-09-18 11:08:31 -0700449 return;
John Reckdf1742e2017-01-19 15:56:21 -0800450 }
451 if (!statsProto.IsInitialized()) {
452 ALOGW("Failed to load profile data from path '%s' and data %p",
John Reck1bcacfd2017-11-03 10:12:19 -0700453 path.empty() ? "<empty>" : path.c_str(), data);
John Reckdf1742e2017-01-19 15:56:21 -0800454 return;
455 }
Stan Iliev637ba5e2019-08-16 13:43:08 -0400456 if (dump->type() == DumpType::ProtobufStatsd) {
457 dump->mergeStat(statsProto);
458 } else if (dump->type() == DumpType::Protobuf) {
John Reckdf1742e2017-01-19 15:56:21 -0800459 dump->proto().add_stats()->CopyFrom(statsProto);
460 } else {
461 dumpAsTextToFd(&statsProto, dump->fd());
462 }
463}
464
465void GraphicsStatsService::addToDump(Dump* dump, const std::string& path) {
Kweku Adams1856a4c2018-04-03 16:31:10 -0700466 protos::GraphicsStatsProto statsProto;
John Reckdf1742e2017-01-19 15:56:21 -0800467 if (!parseFromFile(path, &statsProto)) {
468 return;
469 }
Stan Iliev637ba5e2019-08-16 13:43:08 -0400470 if (dump->type() == DumpType::ProtobufStatsd) {
471 dump->mergeStat(statsProto);
472 } else if (dump->type() == DumpType::Protobuf) {
John Reckdf1742e2017-01-19 15:56:21 -0800473 dump->proto().add_stats()->CopyFrom(statsProto);
474 } else {
475 dumpAsTextToFd(&statsProto, dump->fd());
476 }
477}
478
479void GraphicsStatsService::finishDump(Dump* dump) {
480 if (dump->type() == DumpType::Protobuf) {
John Reck915883b2017-05-03 10:27:20 -0700481 FileOutputStreamLite stream(dump->fd());
John Reckdf1742e2017-01-19 15:56:21 -0800482 dump->proto().SerializeToZeroCopyStream(&stream);
483 }
484 delete dump;
485}
486
Stan Ilievc9043812020-02-03 16:57:09 -0500487using namespace google::protobuf;
Stan Iliev637ba5e2019-08-16 13:43:08 -0400488
Stan Ilievc9043812020-02-03 16:57:09 -0500489// Field ids taken from FrameTimingHistogram message in atoms.proto
490#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
491#define FRAME_COUNTS_FIELD_NUMBER 2
Stan Iliev637ba5e2019-08-16 13:43:08 -0400492
Stan Ilievc9043812020-02-03 16:57:09 -0500493static void writeCpuHistogram(AStatsEvent* event,
494 const uirenderer::protos::GraphicsStatsProto& stat) {
495 util::ProtoOutputStream proto;
496 for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
497 auto& bucket = stat.histogram(bucketIndex);
498 proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
499 TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
500 (int)bucket.render_millis());
Stan Iliev637ba5e2019-08-16 13:43:08 -0400501 }
Stan Ilievc9043812020-02-03 16:57:09 -0500502 for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
503 auto& bucket = stat.histogram(bucketIndex);
504 proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
505 FRAME_COUNTS_FIELD_NUMBER /* field id */,
506 (long long)bucket.frame_count());
Stan Iliev637ba5e2019-08-16 13:43:08 -0400507 }
Stan Ilievc9043812020-02-03 16:57:09 -0500508 std::vector<uint8_t> outVector;
509 proto.serializeToVector(&outVector);
510 AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
Stan Iliev637ba5e2019-08-16 13:43:08 -0400511}
512
Stan Ilievc9043812020-02-03 16:57:09 -0500513static void writeGpuHistogram(AStatsEvent* event,
514 const uirenderer::protos::GraphicsStatsProto& stat) {
515 util::ProtoOutputStream proto;
516 for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
517 auto& bucket = stat.gpu_histogram(bucketIndex);
518 proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
519 TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
520 (int)bucket.render_millis());
521 }
522 for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
523 auto& bucket = stat.gpu_histogram(bucketIndex);
524 proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
525 FRAME_COUNTS_FIELD_NUMBER /* field id */,
526 (long long)bucket.frame_count());
527 }
528 std::vector<uint8_t> outVector;
529 proto.serializeToVector(&outVector);
530 AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
531}
532
533
534void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data,
535 bool lastFullDay) {
536 dump->updateProto();
537 auto& serviceDump = dump->proto();
538 for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
539 auto& stat = serviceDump.stats(stat_index);
540 AStatsEvent* event = AStatsEventList_addStatsEvent(data);
Tej Singh78f65b62021-03-18 16:19:55 -0700541 AStatsEvent_setAtomId(event, stats::GRAPHICS_STATS);
Stan Ilievc9043812020-02-03 16:57:09 -0500542 AStatsEvent_writeString(event, stat.package_name().c_str());
543 AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
544 AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
545 AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
546 AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
547 AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
548 AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
549 AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
550 AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
551 AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
552 AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
553 AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
554 writeCpuHistogram(event, stat);
555 writeGpuHistogram(event, stat);
556 // TODO: fill in UI mainline module version, when the feature is available.
557 AStatsEvent_writeInt64(event, (int64_t)0);
558 AStatsEvent_writeBool(event, !lastFullDay);
559 AStatsEvent_build(event);
560 }
Florian Mayer6e1b4a42020-08-06 12:55:42 +0000561 delete dump;
Stan Ilievc9043812020-02-03 16:57:09 -0500562}
563
564
John Reckdf1742e2017-01-19 15:56:21 -0800565} /* namespace uirenderer */
Dan Albert110e0072017-10-11 12:41:26 -0700566} /* namespace android */