[incfs] Add time measurement for native libs extraction
Use the "incremental.perflogging" boolean system property
to enable it
+ a bunch of code cleanups
Bug: 152913040
Test: manual
Change-Id: I1cd259ff5821a47ce055003049f77cbf43eba24a
diff --git a/services/incremental/IncrementalService.cpp b/services/incremental/IncrementalService.cpp
index 0da1673..5a88fc8 100644
--- a/services/incremental/IncrementalService.cpp
+++ b/services/incremental/IncrementalService.cpp
@@ -74,7 +74,7 @@
};
static const Constants& constants() {
- static Constants c;
+ static constexpr Constants c;
return c;
}
@@ -160,6 +160,9 @@
}
} // namespace
+const bool IncrementalService::sEnablePerfLogging =
+ android::base::GetBoolProperty("incremental.perflogging", false);
+
IncrementalService::IncFsMount::~IncFsMount() {
incrementalService.mDataLoaderManager->destroyDataLoader(mountId);
LOG(INFO) << "Unmounting and cleaning up mount " << mountId << " with root '" << root << '\'';
@@ -722,7 +725,10 @@
if (storageInfo == ifs->storages.end()) {
return -EINVAL;
}
- std::string normSource = normalizePathToStorage(ifs, storage, source);
+ std::string normSource = normalizePathToStorageLocked(storageInfo, source);
+ if (normSource.empty()) {
+ return -EINVAL;
+ }
l.unlock();
std::unique_lock l2(mLock, std::defer_lock);
return addBindMount(*ifs, storage, storageInfo->second.name, std::move(normSource),
@@ -771,22 +777,28 @@
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) {
+ std::unique_lock l(ifs->lock);
const auto storageInfo = ifs->storages.find(storage);
if (storageInfo == ifs->storages.end()) {
return {};
}
- std::string normPath;
- 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;
+ return normalizePathToStorageLocked(storageInfo, path);
}
int IncrementalService::makeFile(StorageId storage, std::string_view path, int mode, FileId id,
@@ -794,7 +806,8 @@
if (auto ifs = getIfs(storage)) {
std::string normPath = normalizePathToStorage(ifs, storage, path);
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;
}
auto err = mIncFs->makeFile(ifs->control, normPath, mode, id, params);
@@ -802,10 +815,6 @@
LOG(ERROR) << "Internal error: storageId " << storage << " failed to makeFile: " << 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 -EINVAL;
@@ -845,8 +854,9 @@
int IncrementalService::link(StorageId sourceStorageId, std::string_view oldPath,
StorageId destStorageId, std::string_view newPath) {
- if (auto ifsSrc = getIfs(sourceStorageId), ifsDest = getIfs(destStorageId);
- ifsSrc && ifsSrc == ifsDest) {
+ auto ifsSrc = getIfs(sourceStorageId);
+ auto ifsDest = sourceStorageId == destStorageId ? ifsSrc : getIfs(destStorageId);
+ if (ifsSrc && ifsSrc == ifsDest) {
std::string normOldPath = normalizePathToStorage(ifsSrc, sourceStorageId, oldPath);
std::string normNewPath = normalizePathToStorage(ifsDest, destStorageId, newPath);
if (normOldPath.empty() || normNewPath.empty()) {
@@ -1159,11 +1169,25 @@
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,
std::string_view libDirRelativePath,
std::string_view abi) {
+ namespace sc = std::chrono;
+ using Clock = sc::steady_clock;
+ auto start = Clock::now();
+
const auto ifs = getIfs(storage);
+ if (!ifs) {
+ LOG(ERROR) << "Invalid storage " << storage;
+ return false;
+ }
+
// First prepare target directories if they don't exist yet
if (auto res = makeDirs(storage, libDirRelativePath, 0755)) {
LOG(ERROR) << "Failed to prepare target lib directory " << libDirRelativePath
@@ -1171,112 +1195,145 @@
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) {
LOG(ERROR) << "Failed to open zip file at " << apkFullPath;
return false;
}
void* cookie = nullptr;
const auto libFilePrefix = path::join(constants().libDir, abi);
- if (!zipFile.get()->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
- constants().libSuffix.data() /* suffix */)) {
+ if (!zipFile->startIteration(&cookie, libFilePrefix.c_str() /* prefix */,
+ constants().libSuffix.data() /* suffix */)) {
LOG(ERROR) << "Failed to start zip iteration for " << apkFullPath;
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;
- bool success = true;
- while ((entry = zipFile.get()->nextEntry(cookie)) != nullptr) {
+ while ((entry = zipFile->nextEntry(cookie)) != nullptr) {
+ auto startFileTs = Clock::now();
+
char fileName[PATH_MAX];
- if (zipFile.get()->getEntryFileName(entry, fileName, sizeof(fileName))) {
+ if (zipFile->getEntryFileName(entry, fileName, sizeof(fileName))) {
continue;
}
const auto libName = path::basename(fileName);
const auto targetLibPath = path::join(libDirRelativePath, libName);
const auto targetLibPathAbsolute = normalizePathToStorage(ifs, storage, targetLibPath);
// If the extract file already exists, skip
- struct stat st;
- if (stat(targetLibPathAbsolute.c_str(), &st) == 0) {
- LOG(INFO) << "Native lib file already exists: " << targetLibPath
- << "; skipping extraction";
+ if (access(targetLibPathAbsolute.c_str(), F_OK) == 0) {
+ if (sEnablePerfLogging) {
+ LOG(INFO) << "incfs: Native lib file already exists: " << targetLibPath
+ << "; skipping extraction, spent "
+ << elapsedMcs(startFileTs, Clock::now()) << "mcs";
+ }
continue;
}
- uint32_t uncompressedLen;
- if (!zipFile.get()->getEntryInfo(entry, nullptr, &uncompressedLen, nullptr, nullptr,
- nullptr, nullptr)) {
+ uint32_t uncompressedLen, compressedLen;
+ if (!zipFile->getEntryInfo(entry, nullptr, &uncompressedLen, &compressedLen, nullptr,
+ nullptr, nullptr)) {
LOG(ERROR) << "Failed to read native lib entry: " << fileName;
- success = false;
- break;
+ return false;
}
// Create new lib file without signature info
- incfs::NewFileParams libFileParams{};
- libFileParams.size = uncompressedLen;
- libFileParams.signature = {};
- // Metadata of the new lib file is its relative path
- IncFsSpan libFileMetadata;
- libFileMetadata.data = targetLibPath.c_str();
- libFileMetadata.size = targetLibPath.size();
- libFileParams.metadata = libFileMetadata;
+ incfs::NewFileParams libFileParams = {
+ .size = uncompressedLen,
+ .signature = {},
+ // Metadata of the new lib file is its relative path
+ .metadata = {targetLibPath.c_str(), (IncFsSize)targetLibPath.size()},
+ };
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;
- success = false;
// 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 (uncompressedLen == 0) {
+ if (sEnablePerfLogging) {
+ LOG(INFO) << "incfs: Extracted " << libName << "(" << compressedLen << " -> "
+ << uncompressedLen << " bytes): " << elapsedMcs(startFileTs, makeFileTs)
+ << "mcs, make: " << elapsedMcs(startFileTs, makeFileTs);
+ }
continue;
}
// Write extracted data to new file
- std::vector<uint8_t> libData(uncompressedLen);
- if (!zipFile.get()->uncompressEntry(entry, &libData[0], uncompressedLen)) {
+ // NOTE: don't zero-initialize memory, it may take a while
+ 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;
- success = false;
- break;
+ return false;
}
+
+ auto extractFileTs = Clock::now();
+
const auto writeFd = mIncFs->openForSpecialOps(ifs->control, libFileId);
if (!writeFd.ok()) {
LOG(ERROR) << "Failed to open write fd for: " << targetLibPath << " errno: " << writeFd;
- success = false;
- break;
+ return false;
}
- const int numBlocks = uncompressedLen / constants().blockSize + 1;
- std::vector<IncFsDataBlock> instructions;
- auto remainingData = std::span(libData);
- for (int i = 0; i < numBlocks - 1; i++) {
+
+ auto openFileTs = Clock::now();
+
+ 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{
- .fileFd = writeFd,
+ .fileFd = writeFd.get(),
.pageIndex = static_cast<IncFsBlockIndex>(i),
.compression = INCFS_COMPRESSION_KIND_NONE,
.kind = INCFS_BLOCK_KIND_DATA,
- .dataSize = static_cast<uint16_t>(constants().blockSize),
+ .dataSize = blockSize,
.data = reinterpret_cast<const char*>(remainingData.data()),
};
instructions.push_back(inst);
- remainingData = remainingData.subspan(constants().blockSize);
+ remainingData = remainingData.subspan(blockSize);
}
- // Last block
- 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);
+ auto prepareInstsTs = Clock::now();
+
size_t res = mIncFs->writeBlocks(instructions);
if (res != instructions.size()) {
LOG(ERROR) << "Failed to write data into: " << targetLibPath;
- success = false;
+ return false;
}
- instructions.clear();
+
+ 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);
+ }
}
- 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) {
diff --git a/services/incremental/IncrementalService.h b/services/incremental/IncrementalService.h
index ff69633..7ebc41df 100644
--- a/services/incremental/IncrementalService.h
+++ b/services/incremental/IncrementalService.h
@@ -158,6 +158,8 @@
};
private:
+ static const bool sEnablePerfLogging;
+
struct IncFsMount {
struct Bind {
StorageId storage;
@@ -231,8 +233,10 @@
void deleteStorage(IncFsMount& ifs);
void deleteStorageLocked(IncFsMount& ifs, std::unique_lock<std::mutex>&& ifsLock);
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 normalizePathToStorageLocked(IncFsMount::StorageMap::iterator storageIt,
+ std::string_view path);
int applyStorageParams(IncFsMount& ifs);