1*d57664e9SAndroid Build Coastguard Worker /*
2*d57664e9SAndroid Build Coastguard Worker * Copyright (C) 2017 The Android Open Source Project
3*d57664e9SAndroid Build Coastguard Worker *
4*d57664e9SAndroid Build Coastguard Worker * Licensed under the Apache License, Version 2.0 (the "License");
5*d57664e9SAndroid Build Coastguard Worker * you may not use this file except in compliance with the License.
6*d57664e9SAndroid Build Coastguard Worker * You may obtain a copy of the License at
7*d57664e9SAndroid Build Coastguard Worker *
8*d57664e9SAndroid Build Coastguard Worker * http://www.apache.org/licenses/LICENSE-2.0
9*d57664e9SAndroid Build Coastguard Worker *
10*d57664e9SAndroid Build Coastguard Worker * Unless required by applicable law or agreed to in writing, software
11*d57664e9SAndroid Build Coastguard Worker * distributed under the License is distributed on an "AS IS" BASIS,
12*d57664e9SAndroid Build Coastguard Worker * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13*d57664e9SAndroid Build Coastguard Worker * See the License for the specific language governing permissions and
14*d57664e9SAndroid Build Coastguard Worker * limitations under the License.
15*d57664e9SAndroid Build Coastguard Worker */
16*d57664e9SAndroid Build Coastguard Worker
17*d57664e9SAndroid Build Coastguard Worker #include "GraphicsStatsService.h"
18*d57664e9SAndroid Build Coastguard Worker
19*d57664e9SAndroid Build Coastguard Worker #include <android/util/ProtoOutputStream.h>
20*d57664e9SAndroid Build Coastguard Worker #include <errno.h>
21*d57664e9SAndroid Build Coastguard Worker #include <fcntl.h>
22*d57664e9SAndroid Build Coastguard Worker #include <google/protobuf/io/zero_copy_stream_impl_lite.h>
23*d57664e9SAndroid Build Coastguard Worker #include <inttypes.h>
24*d57664e9SAndroid Build Coastguard Worker #include <log/log.h>
25*d57664e9SAndroid Build Coastguard Worker #include <stats_annotations.h>
26*d57664e9SAndroid Build Coastguard Worker #include <stats_event.h>
27*d57664e9SAndroid Build Coastguard Worker #include <statslog_hwui.h>
28*d57664e9SAndroid Build Coastguard Worker #include <sys/mman.h>
29*d57664e9SAndroid Build Coastguard Worker #include <sys/stat.h>
30*d57664e9SAndroid Build Coastguard Worker #include <sys/types.h>
31*d57664e9SAndroid Build Coastguard Worker #include <unistd.h>
32*d57664e9SAndroid Build Coastguard Worker
33*d57664e9SAndroid Build Coastguard Worker #include "JankTracker.h"
34*d57664e9SAndroid Build Coastguard Worker #include "protos/graphicsstats.pb.h"
35*d57664e9SAndroid Build Coastguard Worker
36*d57664e9SAndroid Build Coastguard Worker namespace android {
37*d57664e9SAndroid Build Coastguard Worker namespace uirenderer {
38*d57664e9SAndroid Build Coastguard Worker
39*d57664e9SAndroid Build Coastguard Worker using namespace google::protobuf;
40*d57664e9SAndroid Build Coastguard Worker using namespace uirenderer::protos;
41*d57664e9SAndroid Build Coastguard Worker
42*d57664e9SAndroid Build Coastguard Worker constexpr int32_t sCurrentFileVersion = 1;
43*d57664e9SAndroid Build Coastguard Worker constexpr int32_t sHeaderSize = 4;
44*d57664e9SAndroid Build Coastguard Worker static_assert(sizeof(sCurrentFileVersion) == sHeaderSize, "Header size is wrong");
45*d57664e9SAndroid Build Coastguard Worker
46*d57664e9SAndroid Build Coastguard Worker constexpr int sHistogramSize = ProfileData::HistogramSize();
47*d57664e9SAndroid Build Coastguard Worker constexpr int sGPUHistogramSize = ProfileData::GPUHistogramSize();
48*d57664e9SAndroid Build Coastguard Worker
49*d57664e9SAndroid Build Coastguard Worker static bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, uid_t uid,
50*d57664e9SAndroid Build Coastguard Worker const std::string& package, int64_t versionCode,
51*d57664e9SAndroid Build Coastguard Worker int64_t startTime, int64_t endTime, const ProfileData* data);
52*d57664e9SAndroid Build Coastguard Worker static void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int outFd);
53*d57664e9SAndroid Build Coastguard Worker
54*d57664e9SAndroid Build Coastguard Worker class FileDescriptor {
55*d57664e9SAndroid Build Coastguard Worker public:
FileDescriptor(int fd)56*d57664e9SAndroid Build Coastguard Worker explicit FileDescriptor(int fd) : mFd(fd) {}
~FileDescriptor()57*d57664e9SAndroid Build Coastguard Worker ~FileDescriptor() {
58*d57664e9SAndroid Build Coastguard Worker if (mFd != -1) {
59*d57664e9SAndroid Build Coastguard Worker close(mFd);
60*d57664e9SAndroid Build Coastguard Worker mFd = -1;
61*d57664e9SAndroid Build Coastguard Worker }
62*d57664e9SAndroid Build Coastguard Worker }
valid()63*d57664e9SAndroid Build Coastguard Worker bool valid() { return mFd != -1; }
operator int()64*d57664e9SAndroid Build Coastguard Worker operator int() { return mFd; } // NOLINT(google-explicit-constructor)
65*d57664e9SAndroid Build Coastguard Worker
66*d57664e9SAndroid Build Coastguard Worker private:
67*d57664e9SAndroid Build Coastguard Worker int mFd;
68*d57664e9SAndroid Build Coastguard Worker };
69*d57664e9SAndroid Build Coastguard Worker
70*d57664e9SAndroid Build Coastguard Worker class FileOutputStreamLite : public io::ZeroCopyOutputStream {
71*d57664e9SAndroid Build Coastguard Worker public:
FileOutputStreamLite(int fd)72*d57664e9SAndroid Build Coastguard Worker explicit FileOutputStreamLite(int fd) : mCopyAdapter(fd), mImpl(&mCopyAdapter) {}
~FileOutputStreamLite()73*d57664e9SAndroid Build Coastguard Worker virtual ~FileOutputStreamLite() {}
74*d57664e9SAndroid Build Coastguard Worker
GetErrno()75*d57664e9SAndroid Build Coastguard Worker int GetErrno() { return mCopyAdapter.mErrno; }
76*d57664e9SAndroid Build Coastguard Worker
Next(void ** data,int * size)77*d57664e9SAndroid Build Coastguard Worker virtual bool Next(void** data, int* size) override { return mImpl.Next(data, size); }
78*d57664e9SAndroid Build Coastguard Worker
BackUp(int count)79*d57664e9SAndroid Build Coastguard Worker virtual void BackUp(int count) override { mImpl.BackUp(count); }
80*d57664e9SAndroid Build Coastguard Worker
ByteCount() const81*d57664e9SAndroid Build Coastguard Worker virtual int64 ByteCount() const override { return mImpl.ByteCount(); }
82*d57664e9SAndroid Build Coastguard Worker
Flush()83*d57664e9SAndroid Build Coastguard Worker bool Flush() { return mImpl.Flush(); }
84*d57664e9SAndroid Build Coastguard Worker
85*d57664e9SAndroid Build Coastguard Worker private:
86*d57664e9SAndroid Build Coastguard Worker struct FDAdapter : public io::CopyingOutputStream {
87*d57664e9SAndroid Build Coastguard Worker int mFd;
88*d57664e9SAndroid Build Coastguard Worker int mErrno = 0;
89*d57664e9SAndroid Build Coastguard Worker
FDAdapterandroid::uirenderer::FileOutputStreamLite::FDAdapter90*d57664e9SAndroid Build Coastguard Worker explicit FDAdapter(int fd) : mFd(fd) {}
~FDAdapterandroid::uirenderer::FileOutputStreamLite::FDAdapter91*d57664e9SAndroid Build Coastguard Worker virtual ~FDAdapter() {}
92*d57664e9SAndroid Build Coastguard Worker
Writeandroid::uirenderer::FileOutputStreamLite::FDAdapter93*d57664e9SAndroid Build Coastguard Worker virtual bool Write(const void* buffer, int size) override {
94*d57664e9SAndroid Build Coastguard Worker int ret;
95*d57664e9SAndroid Build Coastguard Worker while (size) {
96*d57664e9SAndroid Build Coastguard Worker ret = TEMP_FAILURE_RETRY(write(mFd, buffer, size));
97*d57664e9SAndroid Build Coastguard Worker if (ret <= 0) {
98*d57664e9SAndroid Build Coastguard Worker mErrno = errno;
99*d57664e9SAndroid Build Coastguard Worker return false;
100*d57664e9SAndroid Build Coastguard Worker }
101*d57664e9SAndroid Build Coastguard Worker size -= ret;
102*d57664e9SAndroid Build Coastguard Worker }
103*d57664e9SAndroid Build Coastguard Worker return true;
104*d57664e9SAndroid Build Coastguard Worker }
105*d57664e9SAndroid Build Coastguard Worker };
106*d57664e9SAndroid Build Coastguard Worker
107*d57664e9SAndroid Build Coastguard Worker FileOutputStreamLite::FDAdapter mCopyAdapter;
108*d57664e9SAndroid Build Coastguard Worker io::CopyingOutputStreamAdaptor mImpl;
109*d57664e9SAndroid Build Coastguard Worker };
110*d57664e9SAndroid Build Coastguard Worker
parseFromFile(const std::string & path,protos::GraphicsStatsProto * output)111*d57664e9SAndroid Build Coastguard Worker bool GraphicsStatsService::parseFromFile(const std::string& path,
112*d57664e9SAndroid Build Coastguard Worker protos::GraphicsStatsProto* output) {
113*d57664e9SAndroid Build Coastguard Worker FileDescriptor fd{open(path.c_str(), O_RDONLY)};
114*d57664e9SAndroid Build Coastguard Worker if (!fd.valid()) {
115*d57664e9SAndroid Build Coastguard Worker int err = errno;
116*d57664e9SAndroid Build Coastguard Worker // The file not existing is normal for addToDump(), so only log if
117*d57664e9SAndroid Build Coastguard Worker // we get an unexpected error
118*d57664e9SAndroid Build Coastguard Worker if (err != ENOENT) {
119*d57664e9SAndroid Build Coastguard Worker ALOGW("Failed to open '%s', errno=%d (%s)", path.c_str(), err, strerror(err));
120*d57664e9SAndroid Build Coastguard Worker }
121*d57664e9SAndroid Build Coastguard Worker return false;
122*d57664e9SAndroid Build Coastguard Worker }
123*d57664e9SAndroid Build Coastguard Worker struct stat sb;
124*d57664e9SAndroid Build Coastguard Worker if (fstat(fd, &sb) || sb.st_size < sHeaderSize) {
125*d57664e9SAndroid Build Coastguard Worker int err = errno;
126*d57664e9SAndroid Build Coastguard Worker // The file not existing is normal for addToDump(), so only log if
127*d57664e9SAndroid Build Coastguard Worker // we get an unexpected error
128*d57664e9SAndroid Build Coastguard Worker if (err != ENOENT) {
129*d57664e9SAndroid Build Coastguard Worker ALOGW("Failed to fstat '%s', errno=%d (%s) (st_size %d)", path.c_str(), err,
130*d57664e9SAndroid Build Coastguard Worker strerror(err), (int)sb.st_size);
131*d57664e9SAndroid Build Coastguard Worker }
132*d57664e9SAndroid Build Coastguard Worker return false;
133*d57664e9SAndroid Build Coastguard Worker }
134*d57664e9SAndroid Build Coastguard Worker void* addr = mmap(nullptr, sb.st_size, PROT_READ, MAP_SHARED, fd, 0);
135*d57664e9SAndroid Build Coastguard Worker if (addr == MAP_FAILED) {
136*d57664e9SAndroid Build Coastguard Worker int err = errno;
137*d57664e9SAndroid Build Coastguard Worker // The file not existing is normal for addToDump(), so only log if
138*d57664e9SAndroid Build Coastguard Worker // we get an unexpected error
139*d57664e9SAndroid Build Coastguard Worker if (err != ENOENT) {
140*d57664e9SAndroid Build Coastguard Worker ALOGW("Failed to mmap '%s', errno=%d (%s)", path.c_str(), err, strerror(err));
141*d57664e9SAndroid Build Coastguard Worker }
142*d57664e9SAndroid Build Coastguard Worker return false;
143*d57664e9SAndroid Build Coastguard Worker }
144*d57664e9SAndroid Build Coastguard Worker uint32_t file_version = *reinterpret_cast<uint32_t*>(addr);
145*d57664e9SAndroid Build Coastguard Worker if (file_version != sCurrentFileVersion) {
146*d57664e9SAndroid Build Coastguard Worker ALOGW("file_version mismatch! expected %d got %d", sCurrentFileVersion, file_version);
147*d57664e9SAndroid Build Coastguard Worker munmap(addr, sb.st_size);
148*d57664e9SAndroid Build Coastguard Worker return false;
149*d57664e9SAndroid Build Coastguard Worker }
150*d57664e9SAndroid Build Coastguard Worker
151*d57664e9SAndroid Build Coastguard Worker void* data = reinterpret_cast<uint8_t*>(addr) + sHeaderSize;
152*d57664e9SAndroid Build Coastguard Worker int dataSize = sb.st_size - sHeaderSize;
153*d57664e9SAndroid Build Coastguard Worker io::ArrayInputStream input{data, dataSize};
154*d57664e9SAndroid Build Coastguard Worker bool success = output->ParseFromZeroCopyStream(&input);
155*d57664e9SAndroid Build Coastguard Worker if (!success) {
156*d57664e9SAndroid Build Coastguard Worker ALOGW("Parse failed on '%s' error='%s'", path.c_str(),
157*d57664e9SAndroid Build Coastguard Worker output->InitializationErrorString().c_str());
158*d57664e9SAndroid Build Coastguard Worker }
159*d57664e9SAndroid Build Coastguard Worker munmap(addr, sb.st_size);
160*d57664e9SAndroid Build Coastguard Worker return success;
161*d57664e9SAndroid Build Coastguard Worker }
162*d57664e9SAndroid Build Coastguard Worker
mergeProfileDataIntoProto(protos::GraphicsStatsProto * proto,uid_t uid,const std::string & package,int64_t versionCode,int64_t startTime,int64_t endTime,const ProfileData * data)163*d57664e9SAndroid Build Coastguard Worker bool mergeProfileDataIntoProto(protos::GraphicsStatsProto* proto, uid_t uid,
164*d57664e9SAndroid Build Coastguard Worker const std::string& package, int64_t versionCode, int64_t startTime,
165*d57664e9SAndroid Build Coastguard Worker int64_t endTime, const ProfileData* data) {
166*d57664e9SAndroid Build Coastguard Worker if (proto->stats_start() == 0 || proto->stats_start() > startTime) {
167*d57664e9SAndroid Build Coastguard Worker proto->set_stats_start(startTime);
168*d57664e9SAndroid Build Coastguard Worker }
169*d57664e9SAndroid Build Coastguard Worker if (proto->stats_end() == 0 || proto->stats_end() < endTime) {
170*d57664e9SAndroid Build Coastguard Worker proto->set_stats_end(endTime);
171*d57664e9SAndroid Build Coastguard Worker }
172*d57664e9SAndroid Build Coastguard Worker proto->set_uid(static_cast<int32_t>(uid));
173*d57664e9SAndroid Build Coastguard Worker proto->set_package_name(package);
174*d57664e9SAndroid Build Coastguard Worker proto->set_version_code(versionCode);
175*d57664e9SAndroid Build Coastguard Worker proto->set_pipeline(data->pipelineType() == RenderPipelineType::SkiaGL ?
176*d57664e9SAndroid Build Coastguard Worker GraphicsStatsProto_PipelineType_GL : GraphicsStatsProto_PipelineType_VULKAN);
177*d57664e9SAndroid Build Coastguard Worker auto summary = proto->mutable_summary();
178*d57664e9SAndroid Build Coastguard Worker summary->set_total_frames(summary->total_frames() + data->totalFrameCount());
179*d57664e9SAndroid Build Coastguard Worker summary->set_janky_frames(summary->janky_frames() + data->jankFrameCount());
180*d57664e9SAndroid Build Coastguard Worker summary->set_missed_vsync_count(summary->missed_vsync_count() +
181*d57664e9SAndroid Build Coastguard Worker data->jankTypeCount(kMissedVsync));
182*d57664e9SAndroid Build Coastguard Worker summary->set_high_input_latency_count(summary->high_input_latency_count() +
183*d57664e9SAndroid Build Coastguard Worker data->jankTypeCount(kHighInputLatency));
184*d57664e9SAndroid Build Coastguard Worker summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() +
185*d57664e9SAndroid Build Coastguard Worker data->jankTypeCount(kSlowUI));
186*d57664e9SAndroid Build Coastguard Worker summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
187*d57664e9SAndroid Build Coastguard Worker data->jankTypeCount(kSlowSync));
188*d57664e9SAndroid Build Coastguard Worker summary->set_slow_draw_count(summary->slow_draw_count() + data->jankTypeCount(kSlowRT));
189*d57664e9SAndroid Build Coastguard Worker summary->set_missed_deadline_count(summary->missed_deadline_count() +
190*d57664e9SAndroid Build Coastguard Worker data->jankTypeCount(kMissedDeadline));
191*d57664e9SAndroid Build Coastguard Worker
192*d57664e9SAndroid Build Coastguard Worker bool creatingHistogram = false;
193*d57664e9SAndroid Build Coastguard Worker if (proto->histogram_size() == 0) {
194*d57664e9SAndroid Build Coastguard Worker proto->mutable_histogram()->Reserve(sHistogramSize);
195*d57664e9SAndroid Build Coastguard Worker creatingHistogram = true;
196*d57664e9SAndroid Build Coastguard Worker } else if (proto->histogram_size() != sHistogramSize) {
197*d57664e9SAndroid Build Coastguard Worker ALOGE("Histogram size mismatch, proto is %d expected %d", proto->histogram_size(),
198*d57664e9SAndroid Build Coastguard Worker sHistogramSize);
199*d57664e9SAndroid Build Coastguard Worker return false;
200*d57664e9SAndroid Build Coastguard Worker }
201*d57664e9SAndroid Build Coastguard Worker int index = 0;
202*d57664e9SAndroid Build Coastguard Worker bool hitMergeError = false;
203*d57664e9SAndroid Build Coastguard Worker data->histogramForEach([&](ProfileData::HistogramEntry entry) {
204*d57664e9SAndroid Build Coastguard Worker if (hitMergeError) return;
205*d57664e9SAndroid Build Coastguard Worker
206*d57664e9SAndroid Build Coastguard Worker protos::GraphicsStatsHistogramBucketProto* bucket;
207*d57664e9SAndroid Build Coastguard Worker if (creatingHistogram) {
208*d57664e9SAndroid Build Coastguard Worker bucket = proto->add_histogram();
209*d57664e9SAndroid Build Coastguard Worker bucket->set_render_millis(entry.renderTimeMs);
210*d57664e9SAndroid Build Coastguard Worker } else {
211*d57664e9SAndroid Build Coastguard Worker bucket = proto->mutable_histogram(index);
212*d57664e9SAndroid Build Coastguard Worker if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) {
213*d57664e9SAndroid Build Coastguard Worker ALOGW("Frame time mistmatch %d vs. %u", bucket->render_millis(),
214*d57664e9SAndroid Build Coastguard Worker entry.renderTimeMs);
215*d57664e9SAndroid Build Coastguard Worker hitMergeError = true;
216*d57664e9SAndroid Build Coastguard Worker return;
217*d57664e9SAndroid Build Coastguard Worker }
218*d57664e9SAndroid Build Coastguard Worker }
219*d57664e9SAndroid Build Coastguard Worker bucket->set_frame_count(bucket->frame_count() + entry.frameCount);
220*d57664e9SAndroid Build Coastguard Worker index++;
221*d57664e9SAndroid Build Coastguard Worker });
222*d57664e9SAndroid Build Coastguard Worker if (hitMergeError) return false;
223*d57664e9SAndroid Build Coastguard Worker // fill in GPU frame time histogram
224*d57664e9SAndroid Build Coastguard Worker creatingHistogram = false;
225*d57664e9SAndroid Build Coastguard Worker if (proto->gpu_histogram_size() == 0) {
226*d57664e9SAndroid Build Coastguard Worker proto->mutable_gpu_histogram()->Reserve(sGPUHistogramSize);
227*d57664e9SAndroid Build Coastguard Worker creatingHistogram = true;
228*d57664e9SAndroid Build Coastguard Worker } else if (proto->gpu_histogram_size() != sGPUHistogramSize) {
229*d57664e9SAndroid Build Coastguard Worker ALOGE("GPU histogram size mismatch, proto is %d expected %d", proto->gpu_histogram_size(),
230*d57664e9SAndroid Build Coastguard Worker sGPUHistogramSize);
231*d57664e9SAndroid Build Coastguard Worker return false;
232*d57664e9SAndroid Build Coastguard Worker }
233*d57664e9SAndroid Build Coastguard Worker index = 0;
234*d57664e9SAndroid Build Coastguard Worker data->histogramGPUForEach([&](ProfileData::HistogramEntry entry) {
235*d57664e9SAndroid Build Coastguard Worker if (hitMergeError) return;
236*d57664e9SAndroid Build Coastguard Worker
237*d57664e9SAndroid Build Coastguard Worker protos::GraphicsStatsHistogramBucketProto* bucket;
238*d57664e9SAndroid Build Coastguard Worker if (creatingHistogram) {
239*d57664e9SAndroid Build Coastguard Worker bucket = proto->add_gpu_histogram();
240*d57664e9SAndroid Build Coastguard Worker bucket->set_render_millis(entry.renderTimeMs);
241*d57664e9SAndroid Build Coastguard Worker } else {
242*d57664e9SAndroid Build Coastguard Worker bucket = proto->mutable_gpu_histogram(index);
243*d57664e9SAndroid Build Coastguard Worker if (bucket->render_millis() != static_cast<int32_t>(entry.renderTimeMs)) {
244*d57664e9SAndroid Build Coastguard Worker ALOGW("GPU frame time mistmatch %d vs. %u", bucket->render_millis(),
245*d57664e9SAndroid Build Coastguard Worker entry.renderTimeMs);
246*d57664e9SAndroid Build Coastguard Worker hitMergeError = true;
247*d57664e9SAndroid Build Coastguard Worker return;
248*d57664e9SAndroid Build Coastguard Worker }
249*d57664e9SAndroid Build Coastguard Worker }
250*d57664e9SAndroid Build Coastguard Worker bucket->set_frame_count(bucket->frame_count() + entry.frameCount);
251*d57664e9SAndroid Build Coastguard Worker index++;
252*d57664e9SAndroid Build Coastguard Worker });
253*d57664e9SAndroid Build Coastguard Worker return !hitMergeError;
254*d57664e9SAndroid Build Coastguard Worker }
255*d57664e9SAndroid Build Coastguard Worker
findPercentile(protos::GraphicsStatsProto * proto,int percentile)256*d57664e9SAndroid Build Coastguard Worker static int32_t findPercentile(protos::GraphicsStatsProto* proto, int percentile) {
257*d57664e9SAndroid Build Coastguard Worker int32_t pos = percentile * proto->summary().total_frames() / 100;
258*d57664e9SAndroid Build Coastguard Worker int32_t remaining = proto->summary().total_frames() - pos;
259*d57664e9SAndroid Build Coastguard Worker for (auto it = proto->histogram().rbegin(); it != proto->histogram().rend(); ++it) {
260*d57664e9SAndroid Build Coastguard Worker remaining -= it->frame_count();
261*d57664e9SAndroid Build Coastguard Worker if (remaining <= 0) {
262*d57664e9SAndroid Build Coastguard Worker return it->render_millis();
263*d57664e9SAndroid Build Coastguard Worker }
264*d57664e9SAndroid Build Coastguard Worker }
265*d57664e9SAndroid Build Coastguard Worker return 0;
266*d57664e9SAndroid Build Coastguard Worker }
267*d57664e9SAndroid Build Coastguard Worker
findGPUPercentile(protos::GraphicsStatsProto * proto,int percentile)268*d57664e9SAndroid Build Coastguard Worker static int32_t findGPUPercentile(protos::GraphicsStatsProto* proto, int percentile) {
269*d57664e9SAndroid Build Coastguard Worker uint32_t totalGPUFrameCount = 0; // this is usually proto->summary().total_frames() - 3.
270*d57664e9SAndroid Build Coastguard Worker for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) {
271*d57664e9SAndroid Build Coastguard Worker totalGPUFrameCount += it->frame_count();
272*d57664e9SAndroid Build Coastguard Worker }
273*d57664e9SAndroid Build Coastguard Worker int32_t pos = percentile * totalGPUFrameCount / 100;
274*d57664e9SAndroid Build Coastguard Worker int32_t remaining = totalGPUFrameCount - pos;
275*d57664e9SAndroid Build Coastguard Worker for (auto it = proto->gpu_histogram().rbegin(); it != proto->gpu_histogram().rend(); ++it) {
276*d57664e9SAndroid Build Coastguard Worker remaining -= it->frame_count();
277*d57664e9SAndroid Build Coastguard Worker if (remaining <= 0) {
278*d57664e9SAndroid Build Coastguard Worker return it->render_millis();
279*d57664e9SAndroid Build Coastguard Worker }
280*d57664e9SAndroid Build Coastguard Worker }
281*d57664e9SAndroid Build Coastguard Worker return 0;
282*d57664e9SAndroid Build Coastguard Worker }
283*d57664e9SAndroid Build Coastguard Worker
dumpAsTextToFd(protos::GraphicsStatsProto * proto,int fd)284*d57664e9SAndroid Build Coastguard Worker void dumpAsTextToFd(protos::GraphicsStatsProto* proto, int fd) {
285*d57664e9SAndroid Build Coastguard Worker // This isn't a full validation, just enough that we can deref at will
286*d57664e9SAndroid Build Coastguard Worker if (proto->package_name().empty() || !proto->has_summary()) {
287*d57664e9SAndroid Build Coastguard Worker ALOGW("Skipping dump, invalid package_name() '%s' or summary %d",
288*d57664e9SAndroid Build Coastguard Worker proto->package_name().c_str(), proto->has_summary());
289*d57664e9SAndroid Build Coastguard Worker return;
290*d57664e9SAndroid Build Coastguard Worker }
291*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nUID: %d", proto->uid());
292*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nPackage: %s", proto->package_name().c_str());
293*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nVersion: %" PRId64, proto->version_code());
294*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nStats since: %" PRId64 "ns", proto->stats_start());
295*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nStats end: %" PRId64 "ns", proto->stats_end());
296*d57664e9SAndroid Build Coastguard Worker auto summary = proto->summary();
297*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nTotal frames rendered: %d", summary.total_frames());
298*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nJanky frames: %d (%.2f%%)", summary.janky_frames(),
299*d57664e9SAndroid Build Coastguard Worker (float)summary.janky_frames() / (float)summary.total_frames() * 100.0f);
300*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n50th percentile: %dms", findPercentile(proto, 50));
301*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n90th percentile: %dms", findPercentile(proto, 90));
302*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n95th percentile: %dms", findPercentile(proto, 95));
303*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n99th percentile: %dms", findPercentile(proto, 99));
304*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nNumber Missed Vsync: %d", summary.missed_vsync_count());
305*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nNumber High input latency: %d", summary.high_input_latency_count());
306*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nNumber Slow UI thread: %d", summary.slow_ui_thread_count());
307*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nNumber Slow bitmap uploads: %d", summary.slow_bitmap_upload_count());
308*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nNumber Slow issue draw commands: %d", summary.slow_draw_count());
309*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nNumber Frame deadline missed: %d", summary.missed_deadline_count());
310*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nHISTOGRAM:");
311*d57664e9SAndroid Build Coastguard Worker for (const auto& it : proto->histogram()) {
312*d57664e9SAndroid Build Coastguard Worker dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
313*d57664e9SAndroid Build Coastguard Worker }
314*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n50th gpu percentile: %dms", findGPUPercentile(proto, 50));
315*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n90th gpu percentile: %dms", findGPUPercentile(proto, 90));
316*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n95th gpu percentile: %dms", findGPUPercentile(proto, 95));
317*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n99th gpu percentile: %dms", findGPUPercentile(proto, 99));
318*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\nGPU HISTOGRAM:");
319*d57664e9SAndroid Build Coastguard Worker for (const auto& it : proto->gpu_histogram()) {
320*d57664e9SAndroid Build Coastguard Worker dprintf(fd, " %dms=%d", it.render_millis(), it.frame_count());
321*d57664e9SAndroid Build Coastguard Worker }
322*d57664e9SAndroid Build Coastguard Worker dprintf(fd, "\n");
323*d57664e9SAndroid Build Coastguard Worker }
324*d57664e9SAndroid Build Coastguard Worker
saveBuffer(const std::string & path,uid_t uid,const std::string & package,int64_t versionCode,int64_t startTime,int64_t endTime,const ProfileData * data)325*d57664e9SAndroid Build Coastguard Worker void GraphicsStatsService::saveBuffer(const std::string& path, uid_t uid,
326*d57664e9SAndroid Build Coastguard Worker const std::string& package, int64_t versionCode,
327*d57664e9SAndroid Build Coastguard Worker int64_t startTime, int64_t endTime, const ProfileData* data) {
328*d57664e9SAndroid Build Coastguard Worker protos::GraphicsStatsProto statsProto;
329*d57664e9SAndroid Build Coastguard Worker if (!parseFromFile(path, &statsProto)) {
330*d57664e9SAndroid Build Coastguard Worker statsProto.Clear();
331*d57664e9SAndroid Build Coastguard Worker }
332*d57664e9SAndroid Build Coastguard Worker if (!mergeProfileDataIntoProto(&statsProto, uid, package, versionCode, startTime, endTime,
333*d57664e9SAndroid Build Coastguard Worker data)) {
334*d57664e9SAndroid Build Coastguard Worker return;
335*d57664e9SAndroid Build Coastguard Worker }
336*d57664e9SAndroid Build Coastguard Worker // Although we might not have read any data from the file, merging the existing data
337*d57664e9SAndroid Build Coastguard Worker // should always fully-initialize the proto
338*d57664e9SAndroid Build Coastguard Worker if (!statsProto.IsInitialized()) {
339*d57664e9SAndroid Build Coastguard Worker ALOGE("proto initialization error %s", statsProto.InitializationErrorString().c_str());
340*d57664e9SAndroid Build Coastguard Worker return;
341*d57664e9SAndroid Build Coastguard Worker }
342*d57664e9SAndroid Build Coastguard Worker if (statsProto.package_name().empty() || !statsProto.has_summary()) {
343*d57664e9SAndroid Build Coastguard Worker ALOGE("missing package_name() '%s' summary %d", statsProto.package_name().c_str(),
344*d57664e9SAndroid Build Coastguard Worker statsProto.has_summary());
345*d57664e9SAndroid Build Coastguard Worker return;
346*d57664e9SAndroid Build Coastguard Worker }
347*d57664e9SAndroid Build Coastguard Worker int outFd = open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0660);
348*d57664e9SAndroid Build Coastguard Worker if (outFd <= 0) {
349*d57664e9SAndroid Build Coastguard Worker int err = errno;
350*d57664e9SAndroid Build Coastguard Worker ALOGW("Failed to open '%s', error=%d (%s)", path.c_str(), err, strerror(err));
351*d57664e9SAndroid Build Coastguard Worker return;
352*d57664e9SAndroid Build Coastguard Worker }
353*d57664e9SAndroid Build Coastguard Worker int wrote = write(outFd, &sCurrentFileVersion, sHeaderSize);
354*d57664e9SAndroid Build Coastguard Worker if (wrote != sHeaderSize) {
355*d57664e9SAndroid Build Coastguard Worker int err = errno;
356*d57664e9SAndroid Build Coastguard Worker ALOGW("Failed to write header to '%s', returned=%d errno=%d (%s)", path.c_str(), wrote, err,
357*d57664e9SAndroid Build Coastguard Worker strerror(err));
358*d57664e9SAndroid Build Coastguard Worker close(outFd);
359*d57664e9SAndroid Build Coastguard Worker return;
360*d57664e9SAndroid Build Coastguard Worker }
361*d57664e9SAndroid Build Coastguard Worker {
362*d57664e9SAndroid Build Coastguard Worker FileOutputStreamLite output(outFd);
363*d57664e9SAndroid Build Coastguard Worker bool success = statsProto.SerializeToZeroCopyStream(&output) && output.Flush();
364*d57664e9SAndroid Build Coastguard Worker if (output.GetErrno() != 0) {
365*d57664e9SAndroid Build Coastguard Worker ALOGW("Error writing to fd=%d, path='%s' err=%d (%s)", outFd, path.c_str(),
366*d57664e9SAndroid Build Coastguard Worker output.GetErrno(), strerror(output.GetErrno()));
367*d57664e9SAndroid Build Coastguard Worker success = false;
368*d57664e9SAndroid Build Coastguard Worker } else if (!success) {
369*d57664e9SAndroid Build Coastguard Worker ALOGW("Serialize failed on '%s' unknown error", path.c_str());
370*d57664e9SAndroid Build Coastguard Worker }
371*d57664e9SAndroid Build Coastguard Worker }
372*d57664e9SAndroid Build Coastguard Worker close(outFd);
373*d57664e9SAndroid Build Coastguard Worker }
374*d57664e9SAndroid Build Coastguard Worker
375*d57664e9SAndroid Build Coastguard Worker class GraphicsStatsService::Dump {
376*d57664e9SAndroid Build Coastguard Worker public:
Dump(int outFd,DumpType type)377*d57664e9SAndroid Build Coastguard Worker Dump(int outFd, DumpType type) : mFd(outFd), mType(type) {
378*d57664e9SAndroid Build Coastguard Worker if (mFd == -1 && mType == DumpType::Protobuf) {
379*d57664e9SAndroid Build Coastguard Worker mType = DumpType::ProtobufStatsd;
380*d57664e9SAndroid Build Coastguard Worker }
381*d57664e9SAndroid Build Coastguard Worker }
fd()382*d57664e9SAndroid Build Coastguard Worker int fd() { return mFd; }
type()383*d57664e9SAndroid Build Coastguard Worker DumpType type() { return mType; }
proto()384*d57664e9SAndroid Build Coastguard Worker protos::GraphicsStatsServiceDumpProto& proto() { return mProto; }
385*d57664e9SAndroid Build Coastguard Worker void mergeStat(const protos::GraphicsStatsProto& stat);
386*d57664e9SAndroid Build Coastguard Worker void updateProto();
387*d57664e9SAndroid Build Coastguard Worker
388*d57664e9SAndroid Build Coastguard Worker private:
389*d57664e9SAndroid Build Coastguard Worker // use package name and app version for a key
390*d57664e9SAndroid Build Coastguard Worker typedef std::tuple<uid_t, std::string, int64_t> DumpKey;
391*d57664e9SAndroid Build Coastguard Worker
392*d57664e9SAndroid Build Coastguard Worker std::map<DumpKey, protos::GraphicsStatsProto> mStats;
393*d57664e9SAndroid Build Coastguard Worker int mFd;
394*d57664e9SAndroid Build Coastguard Worker DumpType mType;
395*d57664e9SAndroid Build Coastguard Worker protos::GraphicsStatsServiceDumpProto mProto;
396*d57664e9SAndroid Build Coastguard Worker };
397*d57664e9SAndroid Build Coastguard Worker
mergeStat(const protos::GraphicsStatsProto & stat)398*d57664e9SAndroid Build Coastguard Worker void GraphicsStatsService::Dump::mergeStat(const protos::GraphicsStatsProto& stat) {
399*d57664e9SAndroid Build Coastguard Worker auto dumpKey = std::make_tuple(static_cast<uid_t>(stat.uid()), stat.package_name(),
400*d57664e9SAndroid Build Coastguard Worker stat.version_code());
401*d57664e9SAndroid Build Coastguard Worker auto findIt = mStats.find(dumpKey);
402*d57664e9SAndroid Build Coastguard Worker if (findIt == mStats.end()) {
403*d57664e9SAndroid Build Coastguard Worker mStats[dumpKey] = stat;
404*d57664e9SAndroid Build Coastguard Worker } else {
405*d57664e9SAndroid Build Coastguard Worker auto summary = findIt->second.mutable_summary();
406*d57664e9SAndroid Build Coastguard Worker summary->set_total_frames(summary->total_frames() + stat.summary().total_frames());
407*d57664e9SAndroid Build Coastguard Worker summary->set_janky_frames(summary->janky_frames() + stat.summary().janky_frames());
408*d57664e9SAndroid Build Coastguard Worker summary->set_missed_vsync_count(summary->missed_vsync_count() +
409*d57664e9SAndroid Build Coastguard Worker stat.summary().missed_vsync_count());
410*d57664e9SAndroid Build Coastguard Worker summary->set_high_input_latency_count(summary->high_input_latency_count() +
411*d57664e9SAndroid Build Coastguard Worker stat.summary().high_input_latency_count());
412*d57664e9SAndroid Build Coastguard Worker summary->set_slow_ui_thread_count(summary->slow_ui_thread_count() +
413*d57664e9SAndroid Build Coastguard Worker stat.summary().slow_ui_thread_count());
414*d57664e9SAndroid Build Coastguard Worker summary->set_slow_bitmap_upload_count(summary->slow_bitmap_upload_count() +
415*d57664e9SAndroid Build Coastguard Worker stat.summary().slow_bitmap_upload_count());
416*d57664e9SAndroid Build Coastguard Worker summary->set_slow_draw_count(summary->slow_draw_count() + stat.summary().slow_draw_count());
417*d57664e9SAndroid Build Coastguard Worker summary->set_missed_deadline_count(summary->missed_deadline_count() +
418*d57664e9SAndroid Build Coastguard Worker stat.summary().missed_deadline_count());
419*d57664e9SAndroid Build Coastguard Worker for (int bucketIndex = 0; bucketIndex < findIt->second.histogram_size(); bucketIndex++) {
420*d57664e9SAndroid Build Coastguard Worker auto bucket = findIt->second.mutable_histogram(bucketIndex);
421*d57664e9SAndroid Build Coastguard Worker bucket->set_frame_count(bucket->frame_count() +
422*d57664e9SAndroid Build Coastguard Worker stat.histogram(bucketIndex).frame_count());
423*d57664e9SAndroid Build Coastguard Worker }
424*d57664e9SAndroid Build Coastguard Worker for (int bucketIndex = 0; bucketIndex < findIt->second.gpu_histogram_size();
425*d57664e9SAndroid Build Coastguard Worker bucketIndex++) {
426*d57664e9SAndroid Build Coastguard Worker auto bucket = findIt->second.mutable_gpu_histogram(bucketIndex);
427*d57664e9SAndroid Build Coastguard Worker bucket->set_frame_count(bucket->frame_count() +
428*d57664e9SAndroid Build Coastguard Worker stat.gpu_histogram(bucketIndex).frame_count());
429*d57664e9SAndroid Build Coastguard Worker }
430*d57664e9SAndroid Build Coastguard Worker findIt->second.set_stats_start(std::min(findIt->second.stats_start(), stat.stats_start()));
431*d57664e9SAndroid Build Coastguard Worker findIt->second.set_stats_end(std::max(findIt->second.stats_end(), stat.stats_end()));
432*d57664e9SAndroid Build Coastguard Worker }
433*d57664e9SAndroid Build Coastguard Worker }
434*d57664e9SAndroid Build Coastguard Worker
updateProto()435*d57664e9SAndroid Build Coastguard Worker void GraphicsStatsService::Dump::updateProto() {
436*d57664e9SAndroid Build Coastguard Worker for (auto& stat : mStats) {
437*d57664e9SAndroid Build Coastguard Worker mProto.add_stats()->CopyFrom(stat.second);
438*d57664e9SAndroid Build Coastguard Worker }
439*d57664e9SAndroid Build Coastguard Worker }
440*d57664e9SAndroid Build Coastguard Worker
createDump(int outFd,DumpType type)441*d57664e9SAndroid Build Coastguard Worker GraphicsStatsService::Dump* GraphicsStatsService::createDump(int outFd, DumpType type) {
442*d57664e9SAndroid Build Coastguard Worker return new Dump(outFd, type);
443*d57664e9SAndroid Build Coastguard Worker }
444*d57664e9SAndroid Build Coastguard Worker
addToDump(Dump * dump,const std::string & path,uid_t uid,const std::string & package,int64_t versionCode,int64_t startTime,int64_t endTime,const ProfileData * data)445*d57664e9SAndroid Build Coastguard Worker void GraphicsStatsService::addToDump(Dump* dump, const std::string& path, uid_t uid,
446*d57664e9SAndroid Build Coastguard Worker const std::string& package, int64_t versionCode,
447*d57664e9SAndroid Build Coastguard Worker int64_t startTime, int64_t endTime, const ProfileData* data) {
448*d57664e9SAndroid Build Coastguard Worker protos::GraphicsStatsProto statsProto;
449*d57664e9SAndroid Build Coastguard Worker if (!path.empty() && !parseFromFile(path, &statsProto)) {
450*d57664e9SAndroid Build Coastguard Worker statsProto.Clear();
451*d57664e9SAndroid Build Coastguard Worker }
452*d57664e9SAndroid Build Coastguard Worker if (data && !mergeProfileDataIntoProto(&statsProto, uid, package, versionCode, startTime,
453*d57664e9SAndroid Build Coastguard Worker endTime, data)) {
454*d57664e9SAndroid Build Coastguard Worker return;
455*d57664e9SAndroid Build Coastguard Worker }
456*d57664e9SAndroid Build Coastguard Worker if (!statsProto.IsInitialized()) {
457*d57664e9SAndroid Build Coastguard Worker ALOGW("Failed to load profile data from path '%s' and data %p",
458*d57664e9SAndroid Build Coastguard Worker path.empty() ? "<empty>" : path.c_str(), data);
459*d57664e9SAndroid Build Coastguard Worker return;
460*d57664e9SAndroid Build Coastguard Worker }
461*d57664e9SAndroid Build Coastguard Worker if (dump->type() == DumpType::ProtobufStatsd) {
462*d57664e9SAndroid Build Coastguard Worker dump->mergeStat(statsProto);
463*d57664e9SAndroid Build Coastguard Worker } else if (dump->type() == DumpType::Protobuf) {
464*d57664e9SAndroid Build Coastguard Worker dump->proto().add_stats()->CopyFrom(statsProto);
465*d57664e9SAndroid Build Coastguard Worker } else {
466*d57664e9SAndroid Build Coastguard Worker dumpAsTextToFd(&statsProto, dump->fd());
467*d57664e9SAndroid Build Coastguard Worker }
468*d57664e9SAndroid Build Coastguard Worker }
469*d57664e9SAndroid Build Coastguard Worker
addToDump(Dump * dump,const std::string & path)470*d57664e9SAndroid Build Coastguard Worker void GraphicsStatsService::addToDump(Dump* dump, const std::string& path) {
471*d57664e9SAndroid Build Coastguard Worker protos::GraphicsStatsProto statsProto;
472*d57664e9SAndroid Build Coastguard Worker if (!parseFromFile(path, &statsProto)) {
473*d57664e9SAndroid Build Coastguard Worker return;
474*d57664e9SAndroid Build Coastguard Worker }
475*d57664e9SAndroid Build Coastguard Worker if (dump->type() == DumpType::ProtobufStatsd) {
476*d57664e9SAndroid Build Coastguard Worker dump->mergeStat(statsProto);
477*d57664e9SAndroid Build Coastguard Worker } else if (dump->type() == DumpType::Protobuf) {
478*d57664e9SAndroid Build Coastguard Worker dump->proto().add_stats()->CopyFrom(statsProto);
479*d57664e9SAndroid Build Coastguard Worker } else {
480*d57664e9SAndroid Build Coastguard Worker dumpAsTextToFd(&statsProto, dump->fd());
481*d57664e9SAndroid Build Coastguard Worker }
482*d57664e9SAndroid Build Coastguard Worker }
483*d57664e9SAndroid Build Coastguard Worker
finishDump(Dump * dump)484*d57664e9SAndroid Build Coastguard Worker void GraphicsStatsService::finishDump(Dump* dump) {
485*d57664e9SAndroid Build Coastguard Worker if (dump->type() == DumpType::Protobuf) {
486*d57664e9SAndroid Build Coastguard Worker FileOutputStreamLite stream(dump->fd());
487*d57664e9SAndroid Build Coastguard Worker dump->proto().SerializeToZeroCopyStream(&stream);
488*d57664e9SAndroid Build Coastguard Worker }
489*d57664e9SAndroid Build Coastguard Worker delete dump;
490*d57664e9SAndroid Build Coastguard Worker }
491*d57664e9SAndroid Build Coastguard Worker
492*d57664e9SAndroid Build Coastguard Worker using namespace google::protobuf;
493*d57664e9SAndroid Build Coastguard Worker
494*d57664e9SAndroid Build Coastguard Worker // Field ids taken from FrameTimingHistogram message in atoms.proto
495*d57664e9SAndroid Build Coastguard Worker #define TIME_MILLIS_BUCKETS_FIELD_NUMBER 1
496*d57664e9SAndroid Build Coastguard Worker #define FRAME_COUNTS_FIELD_NUMBER 2
497*d57664e9SAndroid Build Coastguard Worker
writeCpuHistogram(AStatsEvent * event,const uirenderer::protos::GraphicsStatsProto & stat)498*d57664e9SAndroid Build Coastguard Worker static void writeCpuHistogram(AStatsEvent* event,
499*d57664e9SAndroid Build Coastguard Worker const uirenderer::protos::GraphicsStatsProto& stat) {
500*d57664e9SAndroid Build Coastguard Worker util::ProtoOutputStream proto;
501*d57664e9SAndroid Build Coastguard Worker for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
502*d57664e9SAndroid Build Coastguard Worker auto& bucket = stat.histogram(bucketIndex);
503*d57664e9SAndroid Build Coastguard Worker proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
504*d57664e9SAndroid Build Coastguard Worker TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
505*d57664e9SAndroid Build Coastguard Worker (int)bucket.render_millis());
506*d57664e9SAndroid Build Coastguard Worker }
507*d57664e9SAndroid Build Coastguard Worker for (int bucketIndex = 0; bucketIndex < stat.histogram_size(); bucketIndex++) {
508*d57664e9SAndroid Build Coastguard Worker auto& bucket = stat.histogram(bucketIndex);
509*d57664e9SAndroid Build Coastguard Worker proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
510*d57664e9SAndroid Build Coastguard Worker FRAME_COUNTS_FIELD_NUMBER /* field id */,
511*d57664e9SAndroid Build Coastguard Worker (long long)bucket.frame_count());
512*d57664e9SAndroid Build Coastguard Worker }
513*d57664e9SAndroid Build Coastguard Worker std::vector<uint8_t> outVector;
514*d57664e9SAndroid Build Coastguard Worker proto.serializeToVector(&outVector);
515*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
516*d57664e9SAndroid Build Coastguard Worker }
517*d57664e9SAndroid Build Coastguard Worker
writeGpuHistogram(AStatsEvent * event,const uirenderer::protos::GraphicsStatsProto & stat)518*d57664e9SAndroid Build Coastguard Worker static void writeGpuHistogram(AStatsEvent* event,
519*d57664e9SAndroid Build Coastguard Worker const uirenderer::protos::GraphicsStatsProto& stat) {
520*d57664e9SAndroid Build Coastguard Worker util::ProtoOutputStream proto;
521*d57664e9SAndroid Build Coastguard Worker for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
522*d57664e9SAndroid Build Coastguard Worker auto& bucket = stat.gpu_histogram(bucketIndex);
523*d57664e9SAndroid Build Coastguard Worker proto.write(android::util::FIELD_TYPE_INT32 | android::util::FIELD_COUNT_REPEATED |
524*d57664e9SAndroid Build Coastguard Worker TIME_MILLIS_BUCKETS_FIELD_NUMBER /* field id */,
525*d57664e9SAndroid Build Coastguard Worker (int)bucket.render_millis());
526*d57664e9SAndroid Build Coastguard Worker }
527*d57664e9SAndroid Build Coastguard Worker for (int bucketIndex = 0; bucketIndex < stat.gpu_histogram_size(); bucketIndex++) {
528*d57664e9SAndroid Build Coastguard Worker auto& bucket = stat.gpu_histogram(bucketIndex);
529*d57664e9SAndroid Build Coastguard Worker proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
530*d57664e9SAndroid Build Coastguard Worker FRAME_COUNTS_FIELD_NUMBER /* field id */,
531*d57664e9SAndroid Build Coastguard Worker (long long)bucket.frame_count());
532*d57664e9SAndroid Build Coastguard Worker }
533*d57664e9SAndroid Build Coastguard Worker std::vector<uint8_t> outVector;
534*d57664e9SAndroid Build Coastguard Worker proto.serializeToVector(&outVector);
535*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeByteArray(event, outVector.data(), outVector.size());
536*d57664e9SAndroid Build Coastguard Worker }
537*d57664e9SAndroid Build Coastguard Worker
538*d57664e9SAndroid Build Coastguard Worker
finishDumpInMemory(Dump * dump,AStatsEventList * data,bool lastFullDay)539*d57664e9SAndroid Build Coastguard Worker void GraphicsStatsService::finishDumpInMemory(Dump* dump, AStatsEventList* data,
540*d57664e9SAndroid Build Coastguard Worker bool lastFullDay) {
541*d57664e9SAndroid Build Coastguard Worker dump->updateProto();
542*d57664e9SAndroid Build Coastguard Worker auto& serviceDump = dump->proto();
543*d57664e9SAndroid Build Coastguard Worker for (int stat_index = 0; stat_index < serviceDump.stats_size(); stat_index++) {
544*d57664e9SAndroid Build Coastguard Worker auto& stat = serviceDump.stats(stat_index);
545*d57664e9SAndroid Build Coastguard Worker AStatsEvent* event = AStatsEventList_addStatsEvent(data);
546*d57664e9SAndroid Build Coastguard Worker AStatsEvent_setAtomId(event, stats::GRAPHICS_STATS);
547*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeString(event, stat.package_name().c_str());
548*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt64(event, (int64_t)stat.version_code());
549*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt64(event, (int64_t)stat.stats_start());
550*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt64(event, (int64_t)stat.stats_end());
551*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, (int32_t)stat.pipeline());
552*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, (int32_t)stat.summary().total_frames());
553*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_vsync_count());
554*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, (int32_t)stat.summary().high_input_latency_count());
555*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_ui_thread_count());
556*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_bitmap_upload_count());
557*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, (int32_t)stat.summary().slow_draw_count());
558*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, (int32_t)stat.summary().missed_deadline_count());
559*d57664e9SAndroid Build Coastguard Worker writeCpuHistogram(event, stat);
560*d57664e9SAndroid Build Coastguard Worker writeGpuHistogram(event, stat);
561*d57664e9SAndroid Build Coastguard Worker // TODO: fill in UI mainline module version, when the feature is available.
562*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt64(event, (int64_t)0);
563*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeBool(event, !lastFullDay);
564*d57664e9SAndroid Build Coastguard Worker AStatsEvent_writeInt32(event, stat.uid());
565*d57664e9SAndroid Build Coastguard Worker AStatsEvent_addBoolAnnotation(event, ASTATSLOG_ANNOTATION_ID_IS_UID, true);
566*d57664e9SAndroid Build Coastguard Worker AStatsEvent_build(event);
567*d57664e9SAndroid Build Coastguard Worker }
568*d57664e9SAndroid Build Coastguard Worker delete dump;
569*d57664e9SAndroid Build Coastguard Worker }
570*d57664e9SAndroid Build Coastguard Worker
571*d57664e9SAndroid Build Coastguard Worker
572*d57664e9SAndroid Build Coastguard Worker } /* namespace uirenderer */
573*d57664e9SAndroid Build Coastguard Worker } /* namespace android */
574