/* * Copyright (c) 2023 Huawei Device Co., Ltd. * 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. */ #include "profile.h" #include #include #include #include #include #include #include #include #include #include #include #include "namemangler.h" #include "file_layout.h" #include "types_def.h" namespace maple { constexpr uint8 Profile::stringEnd = 0x00; uint32 Profile::hotFuncCountThreshold = 0; bool Profile::debug = false; bool Profile::initialized = false; constexpr uint32 kPrecision = 1000000; Profile::Profile() {} bool Profile::CheckProfileHeader(const Header &header) const { return (memcmp(header.magic, kProfileMagic, sizeof(kProfileMagic)) == 0); } std::string Profile::GetProfileNameByType(uint8 type) const { switch (type) { case kFunction: return "FUNCTION"; case kClassMeta: return "CLASSMETA"; case kFieldMeta: return "FIELDMETA"; case kMethodMeta: return "METHODMETA"; case kReflectionStr: return "ReflectionStr"; case kLiteral: return "Literal"; case kBBInfo: return "IRProfDescption"; case kIRCounter: return "IRProfCounter"; case kFileDesc: return "FileDescription"; default: CHECK_FATAL(false, "type not found"); } } bool Profile::CheckDexValid(uint32 idx) const { if (isAppProfile) { return true; // for app dont't check dexName } return (dexName.empty() || dexName.find(strMap.at(idx)) != std::string::npos); } void Profile::ParseLiteral(const char *data, const char *end) { if (data > end) { LogInfo::MapleLogger() << "parse Literal error" << '\n'; } const std::string str(data, end - data); std::stringstream ss; ss.str(str); std::string item; while (std::getline(ss, item)) { literal.insert(item); } if (debug) { LogInfo::MapleLogger() << "parse Literal succ literal size " << literal.size() << "\n"; } } std::string Profile::GetFunctionName(uint32 classIdx, uint32 methodIdx, uint32 sigIdx) const { const std::string className = namemangler::EncodeName(strMap.at(classIdx)); const std::string methodName = namemangler::EncodeName(strMap.at(methodIdx)); const std::string sigName = namemangler::EncodeName(strMap.at(sigIdx)); const std::string funcName = className + "_7C" + methodName + "_7C" + sigName; return funcName; } std::string Profile::GetMethodSigStr(uint32 methodIdx, uint32 sigIdx) const { const std::string methodName = namemangler::EncodeName(strMap.at(methodIdx)); const std::string sigName = namemangler::EncodeName(strMap.at(sigIdx)); const std::string methodSigStr = methodName + "_7C" + sigName; return methodSigStr; } void Profile::ParseFunc(const char *data, int32 fileNum) { const MapleFileProf *funcProf = nullptr; const FunctionItem *funcItem = nullptr; size_t offset = 0; for (int32 mapleFileIdx = 0; mapleFileIdx < fileNum; ++mapleFileIdx) { funcProf = reinterpret_cast(data + offset); if (CheckDexValid(funcProf->idx)) { if (debug) { LogInfo::MapleLogger() << "FuncProfile" << ":" << strMap.at(funcProf->idx) << ":" << funcProf->num << "\n"; } funcItem = reinterpret_cast(data + offset + sizeof(MapleFileProf)); for (uint32 item = 0; item < funcProf->num; ++item, ++funcItem) { if (funcItem->type >= kLayoutTypeCount) { if (debug) { LogInfo::MapleLogger() << "ParseFunc Error usupport type " << funcItem->type << "\n"; } continue; } std::string funcName = GetFunctionName(funcItem->classIdx, funcItem->methodIdx, funcItem->sigIdx); funcProfData.insert( std::make_pair(funcName, (FuncItem) {.callTimes = funcItem->callTimes, .type = funcItem->type})); } } // new maple file's profile offset += sizeof(MapleFileProf) + funcProf->size; } } void Profile::ParseMethodSignature(const char *data, int fileNum, std::unordered_set &metaData) const { const MapleFileProf *methodSigProf = nullptr; const MethodSignatureItem *methodSigItem = nullptr; uint32_t offset = 0; for (int32 mapleFileIdx = 0; mapleFileIdx < fileNum; ++mapleFileIdx) { methodSigProf = reinterpret_cast(data + offset); if (CheckDexValid(methodSigProf->idx)) { if (debug) { LogInfo::MapleLogger() << "MethodSignatureProfile" << ":" << strMap.at(methodSigProf->idx) << ":" << methodSigProf->num << "\n"; } methodSigItem = reinterpret_cast(data + offset + sizeof(MapleFileProf)); for (uint32 item = 0; item < methodSigProf->num; ++item, ++methodSigItem) { std::string methodSigStr = GetMethodSigStr(methodSigItem->methodIdx, methodSigItem->sigIdx); metaData.insert(methodSigStr); } // new maple file's profile } offset += sizeof(MapleFileProf) + methodSigProf->size; } } void Profile::ParseIRFuncDesc(const char *data, int32 fileNum) { const MapleFileProf *funcProf = nullptr; const FunctionIRProfItem *funcItem = nullptr; size_t offset = 0; for (int32 mapleFileIdx = 0; mapleFileIdx < fileNum; ++mapleFileIdx) { funcProf = reinterpret_cast(data + offset); if (CheckDexValid(funcProf->idx)) { if (debug) { LogInfo::MapleLogger() << "IRFuncProfile" << ":" << strMap.at(funcProf->idx) << ":" << funcProf->num << "\n"; } funcItem = reinterpret_cast(data + offset + sizeof(MapleFileProf)); for (uint32 item = 0; item < funcProf->num; ++item, ++funcItem) { if ((funcItem->counterStart <= counterTab.size()) && (funcItem->counterEnd <= counterTab.size())) { auto begin = counterTab.begin() + funcItem->counterStart; auto end = counterTab.begin() + funcItem->counterEnd + 1; std::vector tempCounter(begin, end); BBInfo bbInfo(funcItem->hash, tempCounter.size(), std::move(tempCounter)); std::string funcName = GetFunctionName(funcItem->classIdx, funcItem->methodIdx, funcItem->sigIdx); funcBBProfData.emplace(std::make_pair(funcName, bbInfo)); } } } // new maple file's profile offset += sizeof(MapleFileProf) + funcProf->size; } } void Profile::ParseCounterTab(const char *data, int32 fileNum) { const MapleFileProf *counterProf = nullptr; size_t offset = 0; for (int32 mapleFileIdx = 0; mapleFileIdx < fileNum; ++mapleFileIdx) { counterProf = reinterpret_cast(data + offset); if (CheckDexValid(counterProf->idx)) { if (debug) { LogInfo::MapleLogger() << "module name " << strMap.at(counterProf->idx) << std::endl; } const FuncCounterItem *item = reinterpret_cast(data + offset + sizeof(MapleFileProf)); for (uint32 i = 0; i < counterProf->num; ++i, ++item) { counterTab.emplace_back(item->callTimes); } } offset += sizeof(MapleFileProf) + counterProf->size; } } void Profile::ParseMeta(const char *data, int32 fileNum, std::unordered_set &metaData) const { const MapleFileProf *metaProf = nullptr; size_t offset = 0; for (int32 mapleFileIdx = 0; mapleFileIdx < fileNum; ++mapleFileIdx) { metaProf = reinterpret_cast(data + offset); if (CheckDexValid(metaProf->idx)) { if (debug) { LogInfo::MapleLogger() << "module name " << strMap.at(metaProf->idx) << '\n'; } const MetaItem *metaItem = reinterpret_cast(data + offset + sizeof(MapleFileProf)); for (uint32 item = 0; item < metaProf->num; ++item, ++metaItem) { metaData.insert(strMap.at(metaItem->idx)); } } offset += sizeof(MapleFileProf) + metaProf->size; } } void Profile::ParseReflectionStr(const char *data, int32 fileNum) { const MapleFileProf *metaProf = nullptr; size_t offset = 0; for (int32 mapleFileIdx = 0; mapleFileIdx < fileNum; ++mapleFileIdx) { metaProf = reinterpret_cast(data + offset); if (CheckDexValid(metaProf->idx)) { if (debug) { LogInfo::MapleLogger() << "module name " << strMap.at(metaProf->idx) << '\n'; } const ReflectionStrItem *strItem = reinterpret_cast(data + offset + sizeof(MapleFileProf)); for (uint32 item = 0; item < metaProf->num; ++item, ++strItem) { reflectionStrData.insert(std::make_pair(strMap.at(strItem->idx), strItem->type)); } } offset += sizeof(MapleFileProf) + metaProf->size; } } bool Profile::DeCompress(const std::string &path, const std::string &dexNameInner, ProfileType type) { if (initialized) { LogInfo::MapleLogger() << "System server Profile has been decompressed before" << '\n'; return valid; } initialized = true; this->dexName = dexNameInner; bool res = true; std::ifstream in(path, std::ios::binary); if (!in.is_open()) { if (errno != ENOENT && errno != EACCES) { LogInfo::MapleLogger() << "WARN: DeCompress(" << "), failed to open " << path << '\n'; ; } res = false; return res; } in.seekg(0, std::ios::end); size_t byteCount = static_cast(in.tellg()); in.seekg(0, std::ios::beg); std::vector bufVector; bufVector.resize(byteCount); char *buf = reinterpret_cast(bufVector.data()); if (!in.read(buf, byteCount)) { LogInfo::MapleLogger() << "WARN: DeCompress(" << "), failed to read all data for " << path << '\n'; res = false; in.close(); return res; } if (byteCount < sizeof(Header)) { LogInfo::MapleLogger() << "WARN: DeCompress(" << "), failed, read no data for " << path << '\n'; res = false; in.close(); return res; } Header *header = reinterpret_cast
(buf); if (!CheckProfileHeader(*header)) { if (debug) { LogInfo::MapleLogger() << "invalid maigc number " << header->magic << '\n'; ; } res = false; in.close(); return res; } this->isAppProfile = (header->profileFileType == kApp) ? true : false; size_t stringTabSize = byteCount - header->stringTabOff; if (debug) { LogInfo::MapleLogger() << "Header summary " << "profile num " << static_cast(header->profileNum) << "string table size" << stringTabSize << '\n'; } const char *strBuf = buf + header->stringTabOff; const char *cursor = strBuf; const char *sentinel = strBuf + stringTabSize; while (cursor < sentinel) { size_t len = std::strlen(cursor); const char *next = cursor + len + 1; strMap.emplace_back(cursor); cursor = next; } DEBUG_ASSERT(strMap.size() == header->stringCount, "string count doesn't match"); if (debug) { LogInfo::MapleLogger() << "str size " << strMap.size() << '\n'; for (const auto &item : strMap) { LogInfo::MapleLogger() << item << '\n'; } LogInfo::MapleLogger() << "str size print end " << '\n'; } uint8_t idx = 0; for (idx = 0; idx < header->profileNum; ++idx) { ProfileDataInfo *profileDataInfo = &(header->data[idx]); if (debug) { LogInfo::MapleLogger() << "profile file num for type " << GetProfileNameByType(profileDataInfo->profileType) << " " << static_cast(profileDataInfo->mapleFileNum) << '\n'; } if (debug) { LogInfo::MapleLogger() << GetProfileNameByType(profileDataInfo->profileType) << " Start" << '\n'; } char *proFileData = buf + profileDataInfo->profileDataOff; if (type != kAll && type != profileDataInfo->profileType) { continue; // only parse the indicated type } switch (profileDataInfo->profileType) { case kFunction: ParseFunc(proFileData, profileDataInfo->mapleFileNum); break; case kClassMeta: ParseMeta(proFileData, profileDataInfo->mapleFileNum, classMeta); break; case kFieldMeta: ParseMeta(proFileData, profileDataInfo->mapleFileNum, fieldMeta); break; case kMethodMeta: ParseMeta(proFileData, profileDataInfo->mapleFileNum, methodMeta); break; case kReflectionStr: ParseReflectionStr(proFileData, profileDataInfo->mapleFileNum); break; case kLiteral: ParseLiteral(proFileData, strBuf); break; case kBBInfo: ParseIRFuncDesc(proFileData, profileDataInfo->mapleFileNum); break; case kIRCounter: ParseCounterTab(proFileData, profileDataInfo->mapleFileNum); break; case kMethodSig: ParseMethodSignature(proFileData, profileDataInfo->mapleFileNum, methodSigMeta); break; case kFileDesc: { uint32_t appPackageNameIdx = *reinterpret_cast(proFileData); this->appPackageName = strMap.at(appPackageNameIdx); if (!appPackageName.empty() && this->appPackageName != appPackageName) { LogInfo::MapleLogger() << "app profile doesnt match expect " << this->appPackageName << " but got " << appPackageName << '\n'; return false; } break; } default: LogInfo::MapleLogger() << "unsupported tag " << profileDataInfo->profileType << '\n'; break; } } LogInfo::MapleLogger() << "SUCC parse " << path << '\n'; valid = true; in.close(); return res; } void Profile::SetProfileMode() { profileMode = true; } bool Profile::CheckFuncHot(const std::string &funcName) const { if (funcProfData.empty()) { return false; } if (valid) { auto iter = funcProfData.find(funcName); if (iter == funcProfData.end()) { return false; } if (hotFuncCountThreshold == 0) { if (Options::profileHotCountSeted) { hotFuncCountThreshold = Options::profileHotCount; } else { std::vector times; for (auto &item : funcProfData) { times.push_back((item.second).callTimes); } std::sort(times.begin(), times.end(), std::greater()); size_t index = static_cast(static_cast(times.size()) / kPrecision * (Options::profileHotRate)); hotFuncCountThreshold = times.at(index); } } return (iter->second).callTimes >= hotFuncCountThreshold; } return false; } bool Profile::CheckMethodHot(const std::string &className) const { if (methodMeta.empty()) { return true; } if (valid) { if (methodMeta.find(className) == methodMeta.end()) { return false; } return true; } return false; } bool Profile::CheckMethodSigHot(const std::string &methodSigStr) const { if (methodSigMeta.empty()) { return false; } if (valid) { if (methodSigMeta.find(methodSigStr) == methodSigMeta.end()) { return false; } return true; } return false; } bool Profile::CheckFieldHot(const std::string &className) const { if (fieldMeta.empty()) { return true; } if (valid) { if (fieldMeta.find(className) == fieldMeta.end()) { return false; } return true; } return false; } bool Profile::CheckClassHot(const std::string &className) const { // If in mode sample all class set to cold, except for core-all so,core-all have // some hot class pre defined if (profileMode && !isCoreSo) { return false; } if (classMeta.empty()) { return true; } if (valid || isCoreSo) { return classMeta.find(className) != classMeta.end(); } return false; } bool Profile::CheckLiteralHot(const std::string &literalInner) const { if (valid) { if ((this->literal).find(literalInner) == (this->literal).end()) { return false; } return true; } return false; } size_t Profile::GetLiteralProfileSize() const { if (valid) { return literal.size(); } return 0; } bool Profile::CheckReflectionStrHot(const std::string &str, uint8 &layoutType) const { if (valid) { auto item = reflectionStrData.find(str); if (item == reflectionStrData.end()) { return false; } layoutType = item->second; return true; } return false; } const std::unordered_map &Profile::GetFunctionProf() const { return funcProfData; } bool Profile::GetFunctionBBProf(const std::string &funcName, Profile::BBInfo &result) { auto item = funcBBProfData.find(funcName); if (item == funcBBProfData.end()) { return false; } result = item->second; return true; } std::unordered_set &Profile::GetMeta(uint8 type) { switch (type) { case kClassMeta: return classMeta; case kFieldMeta: return fieldMeta; case kMethodMeta: return methodMeta; default: CHECK_FATAL(false, "type not found"); } } void Profile::SetFuncStatus(const std::string &funcName, bool succ) { if (succ) { funcBBProfUseInfo[funcName] = true; } } void Profile::DumpFuncIRProfUseInfo() const { if (funcBBProfData.empty()) { return; } LogInfo::MapleLogger() << "ir profile succ " << funcBBProfUseInfo.size() << " total func " << funcBBProfData.size() << '\n'; } void Profile::Dump() const { std::ofstream outFile; outFile.open("prof.dump"); CHECK_FATAL(!outFile.is_open(), "open file failed"); outFile << "classMeta profile start " << '\n'; for (const auto &item : classMeta) { outFile << item << '\n'; } outFile << "fieldMeta profile start " << '\n'; for (const auto &item : fieldMeta) { outFile << item << '\n'; } outFile << "methodMeta profile start " << '\n'; for (const auto &item : methodMeta) { outFile << item << '\n'; } outFile << "literal profile start " << '\n'; for (const auto &item : literal) { outFile << item << '\n'; } outFile << "func profile start " << '\n'; for (const auto &item : funcProfData) { outFile << item.first << " " << static_cast((item.second).type) << " " << (item.second).callTimes << '\n'; } outFile << "reflectStr profile start " << '\n'; for (const auto &item : reflectionStrData) { outFile << item.first << " " << static_cast(item.second) << '\n'; } outFile.close(); } } // namespace maple