/* * Copyright 2016 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #define ATRACE_TAG ATRACE_TAG_GRAPHICS #include "GpuService.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "gpustats/GpuStats.h" namespace android { using base::StringAppendF; namespace { status_t cmdHelp(int out); status_t cmdVkjson(int out, int err); void dumpGameDriverInfo(std::string* result); void dumpMemoryInfo(std::string* result, const GpuMemoryMap& memories, uint32_t pid); } // namespace const String16 sDump("android.permission.DUMP"); const char* const GpuService::SERVICE_NAME = "gpu"; GpuService::GpuService() : mGpuStats(std::make_unique()){}; void GpuService::setGpuStats(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) { mGpuStats->insert(driverPackageName, driverVersionName, driverVersionCode, driverBuildTime, appPackageName, vulkanVersion, driver, isDriverLoaded, driverLoadingTime); } status_t GpuService::getGpuStatsGlobalInfo(std::vector* outStats) const { mGpuStats->pullGlobalStats(outStats); return OK; } status_t GpuService::getGpuStatsAppInfo(std::vector* outStats) const { mGpuStats->pullAppStats(outStats); return OK; } void GpuService::setTargetStats(const std::string& appPackageName, const uint64_t driverVersionCode, const GpuStatsInfo::Stats stats, const uint64_t value) { mGpuStats->insertTargetStats(appPackageName, driverVersionCode, stats, value); } bool isExpectedFormat(const char* str) { // Should match in order: // gpuaddr useraddr size id flags type usage sglen mapsize eglsrf eglimg std::istringstream iss; iss.str(str); std::string word; iss >> word; if (word != "gpuaddr") { return false; } iss >> word; if (word != "useraddr") { return false; } iss >> word; if (word != "size") { return false; } iss >> word; if (word != "id") { return false; } iss >> word; if (word != "flags") { return false; } iss >> word; if (word != "type") { return false; } iss >> word; if (word != "usage") { return false; } iss >> word; if (word != "sglen") { return false; } iss >> word; if (word != "mapsize") { return false; } iss >> word; if (word != "eglsrf") { return false; } iss >> word; if (word != "eglimg") { return false; } return true; } // Queries gpu memory via Qualcomm's /d/kgsl/proc/*/mem interface. status_t GpuService::getQCommGpuMemoryInfo(GpuMemoryMap* memories, std::string* result, int32_t dumpPid) const { const std::string kDirectoryPath = "/d/kgsl/proc"; DIR* directory = opendir(kDirectoryPath.c_str()); if (!directory) { return PERMISSION_DENIED; } // File Format: // gpuaddr useraddr size id flags type usage sglen mapsize eglsrf eglimg // 0000000000000000 0000000000000000 8359936 23 --w--pY-- gpumem VK/others( 38) 0 0 0 0 // 0000000000000000 0000000000000000 16293888 24 --wL--N-- ion surface 41 0 0 1 const bool dumpAll = dumpPid == 0; static constexpr size_t kMaxLineLength = 1024; static char line[kMaxLineLength]; while(dirent* subdir = readdir(directory)) { // Skip "." and ".." in directory. if (strcmp(subdir->d_name, ".") == 0 || strcmp(subdir->d_name, "..") == 0 ) { continue; } std::string pid_str(subdir->d_name); const uint32_t pid(stoi(pid_str)); if (!dumpAll && dumpPid != pid) { continue; } std::string filepath(kDirectoryPath + "/" + pid_str + "/mem"); std::ifstream file(filepath); // Check first line file.getline(line, kMaxLineLength); if (!isExpectedFormat(line)) { continue; } if (result) { StringAppendF(result, "%d:\n%s\n", pid, line); } while( file.getline(line, kMaxLineLength) ) { if (result) { StringAppendF(result, "%s\n", line); } std::istringstream iss; iss.str(line); // Skip gpuaddr, useraddr. const char delimiter = ' '; iss >> std::ws; iss.ignore(kMaxLineLength, delimiter); iss >> std::ws; iss.ignore(kMaxLineLength, delimiter); // Get size. int64_t memsize; iss >> memsize; // Skip id, flags. iss >> std::ws; iss.ignore(kMaxLineLength, delimiter); iss >> std::ws; iss.ignore(kMaxLineLength, delimiter); // Get type, usage. std::string memtype; std::string usage; iss >> memtype >> usage; // Adjust for the space in VK/others( #) if (usage == "VK/others(") { std::string vkTypeEnd; iss >> vkTypeEnd; usage.append(vkTypeEnd); } // Skip sglen. iss >> std::ws; iss.ignore(kMaxLineLength, delimiter); // Get mapsize. int64_t mapsize; iss >> mapsize; if (memsize == 0 && mapsize == 0) { continue; } if (memtype == "gpumem") { (*memories)[pid][usage].gpuMemory += memsize; } else { (*memories)[pid][usage].ionMemory += memsize; } if (mapsize > 0) { (*memories)[pid][usage].mappedMemory += mapsize; } } if (result) { StringAppendF(result, "\n"); } } closedir(directory); return OK; } status_t GpuService::shellCommand(int /*in*/, int out, int err, std::vector& args) { ATRACE_CALL(); ALOGV("shellCommand"); for (size_t i = 0, n = args.size(); i < n; i++) ALOGV(" arg[%zu]: '%s'", i, String8(args[i]).string()); if (args.size() >= 1) { if (args[0] == String16("vkjson")) return cmdVkjson(out, err); if (args[0] == String16("help")) return cmdHelp(out); } // no command, or unrecognized command cmdHelp(err); return BAD_VALUE; } status_t GpuService::doDump(int fd, const Vector& args, bool /*asProto*/) { std::string result; IPCThreadState* ipc = IPCThreadState::self(); const int pid = ipc->getCallingPid(); const int uid = ipc->getCallingUid(); if ((uid != AID_SHELL) && !PermissionCache::checkPermission(sDump, pid, uid)) { StringAppendF(&result, "Permission Denial: can't dump gpu from pid=%d, uid=%d\n", pid, uid); } else { bool dumpAll = true; bool dumpDriverInfo = false; bool dumpStats = false; bool dumpMemory = false; size_t numArgs = args.size(); int32_t pid = 0; if (numArgs) { dumpAll = false; for (size_t index = 0; index < numArgs; ++index) { if (args[index] == String16("--gpustats")) { dumpStats = true; } else if (args[index] == String16("--gpudriverinfo")) { dumpDriverInfo = true; } else if (args[index] == String16("--gpumem")) { dumpMemory = true; } else if (args[index].startsWith(String16("--gpumem="))) { dumpMemory = true; pid = atoi(String8(&args[index][9])); } } } if (dumpAll || dumpDriverInfo) { dumpGameDriverInfo(&result); result.append("\n"); } if (dumpAll || dumpStats) { mGpuStats->dump(Vector(), &result); result.append("\n"); } if (dumpAll || dumpMemory) { GpuMemoryMap memories; // Currently only queries Qualcomm gpu memory. More will be added later. if (getQCommGpuMemoryInfo(&memories, &result, pid) == OK) { dumpMemoryInfo(&result, memories, pid); result.append("\n"); } } } write(fd, result.c_str(), result.size()); return NO_ERROR; } namespace { status_t cmdHelp(int out) { FILE* outs = fdopen(out, "w"); if (!outs) { ALOGE("vkjson: failed to create out stream: %s (%d)", strerror(errno), errno); return BAD_VALUE; } fprintf(outs, "GPU Service commands:\n" " vkjson dump Vulkan properties as JSON\n"); fclose(outs); return NO_ERROR; } void vkjsonPrint(FILE* out) { std::string json = VkJsonInstanceToJson(VkJsonGetInstance()); fwrite(json.data(), 1, json.size(), out); fputc('\n', out); } status_t cmdVkjson(int out, int /*err*/) { FILE* outs = fdopen(out, "w"); if (!outs) { int errnum = errno; ALOGE("vkjson: failed to create output stream: %s", strerror(errnum)); return -errnum; } vkjsonPrint(outs); fclose(outs); return NO_ERROR; } void dumpGameDriverInfo(std::string* result) { if (!result) return; char stableGameDriver[PROPERTY_VALUE_MAX] = {}; property_get("ro.gfx.driver.0", stableGameDriver, "unsupported"); StringAppendF(result, "Stable Game Driver: %s\n", stableGameDriver); char preReleaseGameDriver[PROPERTY_VALUE_MAX] = {}; property_get("ro.gfx.driver.1", preReleaseGameDriver, "unsupported"); StringAppendF(result, "Pre-release Game Driver: %s\n", preReleaseGameDriver); } // Read and print all memory info for each process from /d/kgsl/proc//mem. void dumpMemoryInfo(std::string* result, const GpuMemoryMap& memories, uint32_t pid) { if (!result) return; // Write results. StringAppendF(result, "GPU Memory Summary:\n"); for(auto& mem : memories) { uint32_t process = mem.first; if (pid != 0 && pid != process) { continue; } StringAppendF(result, "%d:\n", process); for(auto& memStruct : mem.second) { StringAppendF(result, " %s", memStruct.first.c_str()); if(memStruct.second.gpuMemory > 0) StringAppendF(result, ", GPU memory = %" PRId64, memStruct.second.gpuMemory); if(memStruct.second.mappedMemory > 0) StringAppendF(result, ", Mapped memory = %" PRId64, memStruct.second.mappedMemory); if(memStruct.second.ionMemory > 0) StringAppendF(result, ", Ion memory = %" PRId64, memStruct.second.ionMemory); StringAppendF(result, "\n"); } } } } // anonymous namespace } // namespace android