Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit c3fcae21 authored by Yurii Zubrytskyi's avatar Yurii Zubrytskyi Committed by Android (Google) Code Review
Browse files

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

parents 53c55f82 3787c9f5
Loading
Loading
Loading
Loading
+129 −72
Original line number Original line Diff line number Diff line
@@ -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(
                                                       StorageId storage, std::string_view path) {
        IncFsMount::StorageMap::iterator storageIt, std::string_view path) {
    const auto storageInfo = ifs->storages.find(storage);
    if (storageInfo == ifs->storages.end()) {
        return {};
    }
    std::string normPath;
    std::string normPath;
    if (path::isAbsolute(path)) {
    if (path::isAbsolute(path)) {
        normPath = path::normalize(path);
        normPath = path::normalize(path);
        if (!path::startsWith(normPath, storageIt->second.name)) {
            return {};
        }
    } else {
    } else {
        normPath = path::normalize(path::join(storageInfo->second.name, path));
        normPath = path::normalize(path::join(storageIt->second.name, path));
    }
    return normPath;
}
}
    if (!path::startsWith(normPath, storageInfo->second.name)) {

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 {};
        return {};
    }
    }
    return normPath;
    return normalizePathToStorageLocked(storageInfo, path);
}
}


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) {
+5 −1
Original line number Original line Diff line number Diff line
@@ -157,6 +157,8 @@ public:
    };
    };


private:
private:
    static const bool sEnablePerfLogging;

    struct IncFsMount {
    struct IncFsMount {
        struct Bind {
        struct Bind {
            StorageId storage;
            StorageId storage;
@@ -227,7 +229,9 @@ 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 normalizePathToStorageLocked(IncFsMount::StorageMap::iterator storageIt,
                                             std::string_view path);
                                             std::string_view path);


    binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);
    binder::Status applyStorageParams(IncFsMount& ifs, bool enableReadLogs);