1 /*
2  * Copyright 2019 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 #undef LOG_TAG
17 #define LOG_TAG "GpuStats"
18 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
19 
20 #include "gpustats/GpuStats.h"
21 
22 #include <android/util/ProtoOutputStream.h>
23 #include <cutils/properties.h>
24 #include <log/log.h>
25 #include <stats_event.h>
26 #include <statslog.h>
27 #include <utils/Trace.h>
28 
29 #include <unordered_set>
30 
31 namespace android {
32 
~GpuStats()33 GpuStats::~GpuStats() {
34     if (mStatsdRegistered) {
35         AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO);
36         AStatsManager_clearPullAtomCallback(android::util::GPU_STATS_APP_INFO);
37     }
38 }
39 
addLoadingCount(GpuStatsInfo::Driver driver,bool isDriverLoaded,GpuStatsGlobalInfo * const outGlobalInfo)40 static void addLoadingCount(GpuStatsInfo::Driver driver, bool isDriverLoaded,
41                             GpuStatsGlobalInfo* const outGlobalInfo) {
42     switch (driver) {
43         case GpuStatsInfo::Driver::GL:
44         case GpuStatsInfo::Driver::GL_UPDATED:
45             outGlobalInfo->glLoadingCount++;
46             if (!isDriverLoaded) outGlobalInfo->glLoadingFailureCount++;
47             break;
48         case GpuStatsInfo::Driver::VULKAN:
49         case GpuStatsInfo::Driver::VULKAN_UPDATED:
50             outGlobalInfo->vkLoadingCount++;
51             if (!isDriverLoaded) outGlobalInfo->vkLoadingFailureCount++;
52             break;
53         case GpuStatsInfo::Driver::ANGLE:
54             outGlobalInfo->angleLoadingCount++;
55             if (!isDriverLoaded) outGlobalInfo->angleLoadingFailureCount++;
56             break;
57         default:
58             break;
59     }
60 }
61 
addLoadingTime(GpuStatsInfo::Driver driver,int64_t driverLoadingTime,GpuStatsAppInfo * const outAppInfo)62 static void addLoadingTime(GpuStatsInfo::Driver driver, int64_t driverLoadingTime,
63                            GpuStatsAppInfo* const outAppInfo) {
64     switch (driver) {
65         case GpuStatsInfo::Driver::GL:
66         case GpuStatsInfo::Driver::GL_UPDATED:
67             if (outAppInfo->glDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
68                 outAppInfo->glDriverLoadingTime.emplace_back(driverLoadingTime);
69             }
70             break;
71         case GpuStatsInfo::Driver::VULKAN:
72         case GpuStatsInfo::Driver::VULKAN_UPDATED:
73             if (outAppInfo->vkDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
74                 outAppInfo->vkDriverLoadingTime.emplace_back(driverLoadingTime);
75             }
76             break;
77         case GpuStatsInfo::Driver::ANGLE:
78             if (outAppInfo->angleDriverLoadingTime.size() < GpuStats::MAX_NUM_LOADING_TIMES) {
79                 outAppInfo->angleDriverLoadingTime.emplace_back(driverLoadingTime);
80             }
81             break;
82         default:
83             break;
84     }
85 }
86 
purgeOldDriverStats()87 void GpuStats::purgeOldDriverStats() {
88     ALOG_ASSERT(mAppStats.size() == MAX_NUM_APP_RECORDS);
89 
90     struct GpuStatsApp {
91         // Key is <app package name>+<driver version code>.
92         const std::string *appStatsKey = nullptr;
93         const std::chrono::time_point<std::chrono::system_clock> *lastAccessTime = nullptr;
94     };
95     std::vector<GpuStatsApp> gpuStatsApps(MAX_NUM_APP_RECORDS);
96 
97     // Create a list of pointers to package names and their last access times.
98     int index = 0;
99     for (const auto & [appStatsKey, gpuStatsAppInfo] : mAppStats) {
100         GpuStatsApp &gpuStatsApp = gpuStatsApps[index];
101         gpuStatsApp.appStatsKey = &appStatsKey;
102         gpuStatsApp.lastAccessTime = &gpuStatsAppInfo.lastAccessTime;
103         ++index;
104     }
105 
106     // Sort the list with the oldest access times at the front.
107     std::sort(gpuStatsApps.begin(), gpuStatsApps.end(), [](GpuStatsApp a, GpuStatsApp b) -> bool {
108         return *a.lastAccessTime < *b.lastAccessTime;
109     });
110 
111     // Remove the oldest packages from mAppStats to make room for new apps.
112     for (int i = 0; i < APP_RECORD_HEADROOM; ++i) {
113         mAppStats.erase(*gpuStatsApps[i].appStatsKey);
114         gpuStatsApps[i].appStatsKey = nullptr;
115         gpuStatsApps[i].lastAccessTime = nullptr;
116     }
117 }
118 
insertDriverStats(const std::string & driverPackageName,const std::string & driverVersionName,uint64_t driverVersionCode,int64_t driverBuildTime,const std::string & appPackageName,const int32_t vulkanVersion,GpuStatsInfo::Driver driver,bool isDriverLoaded,int64_t driverLoadingTime)119 void GpuStats::insertDriverStats(const std::string& driverPackageName,
120                                  const std::string& driverVersionName, uint64_t driverVersionCode,
121                                  int64_t driverBuildTime, const std::string& appPackageName,
122                                  const int32_t vulkanVersion, GpuStatsInfo::Driver driver,
123                                  bool isDriverLoaded, int64_t driverLoadingTime) {
124     ATRACE_CALL();
125 
126     std::lock_guard<std::mutex> lock(mLock);
127     registerStatsdCallbacksIfNeeded();
128     ALOGV("Received:\n"
129           "\tdriverPackageName[%s]\n"
130           "\tdriverVersionName[%s]\n"
131           "\tdriverVersionCode[%" PRIu64 "]\n"
132           "\tdriverBuildTime[%" PRId64 "]\n"
133           "\tappPackageName[%s]\n"
134           "\tvulkanVersion[%d]\n"
135           "\tdriver[%d]\n"
136           "\tisDriverLoaded[%d]\n"
137           "\tdriverLoadingTime[%" PRId64 "]",
138           driverPackageName.c_str(), driverVersionName.c_str(), driverVersionCode, driverBuildTime,
139           appPackageName.c_str(), vulkanVersion, static_cast<int32_t>(driver), isDriverLoaded,
140           driverLoadingTime);
141 
142     if (!mGlobalStats.count(driverVersionCode)) {
143         GpuStatsGlobalInfo globalInfo;
144         addLoadingCount(driver, isDriverLoaded, &globalInfo);
145         globalInfo.driverPackageName = driverPackageName;
146         globalInfo.driverVersionName = driverVersionName;
147         globalInfo.driverVersionCode = driverVersionCode;
148         globalInfo.driverBuildTime = driverBuildTime;
149         globalInfo.vulkanVersion = vulkanVersion;
150         mGlobalStats.insert({driverVersionCode, globalInfo});
151     } else {
152         addLoadingCount(driver, isDriverLoaded, &mGlobalStats[driverVersionCode]);
153     }
154 
155     const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
156     if (!mAppStats.count(appStatsKey)) {
157         if (mAppStats.size() >= MAX_NUM_APP_RECORDS) {
158             ALOGV("GpuStatsAppInfo has reached maximum size. Removing old stats to make room.");
159             purgeOldDriverStats();
160         }
161 
162         GpuStatsAppInfo appInfo;
163         addLoadingTime(driver, driverLoadingTime, &appInfo);
164         appInfo.appPackageName = appPackageName;
165         appInfo.driverVersionCode = driverVersionCode;
166         appInfo.angleInUse =
167                 driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle";
168         appInfo.lastAccessTime = std::chrono::system_clock::now();
169         mAppStats.insert({appStatsKey, appInfo});
170     } else {
171         mAppStats[appStatsKey].angleInUse =
172                 driver == GpuStatsInfo::Driver::ANGLE || driverPackageName == "angle";
173         addLoadingTime(driver, driverLoadingTime, &mAppStats[appStatsKey]);
174         mAppStats[appStatsKey].lastAccessTime = std::chrono::system_clock::now();
175     }
176 }
177 
insertTargetStats(const std::string & appPackageName,const uint64_t driverVersionCode,const GpuStatsInfo::Stats stats,const uint64_t value)178 void GpuStats::insertTargetStats(const std::string& appPackageName,
179                                  const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
180                                  const uint64_t value) {
181     return insertTargetStatsArray(appPackageName, driverVersionCode, stats, &value, 1);
182 }
183 
addVulkanEngineName(const std::string & appPackageName,const uint64_t driverVersionCode,const char * engineNameCStr)184 void GpuStats::addVulkanEngineName(const std::string& appPackageName,
185                                    const uint64_t driverVersionCode,
186                                    const char* engineNameCStr) {
187     ATRACE_CALL();
188 
189     const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
190     const size_t engineNameLen = std::min(strlen(engineNameCStr),
191                                           GpuStatsAppInfo::MAX_VULKAN_ENGINE_NAME_LENGTH);
192     const std::string engineName{engineNameCStr, engineNameLen};
193 
194     std::lock_guard<std::mutex> lock(mLock);
195     registerStatsdCallbacksIfNeeded();
196 
197     const auto foundApp = mAppStats.find(appStatsKey);
198     if (foundApp == mAppStats.end()) {
199         return;
200     }
201 
202     // Storing in std::set<> is not efficient for serialization tasks. Use
203     // vector instead and filter out dups
204     std::vector<std::string>& engineNames = foundApp->second.vulkanEngineNames;
205     if (engineNames.size() < GpuStatsAppInfo::MAX_VULKAN_ENGINE_NAMES
206         && std::find(engineNames.cbegin(), engineNames.cend(), engineName) == engineNames.cend()) {
207         engineNames.push_back(engineName);
208     }
209 }
210 
insertTargetStatsArray(const std::string & appPackageName,const uint64_t driverVersionCode,const GpuStatsInfo::Stats stats,const uint64_t * values,const uint32_t valueCount)211 void GpuStats::insertTargetStatsArray(const std::string& appPackageName,
212                                  const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats,
213                                  const uint64_t* values, const uint32_t valueCount) {
214     ATRACE_CALL();
215 
216     const std::string appStatsKey = appPackageName + std::to_string(driverVersionCode);
217 
218     std::lock_guard<std::mutex> lock(mLock);
219     registerStatsdCallbacksIfNeeded();
220 
221     const auto foundApp = mAppStats.find(appStatsKey);
222     if (foundApp == mAppStats.end()) {
223         return;
224     }
225 
226     GpuStatsAppInfo& targetAppStats = foundApp->second;
227 
228     if (stats == GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION
229         || stats == GpuStatsInfo::Stats::VULKAN_DEVICE_EXTENSION) {
230         // Handle extension arrays separately as we need to store a unique set of them
231         // in the stats vector. Storing in std::set<> is not efficient for serialization tasks.
232         std::vector<int32_t>& targetVec =
233                                 (stats == GpuStatsInfo::Stats::VULKAN_INSTANCE_EXTENSION) ?
234                                 targetAppStats.vulkanInstanceExtensions :
235                                 targetAppStats.vulkanDeviceExtensions;
236         const bool addAll = (targetVec.size() == 0);
237         targetVec.reserve(valueCount);
238 
239         // Add new extensions into the set
240         for(uint32_t i = 0;
241             (i < valueCount) && (targetVec.size() < GpuStatsAppInfo::MAX_NUM_EXTENSIONS);
242             i++) {
243             const int32_t extVal = int32_t(values[i] & 0xFFFFFFFF);
244             if (addAll
245                 || std::find(targetVec.cbegin(), targetVec.cend(), extVal) == targetVec.cend()) {
246                 targetVec.push_back(extVal);
247             }
248         }
249     }
250     else {
251         // Handle other type of stats info events
252         for(uint32_t i = 0; i < valueCount; i++) {
253             const uint64_t value = values[i];
254             switch (stats) {
255                 case GpuStatsInfo::Stats::CPU_VULKAN_IN_USE:
256                     targetAppStats.cpuVulkanInUse = true;
257                     break;
258                 case GpuStatsInfo::Stats::FALSE_PREROTATION:
259                     targetAppStats.falsePrerotation = true;
260                     break;
261                 case GpuStatsInfo::Stats::GLES_1_IN_USE:
262                     targetAppStats.gles1InUse = true;
263                     break;
264                 case GpuStatsInfo::Stats::CREATED_GLES_CONTEXT:
265                     targetAppStats.createdGlesContext = true;
266                     break;
267                 case GpuStatsInfo::Stats::CREATED_VULKAN_DEVICE:
268                     targetAppStats.createdVulkanDevice = true;
269                     break;
270                 case GpuStatsInfo::Stats::CREATED_VULKAN_API_VERSION:
271                     targetAppStats.vulkanApiVersion = uint32_t(value & 0xffffffff);
272                     break;
273                 case GpuStatsInfo::Stats::CREATED_VULKAN_SWAPCHAIN:
274                     targetAppStats.createdVulkanSwapchain = true;
275                     break;
276                 case GpuStatsInfo::Stats::VULKAN_DEVICE_FEATURES_ENABLED:
277                     // Merge all requested feature bits together for this app
278                     targetAppStats.vulkanDeviceFeaturesEnabled |= value;
279                     break;
280                 default:
281                     break;
282             }
283         }
284     }
285 }
286 
interceptSystemDriverStatsLocked()287 void GpuStats::interceptSystemDriverStatsLocked() {
288     // Append cpuVulkanVersion and glesVersion to system driver stats
289     if (!mGlobalStats.count(0) || mGlobalStats[0].glesVersion) {
290         return;
291     }
292 
293     mGlobalStats[0].cpuVulkanVersion = property_get_int32("ro.cpuvulkan.version", 0);
294     mGlobalStats[0].glesVersion = property_get_int32("ro.opengles.version", 0);
295 }
296 
registerStatsdCallbacksIfNeeded()297 void GpuStats::registerStatsdCallbacksIfNeeded() {
298     if (!mStatsdRegistered) {
299         AStatsManager_setPullAtomCallback(android::util::GPU_STATS_GLOBAL_INFO, nullptr,
300                                          GpuStats::pullAtomCallback, this);
301         AStatsManager_setPullAtomCallback(android::util::GPU_STATS_APP_INFO, nullptr,
302                                          GpuStats::pullAtomCallback, this);
303         mStatsdRegistered = true;
304     }
305 }
306 
dump(const Vector<String16> & args,std::string * result)307 void GpuStats::dump(const Vector<String16>& args, std::string* result) {
308     ATRACE_CALL();
309 
310     if (!result) {
311         ALOGE("Dump result shouldn't be nullptr.");
312         return;
313     }
314 
315     std::lock_guard<std::mutex> lock(mLock);
316     bool dumpAll = true;
317 
318     std::unordered_set<std::string> argsSet;
319     for (size_t i = 0; i < args.size(); i++) {
320         argsSet.insert(String8(args[i]).c_str());
321     }
322 
323     const bool dumpGlobal = argsSet.count("--global") != 0;
324     if (dumpGlobal) {
325         dumpGlobalLocked(result);
326         dumpAll = false;
327     }
328 
329     const bool dumpApp = argsSet.count("--app") != 0;
330     if (dumpApp) {
331         dumpAppLocked(result);
332         dumpAll = false;
333     }
334 
335     if (dumpAll) {
336         dumpGlobalLocked(result);
337         dumpAppLocked(result);
338     }
339 
340     if (argsSet.count("--clear")) {
341         bool clearAll = true;
342 
343         if (dumpGlobal) {
344             mGlobalStats.clear();
345             clearAll = false;
346         }
347 
348         if (dumpApp) {
349             mAppStats.clear();
350             clearAll = false;
351         }
352 
353         if (clearAll) {
354             mGlobalStats.clear();
355             mAppStats.clear();
356         }
357     }
358 }
359 
dumpGlobalLocked(std::string * result)360 void GpuStats::dumpGlobalLocked(std::string* result) {
361     interceptSystemDriverStatsLocked();
362 
363     for (const auto& ele : mGlobalStats) {
364         result->append(ele.second.toString());
365         result->append("\n");
366     }
367 }
368 
dumpAppLocked(std::string * result)369 void GpuStats::dumpAppLocked(std::string* result) {
370     for (const auto& ele : mAppStats) {
371         result->append(ele.second.toString());
372         result->append("\n");
373     }
374 }
375 
protoOutputStreamToByteString(android::util::ProtoOutputStream & proto)376 static std::string protoOutputStreamToByteString(android::util::ProtoOutputStream& proto) {
377     if (!proto.size()) return "";
378 
379     std::string byteString;
380     sp<android::util::ProtoReader> reader = proto.data();
381     while (reader->readBuffer() != nullptr) {
382         const size_t toRead = reader->currentToRead();
383         byteString.append((char*)reader->readBuffer(), toRead);
384         reader->move(toRead);
385     }
386 
387     if (byteString.size() != proto.size()) return "";
388 
389     return byteString;
390 }
391 
int64VectorToProtoByteString(const std::vector<int64_t> & value)392 static std::string int64VectorToProtoByteString(const std::vector<int64_t>& value) {
393     if (value.empty()) return "";
394 
395     android::util::ProtoOutputStream proto;
396     for (const auto& ele : value) {
397         proto.write(android::util::FIELD_TYPE_INT64 | android::util::FIELD_COUNT_REPEATED |
398                             1 /* field id */,
399                     (long long)ele);
400     }
401 
402     return protoOutputStreamToByteString(proto);
403 }
404 
pullAppInfoAtom(AStatsEventList * data)405 AStatsManager_PullAtomCallbackReturn GpuStats::pullAppInfoAtom(AStatsEventList* data) {
406     ATRACE_CALL();
407 
408     std::lock_guard<std::mutex> lock(mLock);
409 
410     if (data) {
411         for (const auto& ele : mAppStats) {
412             std::string glDriverBytes = int64VectorToProtoByteString(
413                 ele.second.glDriverLoadingTime);
414             std::string vkDriverBytes = int64VectorToProtoByteString(
415                 ele.second.vkDriverLoadingTime);
416             std::string angleDriverBytes = int64VectorToProtoByteString(
417                 ele.second.angleDriverLoadingTime);
418 
419             std::vector<const char*> engineNames;
420             for (const std::string &engineName : ele.second.vulkanEngineNames) {
421                 engineNames.push_back(engineName.c_str());
422             }
423 
424             android::util::addAStatsEvent(
425                     data,
426                     android::util::GPU_STATS_APP_INFO,
427                     ele.second.appPackageName.c_str(),
428                     ele.second.driverVersionCode,
429                     android::util::BytesField(glDriverBytes.c_str(),
430                                               glDriverBytes.length()),
431                     android::util::BytesField(vkDriverBytes.c_str(),
432                                               vkDriverBytes.length()),
433                     android::util::BytesField(angleDriverBytes.c_str(),
434                                               angleDriverBytes.length()),
435                     ele.second.cpuVulkanInUse,
436                     ele.second.falsePrerotation,
437                     ele.second.gles1InUse,
438                     ele.second.angleInUse,
439                     ele.second.createdGlesContext,
440                     ele.second.createdVulkanDevice,
441                     ele.second.createdVulkanSwapchain,
442                     ele.second.vulkanApiVersion,
443                     ele.second.vulkanDeviceFeaturesEnabled,
444                     ele.second.vulkanInstanceExtensions,
445                     ele.second.vulkanDeviceExtensions,
446                     engineNames);
447         }
448     }
449 
450     mAppStats.clear();
451 
452     return AStatsManager_PULL_SUCCESS;
453 }
454 
pullGlobalInfoAtom(AStatsEventList * data)455 AStatsManager_PullAtomCallbackReturn GpuStats::pullGlobalInfoAtom(AStatsEventList* data) {
456     ATRACE_CALL();
457 
458     std::lock_guard<std::mutex> lock(mLock);
459     // flush cpuVulkanVersion and glesVersion to builtin driver stats
460     interceptSystemDriverStatsLocked();
461 
462     if (data) {
463         for (const auto& ele : mGlobalStats) {
464           android::util::addAStatsEvent(
465                   data,
466                   android::util::GPU_STATS_GLOBAL_INFO,
467                   ele.second.driverPackageName.c_str(),
468                   ele.second.driverVersionName.c_str(),
469                   ele.second.driverVersionCode,
470                   ele.second.driverBuildTime,
471                   ele.second.glLoadingCount,
472                   ele.second.glLoadingFailureCount,
473                   ele.second.vkLoadingCount,
474                   ele.second.vkLoadingFailureCount,
475                   ele.second.vulkanVersion,
476                   ele.second.cpuVulkanVersion,
477                   ele.second.glesVersion,
478                   ele.second.angleLoadingCount,
479                   ele.second.angleLoadingFailureCount);
480         }
481     }
482 
483     mGlobalStats.clear();
484 
485     return AStatsManager_PULL_SUCCESS;
486 }
487 
pullAtomCallback(int32_t atomTag,AStatsEventList * data,void * cookie)488 AStatsManager_PullAtomCallbackReturn GpuStats::pullAtomCallback(int32_t atomTag,
489                                                                 AStatsEventList* data,
490                                                                 void* cookie) {
491     ATRACE_CALL();
492 
493     GpuStats* pGpuStats = reinterpret_cast<GpuStats*>(cookie);
494     if (atomTag == android::util::GPU_STATS_GLOBAL_INFO) {
495         return pGpuStats->pullGlobalInfoAtom(data);
496     } else if (atomTag == android::util::GPU_STATS_APP_INFO) {
497         return pGpuStats->pullAppInfoAtom(data);
498     }
499 
500     return AStatsManager_PULL_SKIP;
501 }
502 
503 } // namespace android
504