Merge "[incfs] Add time measurement for native libs extraction" into rvc-dev

This commit is contained in:
Yurii Zubrytskyi
2020-04-07 18:20:18 +00:00
committed by Android (Google) Code Review
2 changed files with 136 additions and 75 deletions

View File

@@ -73,7 +73,7 @@ struct Constants {
}; };
static const Constants& constants() { static const Constants& constants() {
static Constants c; static constexpr Constants c;
return c; return c;
} }
@@ -159,6 +159,9 @@ std::string makeBindMdName() {
} }
} // namespace } // namespace
const bool IncrementalService::sEnablePerfLogging =
android::base::GetBoolProperty("incremental.perflogging", false);
IncrementalService::IncFsMount::~IncFsMount() { IncrementalService::IncFsMount::~IncFsMount() {
incrementalService.mDataLoaderManager->destroyDataLoader(mountId); incrementalService.mDataLoaderManager->destroyDataLoader(mountId);
LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\''; LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\'';
@@ -719,7 +722,10 @@ int IncrementalService::bind(StorageId storage, std::string_view source, std::st
if (storageInfo == ifs->storages.end()) { if (storageInfo == ifs->storages.end()) {
return -EINVAL; return -EINVAL;
} }
std::string normSource = normalizePathToStorage(ifs, storage, source); std::string normSource = normalizePathToStorageLocked(storageInfo, source);
if (normSource.empty()) {
return -EINVAL;
}
l.unlock(); l.unlock();
std::unique_lock l2(mLock, std::defer_lock); std::unique_lock l2(mLock, std::defer_lock);
return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource), return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
@@ -768,22 +774,28 @@ int IncrementalService::unbind(StorageId storage, std::string_view target) {
return 0; return 0;
} }
std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr ifs, std::string IncrementalService::normalizePathToStorageLocked(
IncFsMount::StorageMap::iterator storageIt, std::string_view path) {
std::string normPath;
if (path::isAbsolute(path)) {
normPath = path::normalize(path);
if (!path::startsWith(normPath, storageIt->second.name)) {
return {};
}
} else {
normPath = path::normalize(path::join(storageIt->second.name, path));
}
return normPath;
}
std::string IncrementalService::normalizePathToStorage(const IncrementalService::IfsMountPtr& ifs,
StorageId storage, std::string_view path) { StorageId storage, std::string_view path) {
std::unique_lock l(ifs->lock);
const auto storageInfo = ifs->storages.find(storage); const auto storageInfo = ifs->storages.find(storage);
if (storageInfo == ifs->storages.end()) { if (storageInfo == ifs->storages.end()) {
return {}; return {};
} }
std::string normPath; return normalizePathToStorageLocked(storageInfo, path);
if (path::isAbsolute(path)) {
normPath = path::normalize(path);
} else {
normPath = path::normalize(path::join(storageInfo->second.name, path));
}
if (!path::startsWith(normPath, storageInfo->second.name)) {
return {};
}
return normPath;
} }
int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id, int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
@@ -791,7 +803,8 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m
if (auto ifs = getIfs(storage)) { if (auto ifs = getIfs(storage)) {
std::string normPath = normalizePathToStorage(ifs, storage, path); std::string normPath = normalizePathToStorage(ifs, storage, path);
if (normPath.empty()) { if (normPath.empty()) {
LOG(ERROR) << "Internal error: storageId " << storage << " failed to normalize: " << path; LOG(ERROR) << "Internal error: storageId " << storage
<< " failed to normalize: " << path;
return -EINVAL; return -EINVAL;
} }
auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params); auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
@@ -799,10 +812,6 @@ int IncrementalService::makeFile(StorageId storage, std::string_view path, int m
LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err; LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << err;
return err; return err;
} }
std::vector<uint8_t> metadataBytes;
if (params.metadata.data && params.metadata.size > 0) {
metadataBytes.assign(params.metadata.data, params.metadata.data + params.metadata.size);
}
return 0; return 0;
} }
return -EINVAL; return -EINVAL;
@@ -842,8 +851,9 @@ int IncrementalService::makeDirs(StorageId storageId, std::string_view path, int
int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath, int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
StorageId destStorageId, std::string_view newPath) { StorageId destStorageId, std::string_view newPath) {
if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId); auto ifsSrc = getIfs(sourceStorageId);
ifsSrc && ifsSrc == ifsDest) { auto ifsDest = sourceStorageId == destStorageId ? ifsSrc : getIfs(destStorageId);
if (ifsSrc && ifsSrc == ifsDest) {
std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath); std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath);
std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath); std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath);
if (normOldPath.empty() || normNewPath.empty()) { if (normOldPath.empty() || normNewPath.empty()) {
@@ -1156,11 +1166,25 @@ bool IncrementalService::prepareDataLoader(IncrementalService::IncFsMount& ifs,
return true; return true;
} }
// Extract lib filse from zip, create new files in incfs and write data to them template <class Duration>
static long elapsedMcs(Duration start, Duration end) {
return std::chrono::duration_cast<std::chrono::microseconds>(end - start).count();
}
// Extract lib files from zip, create new files in incfs and write data to them
bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath, bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_view apkFullPath,
std::string_view libDirRelativePath, std::string_view libDirRelativePath,
std::string_view abi) { std::string_view abi) {
namespace sc = std::chrono;
using Clock = sc::steady_clock;
auto start = Clock::now();
const auto ifs = getIfs(storage); const auto ifs = getIfs(storage);
if (!ifs) {
LOG(ERROR) << "Invalid storage " << storage;
return false;
}
// First prepare target directories if they don't exist yet // First prepare target directories if they don't exist yet
if (auto res = makeDirs(storage, libDirRelativePath, 0755)) { if (auto res = makeDirs(storage, libDirRelativePath, 0755)) {
LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath
@@ -1168,112 +1192,145 @@ bool IncrementalService::configureNativeBinaries(StorageId storage, std::string_
return false; return false;
} }
std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(apkFullPath.data())); auto mkDirsTs = Clock::now();
std::unique_ptr<ZipFileRO> zipFile(ZipFileRO::open(path::c_str(apkFullPath)));
if (!zipFile) { if (!zipFile) {
LOG(ERROR) << "Failed to open zip file at " << apkFullPath; LOG(ERROR) << "Failed to open zip file at " << apkFullPath;
return false; return false;
} }
void* cookie = nullptr; void* cookie = nullptr;
const auto libFilePrefix = path::join(constants().libDir, abi); const auto libFilePrefix = path::join(constants().libDir, abi);
if (!zipFile.get()->startIteration(&cookie, libFilePrefix.c_str() /* prefix */, if (!zipFile->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
constants().libSuffix.data() /* suffix */)) { constants().libSuffix.data() /* suffix */)) {
LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath; LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
return false; return false;
} }
auto endIteration = [&zipFile](void* cookie) { zipFile->endIteration(cookie); };
auto iterationCleaner = std::unique_ptr<void, decltype(endIteration)>(cookie, endIteration);
auto openZipTs = Clock::now();
std::vector<IncFsDataBlock> instructions;
ZipEntryRO entry = nullptr; ZipEntryRO entry = nullptr;
bool success = true; while ((entry = zipFile->nextEntry(cookie)) != nullptr) {
while ((entry = zipFile.get()->nextEntry(cookie)) != nullptr) { auto startFileTs = Clock::now();
char fileName[PATH_MAX]; char fileName[PATH_MAX];
if (zipFile.get()->getEntryFileName(entry, fileName, sizeof(fileName))) { if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
continue; continue;
} }
const auto libName = path::basename(fileName); const auto libName = path::basename(fileName);
const auto targetLibPath = path::join(libDirRelativePath, libName); const auto targetLibPath = path::join(libDirRelativePath, libName);
const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath); const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath);
// If the extract file already exists, skip // If the extract file already exists, skip
struct stat st; if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) {
if (stat(targetLibPathAbsolute.c_str(), &st) == 0) { if (sEnablePerfLogging) {
LOG(INFO) << "Native lib file already exists: " << targetLibPath LOG(INFO) << "incfs: Native lib file already exists: " << targetLibPath
<< "; skipping extraction"; << "; skipping extraction, spent "
<< elapsedMcs(startFileTs, Clock::now()) << "mcs";
}
continue; continue;
} }
uint32_t uncompressedLen; uint32_t uncompressedLen, compressedLen;
if (!zipFile.get()->getEntryInfo(entry, nullptr, &uncompressedLen, nullptr, nullptr, if (!zipFile->getEntryInfo(entry, nullptr, &uncompressedLen, &compressedLen, nullptr,
nullptr, nullptr)) { nullptr, nullptr)) {
LOG(ERROR) << "Failed to read native lib entry: " << fileName; LOG(ERROR) << "Failed to read native lib entry: " << fileName;
success = false; return false;
break;
} }
// Create new lib file without signature info // Create new lib file without signature info
incfs::NewFileParams libFileParams{}; incfs::NewFileParams libFileParams = {
libFileParams.size = uncompressedLen; .size = uncompressedLen,
libFileParams.signature = {}; .signature = {},
// Metadata of the new lib file is its relative path // Metadata of the new lib file is its relative path
IncFsSpan libFileMetadata; .metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()},
libFileMetadata.data = targetLibPath.c_str(); };
libFileMetadata.size = targetLibPath.size();
libFileParams.metadata = libFileMetadata;
incfs::FileId libFileId = idFromMetadata(targetLibPath); incfs::FileId libFileId = idFromMetadata(targetLibPath);
if (auto res = makeFile(storage, targetLibPath, 0777, libFileId, libFileParams)) { if (auto res = mIncFs->makeFile(ifs->control, targetLibPathAbsolute, 0777, libFileId,
libFileParams)) {
LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res; LOG(ERROR) << "Failed to make file for: " << targetLibPath << " errno: " << res;
success = false;
// If one lib file fails to be created, abort others as well // If one lib file fails to be created, abort others as well
break; return false;
} }
auto makeFileTs = Clock::now();
// If it is a zero-byte file, skip data writing // If it is a zero-byte file, skip data writing
if (uncompressedLen == 0) { if (uncompressedLen == 0) {
if (sEnablePerfLogging) {
LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> "
<< uncompressedLen << " bytes): " << elapsedMcs(startFileTs, makeFileTs)
<< "mcs, make: " << elapsedMcs(startFileTs, makeFileTs);
}
continue; continue;
} }
// Write extracted data to new file // Write extracted data to new file
std::vector<uint8_t> libData(uncompressedLen); // NOTE: don't zero-initialize memory, it may take a while
if (!zipFile.get()->uncompressEntry(entry, &libData[0], uncompressedLen)) { auto libData = std::unique_ptr<uint8_t[]>(new uint8_t[uncompressedLen]);
if (!zipFile->uncompressEntry(entry, libData.get(), uncompressedLen)) {
LOG(ERROR) << "Failed to extract native lib zip entry: " << fileName; LOG(ERROR) << "Failed to extract native lib zip entry: " << fileName;
success = false; return false;
break;
} }
auto extractFileTs = Clock::now();
const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId); const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId);
if (!writeFd.ok()) { if (!writeFd.ok()) {
LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd; LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd;
success = false; return false;
break;
} }
const int numBlocks = uncompressedLen / constants().blockSize + 1;
std::vector<IncFsDataBlock> instructions; auto openFileTs = Clock::now();
auto remainingData = std::span(libData);
for (int i = 0; i < numBlocks - 1; i++) { const int numBlocks = (uncompressedLen + constants().blockSize - 1) / constants().blockSize;
instructions.clear();
instructions.reserve(numBlocks);
auto remainingData = std::span(libData.get(), uncompressedLen);
for (int i = 0; i < numBlocks; i++) {
const auto blockSize = std::min<uint16_t>(constants().blockSize, remainingData.size());
auto inst = IncFsDataBlock{ auto inst = IncFsDataBlock{
.fileFd = writeFd, .fileFd = writeFd.get(),
.pageIndex = static_cast<IncFsBlockIndex>(i), .pageIndex = static_cast<IncFsBlockIndex>(i),
.compression = INCFS_COMPRESSION_KIND_NONE, .compression = INCFS_COMPRESSION_KIND_NONE,
.kind = INCFS_BLOCK_KIND_DATA, .kind = INCFS_BLOCK_KIND_DATA,
.dataSize = static_cast<uint16_t>(constants().blockSize), .dataSize = blockSize,
.data = reinterpret_cast<const char*>(remainingData.data()), .data = reinterpret_cast<const char*>(remainingData.data()),
}; };
instructions.push_back(inst); instructions.push_back(inst);
remainingData = remainingData.subspan(constants().blockSize); remainingData = remainingData.subspan(blockSize);
} }
// Last block auto prepareInstsTs = Clock::now();
auto inst = IncFsDataBlock{
.fileFd = writeFd,
.pageIndex = static_cast<IncFsBlockIndex>(numBlocks - 1),
.compression = INCFS_COMPRESSION_KIND_NONE,
.kind = INCFS_BLOCK_KIND_DATA,
.dataSize = static_cast<uint16_t>(remainingData.size()),
.data = reinterpret_cast<const char*>(remainingData.data()),
};
instructions.push_back(inst);
size_t res = mIncFs->writeBlocks(instructions); size_t res = mIncFs->writeBlocks(instructions);
if (res != instructions.size()) { if (res != instructions.size()) {
LOG(ERROR) << "Failed to write data into: " << targetLibPath; LOG(ERROR) << "Failed to write data into: " << targetLibPath;
success = false; return false;
}
if (sEnablePerfLogging) {
auto endFileTs = Clock::now();
LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> "
<< uncompressedLen << " bytes): " << elapsedMcs(startFileTs, endFileTs)
<< "mcs, make: " << elapsedMcs(startFileTs, makeFileTs)
<< " extract: " << elapsedMcs(makeFileTs, extractFileTs)
<< " open: " << elapsedMcs(extractFileTs, openFileTs)
<< " prepare: " << elapsedMcs(openFileTs, prepareInstsTs)
<< " write:" << elapsedMcs(prepareInstsTs, endFileTs);
} }
instructions.clear();
} }
zipFile.get()->endIteration(cookie);
return success; if (sEnablePerfLogging) {
auto end = Clock::now();
LOG(INFO) << "incfs: configureNativeBinaries complete in " << elapsedMcs(start, end)
<< "mcs, make dirs: " << elapsedMcs(start, mkDirsTs)
<< " open zip: " << elapsedMcs(mkDirsTs, openZipTs)
<< " extract all: " << elapsedMcs(openZipTs, end);
}
return true;
} }
void IncrementalService::registerAppOpsCallback(const std::string& packageName) { void IncrementalService::registerAppOpsCallback(const std::string& packageName) {

View File

@@ -157,6 +157,8 @@ public:
}; };
private: private:
static const bool sEnablePerfLogging;
struct IncFsMount { struct IncFsMount {
struct Bind { struct Bind {
StorageId storage; StorageId storage;
@@ -227,8 +229,10 @@ private:
void deleteStorage(IncFsMount& ifs); void deleteStorage(IncFsMount& ifs);
void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock); void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock);
MountMap::iterator getStorageSlotLocked(); MountMap::iterator getStorageSlotLocked();
std::string normalizePathToStorage(const IfsMountPtr incfs, StorageId storage, std::string normalizePathToStorage(const IfsMountPtr& incfs, StorageId storage,
std::string_view path); std::string_view path);
std::string normalizePathToStorageLocked(IncFsMount::StorageMap::iterator storageIt,
std::string_view path);
binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs); binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);