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