blob: 702f2a5626f7418546509208a4daa2305cf15f76 [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>
Alec Mouri04b0d812024-09-20 16:15:55 +000025#include <stats_annotations.h>
Tej Singh78f65b62021-03-18 16:19:55 -070026#include <stats_event.h>
27#include <statslog_hwui.h>
John Reck1bcacfd2017-11-03 10:12:19 -070028#include <sys/mman.h>
Dan Albert110e0072017-10-11 12:41:26 -070029#include <sys/stat.h>
30#include <sys/types.h>
John Reckdf1742e2017-01-19 15:56:21 -080031#include <unistd.h>
32
Stan Iliev637ba5e2019-08-16 13:43:08 -040033#include "JankTracker.h"
34#include "protos/graphicsstats.pb.h"
35
John Reckdf1742e2017-01-19 15:56:21 -080036namespace android {
37namespace uirenderer {
38
39using namespace google::protobuf;
Stan Iliev637ba5e2019-08-16 13:43:08 -040040using namespace uirenderer::protos;
John Reckdf1742e2017-01-19 15:56:21 -080041
42constexpr int32_t sCurrentFileVersion = 1;
43constexpr int32_t sHeaderSize = 4;
44static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong");
45
John Reck7075c792017-07-05 14:03:43 -070046constexpr int sHistogramSize = ProfileData::HistogramSize();
Stan Iliev7203e1f2019-07-25 13:12:02 -040047constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize();
John Reckdf1742e2017-01-19 15:56:21 -080048
Alec Mouri04b0d812024-09-20 16:15:55 +000049static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, uid_t uid,
50 const std::string& package, int64_t versionCode,
51 int64_t startTime, int64_t endTime, const ProfileData* data);
Kweku Adams1856a4c2018-04-03 16:31:10 -070052static void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int outFd);
John Reckdf1742e2017-01-19 15:56:21 -080053
John Reck915883b2017-05-03 10:27:20 -070054class FileDescriptor {
55public:
Chih-Hung Hsiehf21b0b62018-12-20 13:48:02 -080056 explicit FileDescriptor(int fd) : mFd(fd) {}
John Reck915883b2017-05-03 10:27:20 -070057 ~FileDescriptor() {
58 if (mFd != -1) {
59 close(mFd);
60 mFd = -1;
61 }
62 }
63 bool valid() { return mFd != -1; }
Stan Ilievc9043812020-02-03 16:57:09 -050064 operator int() { return mFd; } // NOLINT(google-explicit-constructor)
John Reck1bcacfd2017-11-03 10:12:19 -070065
John Reck915883b2017-05-03 10:27:20 -070066private:
67 int mFd;
68};
69
70class FileOutputStreamLite : public io::ZeroCopyOutputStream {
71public:
Chih-Hung Hsiehf21b0b62018-12-20 13:48:02 -080072 explicit FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
John Reck915883b2017-05-03 10:27:20 -070073 virtual ~FileOutputStreamLite() {}
74
75 int GetErrno() { return mCopyAdapter.mErrno; }
76
John Reck1bcacfd2017-11-03 10:12:19 -070077 virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); }
John Reck915883b2017-05-03 10:27:20 -070078
John Reck1bcacfd2017-11-03 10:12:19 -070079 virtual void BackUp(int count) override { mImpl.BackUp(count); }
John Reck915883b2017-05-03 10:27:20 -070080
John Reck1bcacfd2017-11-03 10:12:19 -070081 virtual int64 ByteCount() const override { return mImpl.ByteCount(); }
John Reck915883b2017-05-03 10:27:20 -070082
John Reck1bcacfd2017-11-03 10:12:19 -070083 bool Flush() { return mImpl.Flush(); }
John Reck915883b2017-05-03 10:27:20 -070084
85private:
86 struct FDAdapter : public io::CopyingOutputStream {
87 int mFd;
88 int mErrno = 0;
89
Chih-Hung Hsiehf21b0b62018-12-20 13:48:02 -080090 explicit FDAdapter(int fd) : mFd(fd) {}
John Reck915883b2017-05-03 10:27:20 -070091 virtual ~FDAdapter() {}
92
93 virtual bool Write(const void* buffer, int size) override {
94 int ret;
95 while (size) {
John Reck1bcacfd2017-11-03 10:12:19 -070096 ret = TEMP_FAILURE_RETRY(write(mFd, buffer, size));
John Reck915883b2017-05-03 10:27:20 -070097 if (ret <= 0) {
98 mErrno = errno;
99 return false;
100 }
101 size -= ret;
102 }
103 return true;
104 }
105 };
106
107 FileOutputStreamLite::FDAdapter mCopyAdapter;
108 io::CopyingOutputStreamAdaptor mImpl;
109};
110
John Reck1bcacfd2017-11-03 10:12:19 -0700111bool GraphicsStatsService::parseFromFile(const std::string& path,
Kweku Adams1856a4c2018-04-03 16:31:10 -0700112 protos::GraphicsStatsProto* output) {
John Reck915883b2017-05-03 10:27:20 -0700113 FileDescriptor fd{open(path.c_str(), O_RDONLY)};
114 if (!fd.valid()) {
John Reckdf1742e2017-01-19 15:56:21 -0800115 int err = errno;
116 // The file not existing is normal for addToDump(), so only log if
117 // we get an unexpected error
118 if (err != ENOENT) {
119 ALOGW("Failed to open '%s', errno=%d (%s)", path.c_str(), err, strerror(err));
120 }
121 return false;
122 }
John Reck915883b2017-05-03 10:27:20 -0700123 struct stat sb;
124 if (fstat(fd, &sb) || sb.st_size < sHeaderSize) {
125 int err = errno;
126 // The file not existing is normal for addToDump(), so only log if
127 // we get an unexpected error
128 if (err != ENOENT) {
129 ALOGW("Failed to fstat '%s', errno=%d (%s) (st_size %d)", path.c_str(), err,
John Reck1bcacfd2017-11-03 10:12:19 -0700130 strerror(err), (int)sb.st_size);
John Reck915883b2017-05-03 10:27:20 -0700131 }
132 return false;
133 }
134 void* addr = mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
zhangkuili24a1bc32018-05-29 10:23:29 +0800135 if (addr == MAP_FAILED) {
John Reck915883b2017-05-03 10:27:20 -0700136 int err = errno;
137 // The file not existing is normal for addToDump(), so only log if
138 // we get an unexpected error
139 if (err != ENOENT) {
140 ALOGW("Failed to mmap '%s', errno=%d (%s)", path.c_str(), err, strerror(err));
141 }
142 return false;
143 }
144 uint32_t file_version = *reinterpret_cast<uint32_t*>(addr);
145 if (file_version != sCurrentFileVersion) {
146 ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version);
liulvping4832438c2018-12-20 20:34:56 +0800147 munmap(addr, sb.st_size);
John Reckdf1742e2017-01-19 15:56:21 -0800148 return false;
149 }
150
John Reck915883b2017-05-03 10:27:20 -0700151 void* data = reinterpret_cast<uint8_t*>(addr) + sHeaderSize;
152 int dataSize = sb.st_size - sHeaderSize;
153 io::ArrayInputStream input{data, dataSize};
John Reckdf1742e2017-01-19 15:56:21 -0800154 bool success = output->ParseFromZeroCopyStream(&input);
John Reck915883b2017-05-03 10:27:20 -0700155 if (!success) {
John Reck1bcacfd2017-11-03 10:12:19 -0700156 ALOGW("Parse failed on '%s' error='%s'", path.c_str(),
157 output->InitializationErrorString().c_str());
John Reckdf1742e2017-01-19 15:56:21 -0800158 }
liulvping4832438c2018-12-20 20:34:56 +0800159 munmap(addr, sb.st_size);
John Reckdf1742e2017-01-19 15:56:21 -0800160 return success;
161}
162
Alec Mouri04b0d812024-09-20 16:15:55 +0000163bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, uid_t uid,
164 const std::string& package, int64_t versionCode, int64_t startTime,
165 int64_t endTime, const ProfileData* data) {
John Reckdf1742e2017-01-19 15:56:21 -0800166 if (proto->stats_start() == 0 || proto->stats_start() > startTime) {
167 proto->set_stats_start(startTime);
168 }
169 if (proto->stats_end() == 0 || proto->stats_end() < endTime) {
170 proto->set_stats_end(endTime);
171 }
Alec Mouri04b0d812024-09-20 16:15:55 +0000172 proto->set_uid(static_cast<int32_t>(uid));
John Reckdf1742e2017-01-19 15:56:21 -0800173 proto->set_package_name(package);
174 proto->set_version_code(versionCode);
Stan Iliev637ba5e2019-08-16 13:43:08 -0400175 proto->set_pipeline(data->pipelineType() == RenderPipelineType::SkiaGL ?
176 GraphicsStatsProto_PipelineType_GL : GraphicsStatsProto_PipelineType_VULKAN);
John Reckdf1742e2017-01-19 15:56:21 -0800177 auto summary = proto->mutable_summary();
John Reck7075c792017-07-05 14:03:43 -0700178 summary->set_total_frames(summary->total_frames() + data->totalFrameCount());
179 summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount());
John Reck1bcacfd2017-11-03 10:12:19 -0700180 summary->set_missed_vsync_count(summary->missed_vsync_count() +
181 data->jankTypeCount(kMissedVsync));
182 summary->set_high_input_latency_count(summary->high_input_latency_count() +
183 data->jankTypeCount(kHighInputLatency));
184 summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() +
185 data->jankTypeCount(kSlowUI));
186 summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
187 data->jankTypeCount(kSlowSync));
188 summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT));
Stan Iliev637ba5e2019-08-16 13:43:08 -0400189 summary->set_missed_deadline_count(summary->missed_deadline_count() +
190 data->jankTypeCount(kMissedDeadline));
John Reckdf1742e2017-01-19 15:56:21 -0800191
192 bool creatingHistogram = false;
193 if (proto->histogram_size() == 0) {
194 proto->mutable_histogram()->Reserve(sHistogramSize);
195 creatingHistogram = true;
196 } else if (proto->histogram_size() != sHistogramSize) {
John Reck1bcacfd2017-11-03 10:12:19 -0700197 ALOGE("Histogram size mismatch, proto is %d expected %d", proto->histogram_size(),
198 sHistogramSize);
John Reck5206a872017-09-18 11:08:31 -0700199 return false;
John Reckdf1742e2017-01-19 15:56:21 -0800200 }
John Reck7075c792017-07-05 14:03:43 -0700201 int index = 0;
John Reck5206a872017-09-18 11:08:31 -0700202 bool hitMergeError = false;
John Reck7075c792017-07-05 14:03:43 -0700203 data->histogramForEach([&](ProfileData::HistogramEntry entry) {
John Reck5206a872017-09-18 11:08:31 -0700204 if (hitMergeError) return;
205
Kweku Adams1856a4c2018-04-03 16:31:10 -0700206 protos::GraphicsStatsHistogramBucketProto* bucket;
John Reckdf1742e2017-01-19 15:56:21 -0800207 if (creatingHistogram) {
208 bucket = proto->add_histogram();
John Reck7075c792017-07-05 14:03:43 -0700209 bucket->set_render_millis(entry.renderTimeMs);
John Reckdf1742e2017-01-19 15:56:21 -0800210 } else {
John Reck7075c792017-07-05 14:03:43 -0700211 bucket = proto->mutable_histogram(index);
John Reck5206a872017-09-18 11:08:31 -0700212 if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) {
John Reck1bcacfd2017-11-03 10:12:19 -0700213 ALOGW("Frame time mistmatch %d vs. %u", bucket->render_millis(),
214 entry.renderTimeMs);
John Reck5206a872017-09-18 11:08:31 -0700215 hitMergeError = true;
216 return;
217 }
John Reckdf1742e2017-01-19 15:56:21 -0800218 }
John Reck7075c792017-07-05 14:03:43 -0700219 bucket->set_frame_count(bucket->frame_count() + entry.frameCount);
220 index++;
221 });
Stan Iliev7203e1f2019-07-25 13:12:02 -0400222 if (hitMergeError) return false;
223 // fill in GPU frame time histogram
224 creatingHistogram = false;
225 if (proto->gpu_histogram_size() == 0) {
226 proto->mutable_gpu_histogram()->Reserve(sGPUHistogramSize);
227 creatingHistogram = true;
228 } else if (proto->gpu_histogram_size() != sGPUHistogramSize) {
229 ALOGE("GPU histogram size mismatch, proto is %d expected %d", proto->gpu_histogram_size(),
230 sGPUHistogramSize);
231 return false;
232 }
233 index = 0;
234 data->histogramGPUForEach([&](ProfileData::HistogramEntry entry) {
235 if (hitMergeError) return;
236
237 protos::GraphicsStatsHistogramBucketProto* bucket;
238 if (creatingHistogram) {
239 bucket = proto->add_gpu_histogram();
240 bucket->set_render_millis(entry.renderTimeMs);
241 } else {
242 bucket = proto->mutable_gpu_histogram(index);
243 if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) {
244 ALOGW("GPU frame time mistmatch %d vs. %u", bucket->render_millis(),
245 entry.renderTimeMs);
246 hitMergeError = true;
247 return;
248 }
249 }
250 bucket->set_frame_count(bucket->frame_count() + entry.frameCount);
251 index++;
252 });
John Reck5206a872017-09-18 11:08:31 -0700253 return !hitMergeError;
John Reckdf1742e2017-01-19 15:56:21 -0800254}
255
Kweku Adams1856a4c2018-04-03 16:31:10 -0700256static int32_t findPercentile(protos::GraphicsStatsProto* proto, int percentile) {
John Reckdf1742e2017-01-19 15:56:21 -0800257 int32_t pos = percentile * proto->summary().total_frames() / 100;
258 int32_t remaining = proto->summary().total_frames() - pos;
259 for (auto it = proto->histogram().rbegin(); it != proto->histogram().rend(); ++it) {
260 remaining -= it->frame_count();
261 if (remaining <= 0) {
262 return it->render_millis();
263 }
264 }
265 return 0;
266}
267
Stan Iliev7203e1f2019-07-25 13:12:02 -0400268static int32_t findGPUPercentile(protos::GraphicsStatsProto* proto, int percentile) {
269 uint32_t totalGPUFrameCount = 0; // this is usually proto->summary().total_frames() - 3.
270 for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) {
271 totalGPUFrameCount += it->frame_count();
272 }
273 int32_t pos = percentile * totalGPUFrameCount / 100;
274 int32_t remaining = totalGPUFrameCount - pos;
275 for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) {
276 remaining -= it->frame_count();
277 if (remaining <= 0) {
278 return it->render_millis();
279 }
280 }
281 return 0;
282}
283
Kweku Adams1856a4c2018-04-03 16:31:10 -0700284void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) {
John Reckdf1742e2017-01-19 15:56:21 -0800285 // This isn't a full validation, just enough that we can deref at will
John Reck5206a872017-09-18 11:08:31 -0700286 if (proto->package_name().empty() || !proto->has_summary()) {
287 ALOGW("Skipping dump, invalid package_name() '%s' or summary %d",
John Reck1bcacfd2017-11-03 10:12:19 -0700288 proto->package_name().c_str(), proto->has_summary());
John Reck5206a872017-09-18 11:08:31 -0700289 return;
290 }
Alec Mouri04b0d812024-09-20 16:15:55 +0000291 dprintf(fd, "\nUID: %d", proto->uid());
John Reckdf1742e2017-01-19 15:56:21 -0800292 dprintf(fd, "\nPackage: %s", proto->package_name().c_str());
Colin Crossd013a882018-10-26 13:04:41 -0700293 dprintf(fd, "\nVersion: %" PRId64, proto->version_code());
294 dprintf(fd, "\nStats since: %" PRId64 "ns", proto->stats_start());
295 dprintf(fd, "\nStats end: %" PRId64 "ns", proto->stats_end());
John Reckdf1742e2017-01-19 15:56:21 -0800296 auto summary = proto->summary();
297 dprintf(fd, "\nTotal frames rendered: %d", summary.total_frames());
298 dprintf(fd, "\nJanky frames: %d (%.2f%%)", summary.janky_frames(),
John Reck1bcacfd2017-11-03 10:12:19 -0700299 (float)summary.janky_frames() / (float)summary.total_frames() * 100.0f);
John Reckdf1742e2017-01-19 15:56:21 -0800300 dprintf(fd, "\n50th percentile: %dms", findPercentile(proto, 50));
301 dprintf(fd, "\n90th percentile: %dms", findPercentile(proto, 90));
302 dprintf(fd, "\n95th percentile: %dms", findPercentile(proto, 95));
303 dprintf(fd, "\n99th percentile: %dms", findPercentile(proto, 99));
304 dprintf(fd, "\nNumber Missed Vsync: %d", summary.missed_vsync_count());
305 dprintf(fd, "\nNumber High input latency: %d", summary.high_input_latency_count());
306 dprintf(fd, "\nNumber Slow UI thread: %d", summary.slow_ui_thread_count());
307 dprintf(fd, "\nNumber Slow bitmap uploads: %d", summary.slow_bitmap_upload_count());
308 dprintf(fd, "\nNumber Slow issue draw commands: %d", summary.slow_draw_count());
John Reck0e486472018-03-19 14:06:16 -0700309 dprintf(fd, "\nNumber Frame deadline missed: %d", summary.missed_deadline_count());
John Reckdf1742e2017-01-19 15:56:21 -0800310 dprintf(fd, "\nHISTOGRAM:");
311 for (const auto& it : proto->histogram()) {
312 dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
313 }
Stan Iliev7203e1f2019-07-25 13:12:02 -0400314 dprintf(fd, "\n50th gpu percentile: %dms", findGPUPercentile(proto, 50));
315 dprintf(fd, "\n90th gpu percentile: %dms", findGPUPercentile(proto, 90));
316 dprintf(fd, "\n95th gpu percentile: %dms", findGPUPercentile(proto, 95));
317 dprintf(fd, "\n99th gpu percentile: %dms", findGPUPercentile(proto, 99));
318 dprintf(fd, "\nGPU HISTOGRAM:");
319 for (const auto& it : proto->gpu_histogram()) {
320 dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
321 }
John Reckdf1742e2017-01-19 15:56:21 -0800322 dprintf(fd, "\n");
323}
324
Alec Mouri04b0d812024-09-20 16:15:55 +0000325void GraphicsStatsService::saveBuffer(const std::string& path, uid_t uid,
326 const std::string& package, int64_t versionCode,
327 int64_t startTime, int64_t endTime, const ProfileData* data) {
Kweku Adams1856a4c2018-04-03 16:31:10 -0700328 protos::GraphicsStatsProto statsProto;
John Reckdf1742e2017-01-19 15:56:21 -0800329 if (!parseFromFile(path, &statsProto)) {
330 statsProto.Clear();
331 }
Alec Mouri04b0d812024-09-20 16:15:55 +0000332 if (!mergeProfileDataIntoProto(&statsProto, uid, package, versionCode, startTime, endTime,
333 data)) {
John Reck5206a872017-09-18 11:08:31 -0700334 return;
335 }
John Reckdf1742e2017-01-19 15:56:21 -0800336 // Although we might not have read any data from the file, merging the existing data
337 // should always fully-initialize the proto
John Reck5206a872017-09-18 11:08:31 -0700338 if (!statsProto.IsInitialized()) {
339 ALOGE("proto initialization error %s", statsProto.InitializationErrorString().c_str());
340 return;
341 }
342 if (statsProto.package_name().empty() || !statsProto.has_summary()) {
John Reck1bcacfd2017-11-03 10:12:19 -0700343 ALOGE("missing package_name() '%s' summary %d", statsProto.package_name().c_str(),
344 statsProto.has_summary());
John Reck5206a872017-09-18 11:08:31 -0700345 return;
346 }
John Reckdf1742e2017-01-19 15:56:21 -0800347 int outFd = open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0660);
348 if (outFd <= 0) {
349 int err = errno;
350 ALOGW("Failed to open '%s', error=%d (%s)", path.c_str(), err, strerror(err));
351 return;
352 }
353 int wrote = write(outFd, &sCurrentFileVersion, sHeaderSize);
354 if (wrote != sHeaderSize) {
355 int err = errno;
John Reck1bcacfd2017-11-03 10:12:19 -0700356 ALOGW("Failed to write header to '%s', returned=%d errno=%d (%s)", path.c_str(), wrote, err,
357 strerror(err));
John Reckdf1742e2017-01-19 15:56:21 -0800358 close(outFd);
359 return;
360 }
361 {
John Reck915883b2017-05-03 10:27:20 -0700362 FileOutputStreamLite output(outFd);
John Reckdf1742e2017-01-19 15:56:21 -0800363 bool success = statsProto.SerializeToZeroCopyStream(&output) && output.Flush();
364 if (output.GetErrno() != 0) {
John Reck1bcacfd2017-11-03 10:12:19 -0700365 ALOGW("Error writing to fd=%d, path='%s' err=%d (%s)", outFd, path.c_str(),
366 output.GetErrno(), strerror(output.GetErrno()));
John Reckdf1742e2017-01-19 15:56:21 -0800367 success = false;
368 } else if (!success) {
369 ALOGW("Serialize failed on '%s' unknown error", path.c_str());
370 }
371 }
372 close(outFd);
373}
374
375class GraphicsStatsService::Dump {
376public:
Stan Iliev637ba5e2019-08-16 13:43:08 -0400377 Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {
378 if (mFd == -1 && mType == DumpType::Protobuf) {
379 mType = DumpType::ProtobufStatsd;
380 }
381 }
John Reckdf1742e2017-01-19 15:56:21 -0800382 int fd() { return mFd; }
383 DumpType type() { return mType; }
Kweku Adams1856a4c2018-04-03 16:31:10 -0700384 protos::GraphicsStatsServiceDumpProto& proto() { return mProto; }
Stan Iliev637ba5e2019-08-16 13:43:08 -0400385 void mergeStat(const protos::GraphicsStatsProto& stat);
386 void updateProto();
John Reck1bcacfd2017-11-03 10:12:19 -0700387
John Reckdf1742e2017-01-19 15:56:21 -0800388private:
Stan Iliev637ba5e2019-08-16 13:43:08 -0400389 // use package name and app version for a key
Alec Mouri04b0d812024-09-20 16:15:55 +0000390 typedef std::tuple<uid_t, std::string, int64_t> DumpKey;
Stan Iliev637ba5e2019-08-16 13:43:08 -0400391
392 std::map<DumpKey, protos::GraphicsStatsProto> mStats;
John Reckdf1742e2017-01-19 15:56:21 -0800393 int mFd;
394 DumpType mType;
Kweku Adams1856a4c2018-04-03 16:31:10 -0700395 protos::GraphicsStatsServiceDumpProto mProto;
John Reckdf1742e2017-01-19 15:56:21 -0800396};
397
Stan Iliev637ba5e2019-08-16 13:43:08 -0400398void GraphicsStatsService::Dump::mergeStat(const protos::GraphicsStatsProto& stat) {
Alec Mouri04b0d812024-09-20 16:15:55 +0000399 auto dumpKey = std::make_tuple(static_cast<uid_t>(stat.uid()), stat.package_name(),
400 stat.version_code());
Stan Iliev637ba5e2019-08-16 13:43:08 -0400401 auto findIt = mStats.find(dumpKey);
402 if (findIt == mStats.end()) {
403 mStats[dumpKey] = stat;
404 } else {
405 auto summary = findIt->second.mutable_summary();
406 summary->set_total_frames(summary->total_frames() + stat.summary().total_frames());
407 summary->set_janky_frames(summary->janky_frames() + stat.summary().janky_frames());
408 summary->set_missed_vsync_count(summary->missed_vsync_count() +
409 stat.summary().missed_vsync_count());
410 summary->set_high_input_latency_count(summary->high_input_latency_count() +
411 stat.summary().high_input_latency_count());
412 summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() +
413 stat.summary().slow_ui_thread_count());
414 summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
415 stat.summary().slow_bitmap_upload_count());
416 summary->set_slow_draw_count(summary->slow_draw_count() + stat.summary().slow_draw_count());
417 summary->set_missed_deadline_count(summary->missed_deadline_count() +
418 stat.summary().missed_deadline_count());
419 for (int bucketIndex = 0; bucketIndex < findIt->second.histogram_size(); bucketIndex++) {
420 auto bucket = findIt->second.mutable_histogram(bucketIndex);
421 bucket->set_frame_count(bucket->frame_count() +
422 stat.histogram(bucketIndex).frame_count());
423 }
424 for (int bucketIndex = 0; bucketIndex < findIt->second.gpu_histogram_size();
425 bucketIndex++) {
426 auto bucket = findIt->second.mutable_gpu_histogram(bucketIndex);
427 bucket->set_frame_count(bucket->frame_count() +
428 stat.gpu_histogram(bucketIndex).frame_count());
429 }
430 findIt->second.set_stats_start(std::min(findIt->second.stats_start(), stat.stats_start()));
431 findIt->second.set_stats_end(std::max(findIt->second.stats_end(), stat.stats_end()));
432 }
433}
434
435void GraphicsStatsService::Dump::updateProto() {
436 for (auto& stat : mStats) {
437 mProto.add_stats()->CopyFrom(stat.second);
438 }
439}
440
John Reckdf1742e2017-01-19 15:56:21 -0800441GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType type) {
442 return new Dump(outFd, type);
443}
444
Alec Mouri04b0d812024-09-20 16:15:55 +0000445void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, uid_t uid,
Dianne Hackborn73453e42017-12-11 16:30:36 -0800446 const std::string& package, int64_t versionCode,
447 int64_t startTime, int64_t endTime, const ProfileData* data) {
Kweku Adams1856a4c2018-04-03 16:31:10 -0700448 protos::GraphicsStatsProto statsProto;
John Reckdf1742e2017-01-19 15:56:21 -0800449 if (!path.empty() && !parseFromFile(path, &statsProto)) {
450 statsProto.Clear();
451 }
Alec Mouri04b0d812024-09-20 16:15:55 +0000452 if (data && !mergeProfileDataIntoProto(&statsProto, uid, package, versionCode, startTime,
453 endTime, data)) {
John Reck5206a872017-09-18 11:08:31 -0700454 return;
John Reckdf1742e2017-01-19 15:56:21 -0800455 }
456 if (!statsProto.IsInitialized()) {
457 ALOGW("Failed to load profile data from path '%s' and data %p",
John Reck1bcacfd2017-11-03 10:12:19 -0700458 path.empty() ? "<empty>" : path.c_str(), data);
John Reckdf1742e2017-01-19 15:56:21 -0800459 return;
460 }
Stan Iliev637ba5e2019-08-16 13:43:08 -0400461 if (dump->type() == DumpType::ProtobufStatsd) {
462 dump->mergeStat(statsProto);
463 } else if (dump->type() == DumpType::Protobuf) {
John Reckdf1742e2017-01-19 15:56:21 -0800464 dump->proto().add_stats()->CopyFrom(statsProto);
465 } else {
466 dumpAsTextToFd(&statsProto, dump->fd());
467 }
468}
469
470void GraphicsStatsService::addToDump(Dump* dump, const std::string& path) {
Kweku Adams1856a4c2018-04-03 16:31:10 -0700471 protos::GraphicsStatsProto statsProto;
John Reckdf1742e2017-01-19 15:56:21 -0800472 if (!parseFromFile(path, &statsProto)) {
473 return;
474 }
Stan Iliev637ba5e2019-08-16 13:43:08 -0400475 if (dump->type() == DumpType::ProtobufStatsd) {
476 dump->mergeStat(statsProto);
477 } else if (dump->type() == DumpType::Protobuf) {
John Reckdf1742e2017-01-19 15:56:21 -0800478 dump->proto().add_stats()->CopyFrom(statsProto);
479 } else {
480 dumpAsTextToFd(&statsProto, dump->fd());
481 }
482}
483
484void GraphicsStatsService::finishDump(Dump* dump) {
485 if (dump->type() == DumpType::Protobuf) {
John Reck915883b2017-05-03 10:27:20 -0700486 FileOutputStreamLite stream(dump->fd());
John Reckdf1742e2017-01-19 15:56:21 -0800487 dump->proto().SerializeToZeroCopyStream(&stream);
488 }
489 delete dump;
490}
491
Stan Ilievc9043812020-02-03 16:57:09 -0500492using namespace google::protobuf;
Stan Iliev637ba5e2019-08-16 13:43:08 -0400493
Stan Ilievc9043812020-02-03 16:57:09 -0500494// Field ids taken from FrameTimingHistogram message in atoms.proto
495#define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
496#define FRAME_COUNTS_FIELD_NUMBER 2
Stan Iliev637ba5e2019-08-16 13:43:08 -0400497
Stan Ilievc9043812020-02-03 16:57:09 -0500498static void writeCpuHistogram(AStatsEvent* event,
499 const uirenderer::protos::GraphicsStatsProto& stat) {
500 util::ProtoOutputStream proto;
501 for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
502 auto& bucket = stat.histogram(bucketIndex);
503 proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
504 TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
505 (int)bucket.render_millis());
Stan Iliev637ba5e2019-08-16 13:43:08 -0400506 }
Stan Ilievc9043812020-02-03 16:57:09 -0500507 for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
508 auto& bucket = stat.histogram(bucketIndex);
509 proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
510 FRAME_COUNTS_FIELD_NUMBER /* field id */,
511 (long long)bucket.frame_count());
Stan Iliev637ba5e2019-08-16 13:43:08 -0400512 }
Stan Ilievc9043812020-02-03 16:57:09 -0500513 std::vector<uint8_t> outVector;
514 proto.serializeToVector(&outVector);
515 AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
Stan Iliev637ba5e2019-08-16 13:43:08 -0400516}
517
Stan Ilievc9043812020-02-03 16:57:09 -0500518static void writeGpuHistogram(AStatsEvent* event,
519 const uirenderer::protos::GraphicsStatsProto& stat) {
520 util::ProtoOutputStream proto;
521 for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
522 auto& bucket = stat.gpu_histogram(bucketIndex);
523 proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
524 TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
525 (int)bucket.render_millis());
526 }
527 for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
528 auto& bucket = stat.gpu_histogram(bucketIndex);
529 proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
530 FRAME_COUNTS_FIELD_NUMBER /* field id */,
531 (long long)bucket.frame_count());
532 }
533 std::vector<uint8_t> outVector;
534 proto.serializeToVector(&outVector);
535 AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
536}
537
538
539void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data,
540 bool lastFullDay) {
541 dump->updateProto();
542 auto& serviceDump = dump->proto();
543 for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
544 auto& stat = serviceDump.stats(stat_index);
545 AStatsEvent* event = AStatsEventList_addStatsEvent(data);
Tej Singh78f65b62021-03-18 16:19:55 -0700546 AStatsEvent_setAtomId(event, stats::GRAPHICS_STATS);
Stan Ilievc9043812020-02-03 16:57:09 -0500547 AStatsEvent_writeString(event, stat.package_name().c_str());
548 AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
549 AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
550 AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
551 AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
552 AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
553 AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
554 AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
555 AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
556 AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
557 AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
558 AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
559 writeCpuHistogram(event, stat);
560 writeGpuHistogram(event, stat);
561 // TODO: fill in UI mainline module version, when the feature is available.
562 AStatsEvent_writeInt64(event, (int64_t)0);
563 AStatsEvent_writeBool(event, !lastFullDay);
Alec Mouri04b0d812024-09-20 16:15:55 +0000564 AStatsEvent_writeInt32(event, stat.uid());
565 AStatsEvent_addBoolAnnotation(event, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
Stan Ilievc9043812020-02-03 16:57:09 -0500566 AStatsEvent_build(event);
567 }
Florian Mayer6e1b4a42020-08-06 12:55:42 +0000568 delete dump;
Stan Ilievc9043812020-02-03 16:57:09 -0500569}
570
571
John Reckdf1742e2017-01-19 15:56:21 -0800572} /* namespace uirenderer */
Dan Albert110e0072017-10-11 12:41:26 -0700573} /* namespace android */